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 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 _queue = []; // Recently played final List _recentlyPlayed = []; // Getters SpotifyTrack? get currentTrack => _audioService.currentTrack ?? _currentTrack; bool get isPlaying => _audioService.isPlaying; double get progress => _audioService.progress; List get queue => List.unmodifiable(_queue); List 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 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 togglePlayPause() async { await _audioService.togglePlayPause(); notifyListeners(); } Future skipTrack() async { await playNext(); } Future 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 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> searchTracks(String query) async { return await _spotifyService.searchTracks(query, limit: 20); } // Get queue as JSON for display List> get queueAsJson { return _queue.map((item) => item.toJson()).toList(); } // Get current track info for display Map? 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, }; } }