offline updates and playhead fix

This commit is contained in:
Forrest 2026-08-16 12:46:30 -04:00
parent 7a199fe4df
commit 6663330260
14 changed files with 1673 additions and 545 deletions

View file

@ -263,7 +263,17 @@ class PlaybackController extends StateNotifier<PlaybackState>
(event) {
_posAnchor = event.updatePosition;
_posAnchorAt = event.updateTime;
state = state.copyWith(position: event.updatePosition);
// `updatePosition` is sampled at `updateTime`, i.e. slightly in the
// past. While playing, the ticker has already advanced the displayed
// position to ~now; writing the raw sample here would snap it backward
// every time an event fires (they fire periodically), then the ticker
// re-advances it — a visible flicker. So reflect the *interpolated*
// value (continuous with the ticker) while playing, and the raw value
// only when paused/buffering, where it's exact and nothing is ticking.
final pos = (state.playing && !state.buffering)
? _interpolatedPosition()
: event.updatePosition;
state = state.copyWith(position: pos);
},
onError: (Object e, StackTrace st) => _onPlayerError(e),
);
@ -281,12 +291,20 @@ class PlaybackController extends StateNotifier<PlaybackState>
/// the Subsonic metadata length) so it can't run past the end.
void _tickPosition() {
if (!state.playing || state.buffering) return;
state = state.copyWith(position: _interpolatedPosition());
}
/// The current position interpolated off [_posAnchor] against the wall clock,
/// clamped to [PlaybackState.effectiveDuration] (falls back to the Subsonic
/// metadata length) so it can't run past the end. Shared by the ticker and
/// the event listener so both agree — a mismatch between them is what causes
/// the playhead to visibly jump.
Duration _interpolatedPosition() {
final elapsed = DateTime.now().difference(_posAnchorAt);
if (elapsed.isNegative) return;
var pos = _posAnchor + elapsed;
var pos = elapsed.isNegative ? _posAnchor : _posAnchor + elapsed;
final total = state.effectiveDuration;
if (total > Duration.zero && pos > total) pos = total;
state = state.copyWith(position: pos);
return pos;
}
/// Id of the song we last ran play side effects for. Queue edits shift