fix(player): return from background audio onto the episode it advanced to
An episode that ends while backgrounded in audio-only mode advances in the
backend, but player_exit_background_audio returned only a position, so the
video page reloaded the episode it was mounted with -- the previous one, at
the new episode's timestamp.
The command now returns BackgroundAudioResume { itemId, positionSeconds }.
planHandoffReturn yields "other-item" when the id differs from the mounted
one, and the player page navigates to that episode with resumeAt=<seconds>,
marking the outgoing episode watched and suppressing its stale stop report.
TRACES: UR-040, UR-023 | DR-296 | UT-265, UT-266
This commit is contained in:
@@ -120,6 +120,12 @@
|
||||
onNext?: () => void; // Called when user clicks next episode button
|
||||
hasNext?: boolean; // Whether there is a next episode available
|
||||
isLive?: boolean; // Live stream (Live TV) - no seek bar, no resume, no progress reporting
|
||||
/**
|
||||
* Returning from background audio found the backend on a different item
|
||||
* (the episode advanced while backgrounded). The page switches to it; this
|
||||
* player must not reload the one it was mounted with. (DR-296)
|
||||
*/
|
||||
onResumeOtherItem?: (itemId: string, positionSeconds: number) => void;
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -137,6 +143,7 @@
|
||||
onNext,
|
||||
hasNext = false,
|
||||
isLive = false,
|
||||
onResumeOtherItem,
|
||||
}: Props = $props();
|
||||
|
||||
// The id this player instance reports progress against. Snapshotted from the
|
||||
@@ -2035,9 +2042,12 @@
|
||||
const wasPlaying = shouldResumeOnForeground(handoffState.wasPlaying, get(playerState).kind);
|
||||
handoffState = { ...initialHandoffState };
|
||||
try {
|
||||
// Absolute position the native audio reached (base offset applied in Rust).
|
||||
const pos = await commands.playerExitBackgroundAudio();
|
||||
log.debug("Returning from background audio at:", pos.toFixed(1));
|
||||
// The item the native audio is on and the absolute position it reached
|
||||
// (base offset applied in Rust). The item may not be `media`: an episode
|
||||
// that ended while backgrounded has already advanced in the backend.
|
||||
const resume = await commands.playerExitBackgroundAudio();
|
||||
const pos = resume.positionSeconds;
|
||||
log.debug("Returning from background audio at:", pos.toFixed(1), "item:", resume.itemId);
|
||||
|
||||
isMediaReady = false;
|
||||
// The foreground seek below (pendingForegroundSeek/handleCanPlay) OWNS the
|
||||
@@ -2056,8 +2066,18 @@
|
||||
position: pos,
|
||||
wasPlaying,
|
||||
nativeStateKind: get(playerState).kind,
|
||||
mountedItemId: media?.id ?? null,
|
||||
resumeItemId: resume.itemId,
|
||||
});
|
||||
|
||||
// Reloading `media` here would bring back the previous episode at the new
|
||||
// one's timestamp. Hand the switch to the page instead.
|
||||
// TRACES: UR-040, UR-023 | DR-296
|
||||
if (plan.target === "other-item" && plan.itemId) {
|
||||
onResumeOtherItem?.(plan.itemId, plan.position);
|
||||
return;
|
||||
}
|
||||
|
||||
pendingForegroundPlay = plan.shouldPlay;
|
||||
|
||||
// Determine the target stream + how the element/offset should be
|
||||
|
||||
Reference in New Issue
Block a user