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

242 lines
7.1 KiB
Dart

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';
import '../models/spotify_track.dart';
class AudioService extends ChangeNotifier {
static final AudioService _instance = AudioService._internal();
factory AudioService() => _instance;
AudioService._internal() {
_initializePlayer();
}
final AudioPlayer _audioPlayer = AudioPlayer();
// Current track state
SpotifyTrack? _currentTrack;
bool _isPlaying = false;
bool _isLoading = false;
Duration _currentPosition = Duration.zero;
Duration _totalDuration = Duration.zero;
// Getters
SpotifyTrack? get currentTrack => _currentTrack;
bool get isPlaying => _isPlaying;
bool get isLoading => _isLoading;
Duration get currentPosition => _currentPosition;
Duration get totalDuration => _totalDuration;
double get progress => _totalDuration.inMilliseconds > 0
? _currentPosition.inMilliseconds / _totalDuration.inMilliseconds
: 0.0;
// Free audio sources for demo purposes
// Using royalty-free music from reliable sources
final Map<String, String> _demoAudioUrls = {
// Peaceful, lido-appropriate tracks
'pop1': 'https://www.bensound.com/bensound-music/bensound-relaxing.mp3',
'pop2': 'https://www.bensound.com/bensound-music/bensound-sunny.mp3',
'pop3': 'https://www.bensound.com/bensound-music/bensound-jazzcomedy.mp3',
'pop4': 'https://www.bensound.com/bensound-music/bensound-acousticbreeze.mp3',
'1': 'https://www.bensound.com/bensound-music/bensound-creativeminds.mp3',
'2': 'https://www.bensound.com/bensound-music/bensound-happyrock.mp3',
'3': 'https://www.bensound.com/bensound-music/bensound-ukulele.mp3',
'4': 'https://www.bensound.com/bensound-music/bensound-summer.mp3',
'5': 'https://www.bensound.com/bensound-music/bensound-happiness.mp3',
};
void _initializePlayer() {
// Listen to player state changes
_audioPlayer.onPlayerStateChanged.listen((PlayerState state) {
_isPlaying = state == PlayerState.playing;
_isLoading = state == PlayerState.stopped && _currentTrack != null;
notifyListeners();
});
// Listen to position changes
_audioPlayer.onPositionChanged.listen((Duration position) {
_currentPosition = position;
notifyListeners();
});
// Listen to duration changes
_audioPlayer.onDurationChanged.listen((Duration duration) {
_totalDuration = duration;
notifyListeners();
});
// Listen for track completion
_audioPlayer.onPlayerComplete.listen((_) {
_onTrackComplete();
});
}
Future<void> playTrack(SpotifyTrack track) async {
try {
_isLoading = true;
_currentTrack = track;
notifyListeners();
// Check if we have a demo URL for this track
String? audioUrl = _demoAudioUrls[track.id];
if (audioUrl != null) {
// Play the demo audio
await _audioPlayer.play(UrlSource(audioUrl));
print('Playing audio for: ${track.name} by ${track.artistNames}');
} else {
// For tracks without demo URLs, simulate playback
print('Simulating playback for: ${track.name} by ${track.artistNames}');
_simulateTrackPlayback(track);
}
_isLoading = false;
notifyListeners();
} catch (e) {
print('Error playing track: $e');
_isLoading = false;
// Fallback to simulation
_simulateTrackPlayback(track);
notifyListeners();
}
}
void _simulateTrackPlayback(SpotifyTrack track) {
// Set simulated duration
_totalDuration = Duration(milliseconds: track.durationMs);
_currentPosition = Duration.zero;
_isPlaying = true;
// Simulate playback progress
_startSimulatedProgress();
}
void _startSimulatedProgress() {
if (_isPlaying && _currentTrack != null) {
Future.delayed(const Duration(seconds: 1), () {
if (_isPlaying && _currentTrack != null) {
_currentPosition = _currentPosition + const Duration(seconds: 1);
if (_currentPosition >= _totalDuration) {
_onTrackComplete();
} else {
notifyListeners();
_startSimulatedProgress();
}
}
});
}
}
Future<void> togglePlayPause() async {
try {
if (_isPlaying) {
await _audioPlayer.pause();
} else {
if (_currentTrack != null) {
// Check if we have a real audio URL
String? audioUrl = _demoAudioUrls[_currentTrack!.id];
if (audioUrl != null) {
await _audioPlayer.resume();
} else {
// Resume simulation
_isPlaying = true;
_startSimulatedProgress();
}
}
}
notifyListeners();
} catch (e) {
print('Error toggling play/pause: $e');
// Fallback to simulation toggle
_isPlaying = !_isPlaying;
if (_isPlaying) {
_startSimulatedProgress();
}
notifyListeners();
}
}
Future<void> stop() async {
try {
await _audioPlayer.stop();
} catch (e) {
print('Error stopping audio: $e');
}
_isPlaying = false;
_currentPosition = Duration.zero;
_currentTrack = null;
notifyListeners();
}
Future<void> seekTo(Duration position) async {
try {
// Check if we have a real audio URL
if (_currentTrack != null && _demoAudioUrls.containsKey(_currentTrack!.id)) {
await _audioPlayer.seek(position);
} else {
// Simulate seeking
_currentPosition = position;
notifyListeners();
}
} catch (e) {
print('Error seeking: $e');
// Fallback to simulation
_currentPosition = position;
notifyListeners();
}
}
void _onTrackComplete() {
_isPlaying = false;
_currentPosition = Duration.zero;
notifyListeners();
// Notify that track is complete (for queue management)
onTrackComplete?.call();
}
// Callback for when a track completes
Function()? onTrackComplete;
@override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
// Get formatted time strings
String get currentPositionString => _formatDuration(_currentPosition);
String get totalDurationString => _formatDuration(_totalDuration);
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return '$twoDigitMinutes:$twoDigitSeconds';
}
// Add better demo audio URLs (using royalty-free sources)
void addDemoAudioUrl(String trackId, String audioUrl) {
_demoAudioUrls[trackId] = audioUrl;
}
// Add local asset support
Future<void> playAsset(SpotifyTrack track, String assetPath) async {
try {
_isLoading = true;
_currentTrack = track;
notifyListeners();
await _audioPlayer.play(AssetSource(assetPath));
print('Playing asset: $assetPath for ${track.name}');
_isLoading = false;
notifyListeners();
} catch (e) {
print('Error playing asset: $e');
_isLoading = false;
_simulateTrackPlayback(track);
notifyListeners();
}
}
}