team-2/CHALLENGE_2/sleepysound/lib/services/music_queue_service.dart
2025-08-02 04:48:34 +02:00

204 lines
5.3 KiB
Dart

import 'package:flutter/foundation.dart';
import '../models/spotify_track.dart';
import '../services/spotify_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();
// Current playing track
SpotifyTrack? _currentTrack;
bool _isPlaying = false;
double _progress = 0.0;
// Queue management
final List<QueueItem> _queue = [];
// Recently played
final List<SpotifyTrack> _recentlyPlayed = [];
// Getters
SpotifyTrack? get currentTrack => _currentTrack;
bool get isPlaying => _isPlaying;
double get progress => _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 simulation
void playNext() {
if (_queue.isNotEmpty) {
final nextItem = _queue.removeAt(0);
_currentTrack = nextItem.track;
_isPlaying = true;
_progress = 0.0;
// Add to recently played
_recentlyPlayed.insert(0, nextItem.track);
if (_recentlyPlayed.length > 10) {
_recentlyPlayed.removeLast();
}
notifyListeners();
print('Now playing: ${_currentTrack!.name} by ${_currentTrack!.artistNames}');
// Simulate track progress
_simulatePlayback();
}
}
void togglePlayPause() {
_isPlaying = !_isPlaying;
notifyListeners();
}
void skipTrack() {
playNext();
}
void _simulatePlayback() {
if (_currentTrack == null) return;
// Simulate track progress over time
Future.delayed(const Duration(seconds: 1), () {
if (_isPlaying && _currentTrack != null) {
_progress += 1.0 / (_currentTrack!.durationMs / 1000);
if (_progress >= 1.0) {
// Track finished, play next
playNext();
} else {
notifyListeners();
_simulatePlayback();
}
}
});
}
// 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);
}
// Start playing the first track
if (_queue.isNotEmpty) {
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 {
if (_currentTrack == null) return null;
return {
'title': _currentTrack!.name,
'artist': _currentTrack!.artistNames,
'album': _currentTrack!.album.name,
'imageUrl': _currentTrack!.imageUrl,
'duration': _currentTrack!.duration,
'progress': _progress,
'isPlaying': _isPlaying,
};
}
}