team-2/CHALLENGE_2/sleepysound/lib/services/music_queue_service.dart

200 lines
5.5 KiB
Dart

import 'package:flutter/foundation.dart';
import '../models/spotify_track.dart';
import '../services/spotify_service.dart';
import '../services/audio_service.dart';
class QueueItem {
final SpotifyTrack track;
int votes;
bool userVoted;
final DateTime addedAt;
QueueItem({
required this.track,
this.votes = 1,
this.userVoted = true,
DateTime? addedAt,
}) : addedAt = addedAt ?? DateTime.now();
Map<String, dynamic> toJson() => {
'id': track.id,
'title': track.name,
'artist': track.artistNames,
'votes': votes,
'userVoted': userVoted,
'duration': track.duration,
'imageUrl': track.imageUrl,
'addedAt': addedAt.toIso8601String(),
};
}
class MusicQueueService extends ChangeNotifier {
static final MusicQueueService _instance = MusicQueueService._internal();
factory MusicQueueService() => _instance;
MusicQueueService._internal();
final SpotifyService _spotifyService = SpotifyService();
final AudioService _audioService = AudioService();
// Current playing track
SpotifyTrack? _currentTrack;
// Queue management
final List<QueueItem> _queue = [];
// Recently played
final List<SpotifyTrack> _recentlyPlayed = [];
// Getters
SpotifyTrack? get currentTrack => _audioService.currentTrack ?? _currentTrack;
bool get isPlaying => _audioService.isPlaying;
double get progress => _audioService.progress;
List<QueueItem> get queue => List.unmodifiable(_queue);
List<SpotifyTrack> get recentlyPlayed => List.unmodifiable(_recentlyPlayed);
// Queue operations
void addToQueue(SpotifyTrack track) {
// Check if track is already in queue
final existingIndex = _queue.indexWhere((item) => item.track.id == track.id);
if (existingIndex != -1) {
// If track exists, upvote it
upvote(existingIndex);
} else {
// Add new track to queue
final queueItem = QueueItem(track: track);
_queue.add(queueItem);
_sortQueue();
notifyListeners();
print('Added "${track.name}" by ${track.artistNames} to queue');
}
}
void upvote(int index) {
if (index >= 0 && index < _queue.length) {
_queue[index].votes++;
if (!_queue[index].userVoted) {
_queue[index].userVoted = true;
}
_sortQueue();
notifyListeners();
}
}
void downvote(int index) {
if (index >= 0 && index < _queue.length) {
if (_queue[index].votes > 0) {
_queue[index].votes--;
}
_sortQueue();
notifyListeners();
}
}
void _sortQueue() {
_queue.sort((a, b) {
// First sort by votes (descending)
final voteComparison = b.votes.compareTo(a.votes);
if (voteComparison != 0) return voteComparison;
// If votes are equal, sort by time added (ascending - first come first serve)
return a.addedAt.compareTo(b.addedAt);
});
}
// Playback control
Future<void> playNext() async {
if (_queue.isNotEmpty) {
final nextItem = _queue.removeAt(0);
_currentTrack = nextItem.track;
// Use audio service to actually play the track
await _audioService.playTrack(nextItem.track);
// Add to recently played
_recentlyPlayed.insert(0, nextItem.track);
if (_recentlyPlayed.length > 10) {
_recentlyPlayed.removeLast();
}
notifyListeners();
print('Now playing: ${_currentTrack!.name} by ${_currentTrack!.artistNames}');
}
}
Future<void> togglePlayPause() async {
await _audioService.togglePlayPause();
notifyListeners();
}
Future<void> skipTrack() async {
await playNext();
}
Future<void> seekTo(double position) async {
if (_audioService.totalDuration != Duration.zero) {
final seekPosition = Duration(
milliseconds: (position * _audioService.totalDuration.inMilliseconds).round(),
);
await _audioService.seekTo(seekPosition);
}
}
// Initialize with some popular tracks
Future<void> initializeQueue() async {
if (_queue.isEmpty && _currentTrack == null) {
try {
final popularTracks = await _spotifyService.getPopularTracks();
for (final track in popularTracks.take(4)) {
final queueItem = QueueItem(
track: track,
votes: 10 - popularTracks.indexOf(track) * 2, // Decreasing votes
userVoted: false,
);
_queue.add(queueItem);
}
// Set up audio service callback for track completion
_audioService.onTrackComplete = () {
playNext();
};
// Start playing the first track
if (_queue.isNotEmpty) {
await playNext();
}
notifyListeners();
} catch (e) {
print('Error initializing queue: $e');
}
}
}
// Search functionality
Future<List<SpotifyTrack>> searchTracks(String query) async {
return await _spotifyService.searchTracks(query, limit: 20);
}
// Get queue as JSON for display
List<Map<String, dynamic>> get queueAsJson {
return _queue.map((item) => item.toJson()).toList();
}
// Get current track info for display
Map<String, dynamic>? get currentTrackInfo {
final track = currentTrack;
if (track == null) return null;
return {
'title': track.name,
'artist': track.artistNames,
'album': track.album.name,
'imageUrl': track.imageUrl,
'duration': _audioService.totalDurationString,
'currentTime': _audioService.currentPositionString,
'progress': progress,
'isPlaying': isPlaying,
};
}
}