From 2d5f0691c2170de20e4f60a285296e79bed6eb6f Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 11 May 2026 19:43:37 -0400 Subject: [PATCH] diag(flutter): surface player errors + state transitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tap→audio measured at ~370ms — that path's fast. Real symptom: a few seconds of audio, then silence with no event surfaced to Flutter. ExoPlayer is failing/completing somewhere and we have no log to act on. Three changes, all log-only: - Add onError to playbackEventStream so stream failures (404, range- request bugs, decoder errors, network drops) print instead of silently halting playback. - Subscribe to playerStateStream and log playing + processingState on every transition. Silent stops will now show as a state shift to completed / idle / buffering with no resumption. - Subscribe to processingStateStream separately to catch fine-grained state transitions ExoPlayer reports between source advances. After hot-restart, tap-then-go-quiet should produce a sequence we can read — most likely either "processingState=completed" partway through (server returning premature EOS or wrong Content-Length) or a thrown error from ExoPlayer's source-loading path. --- flutter_client/lib/player/audio_handler.dart | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/flutter_client/lib/player/audio_handler.dart b/flutter_client/lib/player/audio_handler.dart index 0490f5ae..3bade142 100644 --- a/flutter_client/lib/player/audio_handler.dart +++ b/flutter_client/lib/player/audio_handler.dart @@ -12,7 +12,16 @@ import 'album_cover_cache.dart'; class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler { MinstrelAudioHandler() { - _player.playbackEventStream.listen(_broadcastState); + _player.playbackEventStream.listen( + _broadcastState, + // ExoPlayer surfaces stream errors (404, premature EOS, decoder + // failure, network drop) here. Without an error sink, the + // player just goes quiet — exactly the "starts then stops" + // symptom we hit. + onError: (Object e, StackTrace st) { + debugPrint('audio_handler: playbackEventStream error: $e\n$st'); + }, + ); _player.currentIndexStream.listen(_onCurrentIndexChanged); // Re-broadcast on shuffle/repeat changes so the PlaybackState's // shuffleMode + repeatMode fields stay current for UI subscribers. @@ -24,6 +33,15 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl // the index and the eviction loop can't reclaim them. _player.bufferedPositionStream .listen((_) => unawaited(_maybeRegisterStreamCache())); + // Diagnostic: log every player-state transition so silent stops + // surface as something we can read in the log. + _player.playerStateStream.listen((s) { + debugPrint( + 'audio_handler: state playing=${s.playing} processing=${s.processingState}'); + }); + _player.processingStateStream.listen((ps) { + debugPrint('audio_handler: processingState=$ps'); + }); } final AudioPlayer _player = AudioPlayer();