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:
@@ -50,6 +50,9 @@
|
||||
// When advancing to a next episode we always start from the beginning,
|
||||
// even if the episode was previously started or watched.
|
||||
const restartParam = $derived($page.url.searchParams.get("restart") === "true");
|
||||
// Explicit start position in seconds — set when returning from background
|
||||
// audio onto an episode the backend advanced to (DR-296).
|
||||
const resumeAtParam = $derived(Number($page.url.searchParams.get("resumeAt")) || 0);
|
||||
|
||||
// Derive playback context from URL query params
|
||||
const playbackContext = $derived.by(() => {
|
||||
@@ -121,6 +124,7 @@
|
||||
$effect(() => {
|
||||
const id = itemId;
|
||||
const restart = restartParam;
|
||||
const resumeAt = resumeAtParam;
|
||||
if (id && id !== loadedItemId) {
|
||||
autoPlayLog.debug(
|
||||
"$effect triggered: loading new item",
|
||||
@@ -132,7 +136,11 @@
|
||||
);
|
||||
// restart=true (advancing to next episode) forces start-from-beginning,
|
||||
// bypassing the resume-progress check.
|
||||
loadAndPlay(id, restart ? 0 : undefined, restart);
|
||||
if (resumeAt > 0) {
|
||||
loadAndPlay(id, resumeAt);
|
||||
} else {
|
||||
loadAndPlay(id, restart ? 0 : undefined, restart);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -797,6 +805,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Background audio advanced to another episode; show that one where the audio
|
||||
* left off, rather than the episode this page was opened on.
|
||||
*
|
||||
* The outgoing episode was played out (the backend advances only past its
|
||||
* end), so it is recorded as watched and the VideoPlayer's unmount stop
|
||||
* report — which would carry the stale handoff position — is suppressed, as
|
||||
* for a manual skip.
|
||||
*
|
||||
* TRACES: UR-040, UR-023 | DR-296
|
||||
*/
|
||||
function handleResumeOtherItem(nextId: string, positionSeconds: number) {
|
||||
void reportSkippedEpisode(currentMedia?.id ?? itemId ?? null);
|
||||
const start = positionSeconds > 0 ? `resumeAt=${Math.floor(positionSeconds)}` : "restart=true";
|
||||
goto(`/player/${nextId}?${start}`, { replaceState: true });
|
||||
}
|
||||
|
||||
function handleSkipToNextEpisode() {
|
||||
if (nextEpisode) {
|
||||
// Skipping means "I'm done with this one" — record the outgoing episode as
|
||||
@@ -889,6 +914,7 @@
|
||||
onEnded={handleVideoEnded}
|
||||
hasNext={nextEpisode !== null}
|
||||
onNext={handleSkipToNextEpisode}
|
||||
onResumeOtherItem={handleResumeOtherItem}
|
||||
/>
|
||||
<NextEpisodePopup />
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user