fix(slint): reconcile now-playing card to the live track on seamless change
The poll loop's seamless-transition branch guessed whether a track-id change while still playing was a gapless hand-off via a peek_upcoming(1) == track_id heuristic. When that heuristic missed (queue pointer lagging the engine), the meta was never refreshed: the seek bar kept advancing while the title/art/album stayed on the previous track (the reported now-playing populate bug). Replace the heuristic with reconciliation to what is ACTUALLY playing. New frontend-agnostic primitive QueueManager::sync_current_to_id(id) moves the queue pointer to the track the engine advanced to (recording history + keeping the shuffle cursor aligned) and reports whether it moved; QbzCore wraps it and emits a queue update only on a real move. The poll loop now syncs to the live track_id and always refreshes the card, doing the record/sidebar/prefetch bookkeeping only on a genuine advance (a manual play already moved the pointer, so it is idempotent). cc
This commit is contained in:
3 files changed
+73
-24
No files matched your search
@@ -659,6 +659,38 @@ impl QueueManager {
|
||||
prev_idx.and_then(|idx| state.tracks.get(idx).cloned())
|
||||
}
|
||||
|
||||
/// Move the current pointer to the track whose id matches `id`, WITHOUT
|
||||
/// starting playback. Used to reconcile the queue pointer to a track the
|
||||
/// audio engine already advanced to on its own (a gapless hand-off
|
||||
/// happens inside the player, not through `next`), so the now-playing
|
||||
/// card never goes stale while the seek bar keeps moving.
|
||||
///
|
||||
/// Returns the matched track plus whether the pointer actually moved
|
||||
/// (`false` = it was already current). Returns `None` when no queue track
|
||||
/// has that id, leaving the pointer untouched.
|
||||
pub fn sync_current_to_id(&self, id: u64) -> Option<(QueueTrack, bool)> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let target = state.tracks.iter().position(|t| t.id == id)?;
|
||||
let moved = state.current_index != Some(target);
|
||||
if moved {
|
||||
// Record the outgoing track so `previous` still walks back.
|
||||
if let Some(curr_idx) = state.current_index {
|
||||
state.history.push_back(curr_idx);
|
||||
while state.history.len() > 50 {
|
||||
state.history.pop_front();
|
||||
}
|
||||
}
|
||||
state.current_index = Some(target);
|
||||
// Keep the shuffle cursor aligned with the new position.
|
||||
if state.shuffle {
|
||||
if let Some(pos) = state.shuffle_order.iter().position(|&x| x == target) {
|
||||
state.shuffle_position = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
state.tracks.get(target).cloned().map(|t| (t, moved))
|
||||
}
|
||||
|
||||
/// Jump to a track by its position in the `upcoming` list as returned by
|
||||
/// `get_state`. This is the position the user sees in the Queue sidebar;
|
||||
/// the method resolves it to the correct canonical index even when
|
||||
|
||||
Reference in new issue
Block a user