fix(player): freeze the launch queue so Loop All + Next/Previous use the original list
The player's queue came live from the feed query, which refetches under a watch-state filter as each video is auto-marked watched — steadily shrinking the list mid-session. Loop "All" could then never wrap back to the first video (earlier items had dropped out), and prev/next drifted. Snapshot the queue once at mount (useState lazy init) and step through that frozen list for the whole session; the live prop is ignored thereafter. Supersedes the earlier active-item pin with a list-level freeze that also fixes Loop All.
This commit is contained in:
parent
0fff5ad35d
commit
c84cb22e5a
2 changed files with 17 additions and 15 deletions
|
|
@ -50,7 +50,7 @@ function loadYouTubeApi(): Promise<any> {
|
|||
export default function PlayerModal({
|
||||
video,
|
||||
startAt,
|
||||
queue,
|
||||
queue: queueProp,
|
||||
startIndex,
|
||||
onClose,
|
||||
onState,
|
||||
|
|
@ -75,6 +75,14 @@ export default function PlayerModal({
|
|||
const qc = useQueryClient();
|
||||
// Browser/mouse Back closes the player instead of leaving the page.
|
||||
useBackToClose(onClose);
|
||||
// Freeze the launch queue: the player steps through the SAME filtered + sorted list it was
|
||||
// opened with, for its whole session. The live feed refetches and reorders/drops items under
|
||||
// us — e.g. auto-marking each video watched near its end removes it from a filtered feed — which
|
||||
// would otherwise shrink the queue mid-playback and break prev/next + Loop "All" (it could never
|
||||
// wrap back to the first item once earlier items dropped out). Snapshot once at mount via
|
||||
// useState's lazy initializer and ignore later prop changes. Items that turn out gone/
|
||||
// non-embeddable still surface the existing playback-error overlay when reached.
|
||||
const [queue] = useState(() => queueProp);
|
||||
// Precise upload date shown inline after the relative time (e.g. "9 yr ago · 12 Jan 2017").
|
||||
const fullDate = (d: string | null | undefined) =>
|
||||
d
|
||||
|
|
@ -93,18 +101,12 @@ export default function PlayerModal({
|
|||
// `video` in the queue (the feed passes its whole list + the clicked video, no index).
|
||||
startIndex != null ? queue?.[startIndex]?.id ?? video.id : video.id
|
||||
);
|
||||
// Resolve the playing item from the LIVE queue so prev/next + the N/M counter track it. But
|
||||
// the feed query reorders/refetches under us — e.g. auto-marking the current video watched near
|
||||
// its end invalidates the feed — and the playing item can drop OUT of the loaded queue. When
|
||||
// that happens we must KEEP PLAYING it, not snap back to queue[0] (which would silently jump to
|
||||
// an unrelated video mid-playback, ignoring loop/auto-advance). So pin the last resolved video
|
||||
// in a ref and fall back to it; neighbour stepping just treats it as sitting before queue[0].
|
||||
const foundIndex = queue ? queue.findIndex((v) => v.id === playingId) : -1;
|
||||
const inQueue = foundIndex >= 0;
|
||||
const activeRef = useRef<Video>(video);
|
||||
const active = inQueue ? queue![foundIndex] : activeRef.current;
|
||||
activeRef.current = active;
|
||||
const index = inQueue ? foundIndex : 0;
|
||||
// Locate the playing item in the frozen launch queue → drives prev/next + the N/M counter.
|
||||
// Because the queue is frozen, the playing id always stays in it (unlike the old live queue,
|
||||
// where a just-watched item could drop out and snap playback to queue[0]).
|
||||
let index = queue ? queue.findIndex((v) => v.id === playingId) : 0;
|
||||
if (index < 0) index = 0;
|
||||
const active = queue && queue[index] ? queue[index] : video;
|
||||
const hasQueue = !!queue && queue.length > 1;
|
||||
// startAt only applies to the item we opened with; advanced items resume their own pos.
|
||||
const resumeAt =
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ export const RELEASE_NOTES: ReleaseEntry[] = [
|
|||
{
|
||||
version: "0.23.2",
|
||||
date: "2026-07-05",
|
||||
summary: "Fixed the player jumping to a different video when the current one is marked watched.",
|
||||
summary: "The player now steps through the exact list it was opened with, so watching along no longer breaks Loop and Next/Previous.",
|
||||
fixes: [
|
||||
"Player: watching a video to the end (or otherwise marking it watched) no longer makes playback jump to an unrelated video. This also fixes Loop “One”, which the jump could override — the current video now correctly repeats even after it drops out of the live feed order.",
|
||||
"Player: it now plays through a frozen snapshot of the filtered, sorted list it was launched with. Previously, as each video was auto-marked watched it dropped out of a filtered feed, silently shrinking the queue mid-playback — which jumped playback to an unrelated video, and stopped Loop “All” from ever wrapping back to the first video once earlier ones were watched. Next/Previous and both Loop modes now work against the original list for the whole session.",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue