Refactor audio player to manage subscriptions more effectively and ensure proper cleanup

This commit is contained in:
Leon Bösche
2026-01-17 03:40:56 +01:00
parent 014b77a27e
commit 898922efd1

View File

@@ -12,43 +12,74 @@ class AudioPlayer {
final StreamController<String> _errorController =
StreamController<String>.broadcast();
// Store subscriptions for cleanup
StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription;
StreamSubscription? _playSubscription;
StreamSubscription? _pauseSubscription;
StreamSubscription? _endedSubscription;
StreamSubscription? _errorSubscription;
Stream<Duration> get positionStream => _positionController.stream;
Stream<Duration> get durationStream => _durationController.stream;
Stream<bool> get playingStream => _playingController.stream;
Stream<String> get errorStream => _errorController.stream;
void _disposeSubscriptions() {
_durationSubscription?.cancel();
_positionSubscription?.cancel();
_playSubscription?.cancel();
_pauseSubscription?.cancel();
_endedSubscription?.cancel();
_errorSubscription?.cancel();
_durationSubscription = null;
_positionSubscription = null;
_playSubscription = null;
_pauseSubscription = null;
_endedSubscription = null;
_errorSubscription = null;
}
Future<void> setUrl(String url) async {
// Clean up any existing subscriptions
_disposeSubscriptions();
try {
_audioElement = web.HTMLAudioElement();
_audioElement!.src = url;
_audioElement!.crossOrigin = 'anonymous'; // Handle CORS
// Set up event listeners
_audioElement!.onLoadedMetadata.listen((_) {
_durationController.add(
Duration(milliseconds: (_audioElement!.duration * 1000).toInt()),
);
// Set up event listeners and store subscriptions
_durationSubscription = _audioElement!.onLoadedMetadata.listen((_) {
if (_audioElement != null) {
_durationController.add(
Duration(milliseconds: (_audioElement!.duration * 1000).toInt()),
);
}
});
_audioElement!.onTimeUpdate.listen((_) {
_positionController.add(
Duration(milliseconds: (_audioElement!.currentTime * 1000).toInt()),
);
_positionSubscription = _audioElement!.onTimeUpdate.listen((_) {
if (_audioElement != null) {
_positionController.add(
Duration(milliseconds: (_audioElement!.currentTime * 1000).toInt()),
);
}
});
_audioElement!.onPlay.listen((_) {
_playSubscription = _audioElement!.onPlay.listen((_) {
_playingController.add(true);
});
_audioElement!.onPause.listen((_) {
_pauseSubscription = _audioElement!.onPause.listen((_) {
_playingController.add(false);
});
_audioElement!.onEnded.listen((_) {
_endedSubscription = _audioElement!.onEnded.listen((_) {
_playingController.add(false);
});
_audioElement!.onError.listen((_) {
_errorSubscription = _audioElement!.onError.listen((_) {
_errorController.add('Failed to load audio');
});
@@ -82,6 +113,7 @@ class AudioPlayer {
}
void dispose() {
_disposeSubscriptions();
_audioElement?.pause();
_audioElement = null;
_positionController.close();