diff --git a/VERSION b/VERSION index 610e287..c86a09d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.23.1 +0.23.2 \ No newline at end of file diff --git a/frontend/src/components/PlayerModal.tsx b/frontend/src/components/PlayerModal.tsx index 76b41eb..560de63 100644 --- a/frontend/src/components/PlayerModal.tsx +++ b/frontend/src/components/PlayerModal.tsx @@ -50,7 +50,7 @@ function loadYouTubeApi(): Promise { 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,6 +101,9 @@ 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 ); + // 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; diff --git a/frontend/src/lib/releaseNotes.ts b/frontend/src/lib/releaseNotes.ts index c89c59a..e166739 100644 --- a/frontend/src/lib/releaseNotes.ts +++ b/frontend/src/lib/releaseNotes.ts @@ -14,6 +14,14 @@ export interface ReleaseEntry { } export const RELEASE_NOTES: ReleaseEntry[] = [ + { + version: "0.23.2", + date: "2026-07-05", + 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: 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.", + ], + }, { version: "0.23.1", date: "2026-07-05",