fix(player): queue skip + cut over silence on track tap
Two bugs in the audio handler caused the playback issues seen on the v2026.05.13.0 build: 1. **Queue button on the now-playing screen did nothing.** MinstrelAudio Handler never overrode skipToQueueItem, so taps in QueueScreen fell through to BaseAudioHandler's empty default. The queue UI updated nothing because the handler did nothing. Now overridden: rebuilds the source list via setQueueFromTracks(_lastTracks, initialIndex) so the targeted item plays cleanly even when its source hadn't been built yet by the background fill. 2. **Tapping a song in a playlist let the previous track bleed through until the new source finished building.** setQueueFromTracks awaits _buildAudioSource before swapping the player's source list, and that wait can be a few hundred ms on a cache miss. During the wait the old source kept playing while the mini player UI had already flipped to the new title/artist. Now pause()ing the player at the start of setQueueFromTracks silences the old source the moment the user taps. Also stashes the most recent TrackRef list as _lastTracks so skipToQueueItem can reconstruct sources without having to peek into just_audio's internal source list.
This commit is contained in:
@@ -68,6 +68,14 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
||||
/// new queue, leaving the player "locked" to a corrupted state.
|
||||
int _queueGeneration = 0;
|
||||
|
||||
/// Tracks the most recent setQueueFromTracks() input so
|
||||
/// skipToQueueItem can reconstruct the source list. just_audio
|
||||
/// requires every source to be built before it can be a skip
|
||||
/// target, but setQueueFromTracks only builds the initial source
|
||||
/// and fills the rest in the background — so a skip to an index
|
||||
/// past the fill front needs to rebuild from the stored tracks.
|
||||
List<TrackRef> _lastTracks = const [];
|
||||
|
||||
/// Volume stream for UI subscribers. Mirrors the just_audio player's
|
||||
/// volume directly; set via setVolume(double).
|
||||
Stream<double> get volumeStream => _player.volumeStream;
|
||||
@@ -105,6 +113,16 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
||||
// Reset suppress flag in case a prior backward-fill bailed on
|
||||
// gen check before reaching its `finally`.
|
||||
_suppressIndexUpdates = false;
|
||||
_lastTracks = tracks;
|
||||
|
||||
// Pause the old source immediately so the previous track stops
|
||||
// audibly the moment the user taps, instead of bleeding through
|
||||
// until the new source finishes building. setAudioSources below
|
||||
// will swap the source list cleanly; pause is the simplest way
|
||||
// to silence the player during the (possibly multi-100ms) build.
|
||||
if (_player.playing) {
|
||||
await _player.pause();
|
||||
}
|
||||
|
||||
// Populate the visible queue + current mediaItem immediately so
|
||||
// the player UI reflects the user's tap before any source has
|
||||
@@ -130,6 +148,23 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
||||
unawaited(_fillRemainingSources(tracks, clampedInitial, myGen));
|
||||
}
|
||||
|
||||
/// Switches playback to the [index]th queue item. The full
|
||||
/// just_audio source list isn't necessarily built yet
|
||||
/// (_fillRemainingSources runs in the background), so we
|
||||
/// reconstruct from the stored TrackRef list rather than calling
|
||||
/// _player.seek(index: ...) on a possibly-missing source. Calling
|
||||
/// setQueueFromTracks again is the safe path: it bumps the
|
||||
/// generation, cancels any in-flight fill, rebuilds source[0] as
|
||||
/// the target, and re-fills around. play() restarts playback so
|
||||
/// queue taps feel like "jump to this song" rather than "set the
|
||||
/// pointer and wait for me to press play."
|
||||
@override
|
||||
Future<void> skipToQueueItem(int index) async {
|
||||
if (index < 0 || index >= _lastTracks.length) return;
|
||||
await setQueueFromTracks(_lastTracks, initialIndex: index);
|
||||
await play();
|
||||
}
|
||||
|
||||
/// Background fill of the rest of the just_audio source list after
|
||||
/// the initial source is playing. Forward direction first (most
|
||||
/// common skipNext target). Backward inserts shift the player's
|
||||
|
||||
Reference in New Issue
Block a user