added audio output for demo songs since spotify api doesnt support audio streaming
This commit is contained in:
parent
6b93f1206d
commit
a91654df03
14 changed files with 1261 additions and 77 deletions
242
CHALLENGE_2/sleepysound/lib/services/audio_service.dart
Normal file
242
CHALLENGE_2/sleepysound/lib/services/audio_service.dart
Normal file
|
@ -0,0 +1,242 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue