From be907b49456c823ea24b99660e35a5b2ff68f158 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 16 Aug 2026 22:18:06 +0200 Subject: [PATCH] fix(home): stop Next Up repeating Continue Watching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jellyfin's /Shows/NextUp defaults EnableResumable=true, which returns a partially-watched episode as its own series' next up — precisely the episode /Items/Resume already returns. Home's "Next Episode" row and the TV landing's Next Up row therefore duplicated Continue Watching card for card. build_next_up_endpoint now sends EnableResumable=false, and because servers predating that parameter ignore it, filterInProgressNextUpItems also drops any next-up entry whose id appears in the resume list. It is the mirror of DR-089 and sits beside it: presentation-layer de-duplication over two lists the frontend already holds. The resume filter still reads its frontier from the unfiltered Next Up list, so pruning in-progress entries cannot resurrect a stale resume card. The code changes were swept into 5e8efa25 by a concurrent `git add -A`; this carries the remainder — DR-197 / JA-036 / UT-190..192, the renumbering off the DR-196 collision that commit created, the regenerated matrix, and the requirement-count guard. TRACES: UR-059 | DR-197, JA-036 | UT-190, UT-191, UT-192 --- docs/requirements.md | 5 + docs/traceability.md | 2362 +++++++++-------- scripts/extract-traces.test.ts | 6 +- src-tauri/src/repository/online.rs | 14 +- src/lib/stores/continueWatchingFilter.test.ts | 5 +- src/lib/stores/continueWatchingFilter.ts | 4 +- src/lib/stores/home.ts | 2 +- src/lib/stores/tv.ts | 2 +- 8 files changed, 1229 insertions(+), 1171 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index eafb6e00..42fc5d2c 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -171,6 +171,7 @@ API endpoints and data contracts required for Jellyfin integration. | JA-033 | Query favourite items (`Filters=IsFavorite`, recursive, scoped by item type) | Items | UR-067 | Done | | JA-034 | Read `UserData` (favourite, played, resume position) from item responses | UserData | UR-069 | Done | | JA-035 | Mark item played (`POST /Users/{userId}/PlayedItems/{itemId}`) | UserData | UR-025 | Done | +| JA-036 | Query next-up episodes excluding in-progress ones (`/Shows/NextUp` with `EnableResumable=false`) | Shows | UR-059 | Done | ### 2.3 Development Requirements @@ -350,6 +351,7 @@ Internal architecture, components, and application logic. | DR-191 | Forcing the WebView overlay to redraw from the Activity, because with the ExoPlayer **SurfaceView** beneath it the overlay's ordinary damage stopped reaching the screen: the page kept mutating — the clock text every second, the control bar's opacity going to 0 — while the display held whatever frame it last presented, over video that animated perfectly. Not a state defect; the live DOM showed the slider advancing 476 → 479 across three seconds behind a screen showing neither. Only **structural** changes got through, which is why the play overlay always appeared to work (an `{#if}` block, added and removed) while the progress bar never did, and why rotation lost the transport UI. A CSS animation cannot help, since opacity animates on the compositor without repainting the layer. **Superseded by DR-192**: this drove `postInvalidateOnAnimation` in a loop, which treats the symptom — the cause is the SurfaceView's separate layer, and removing that removes the need. Kept as the record of how the mechanism was identified | Android | UR-003, UR-004 | Superseded by DR-192 | | DR-195 | Play/pause works on the native path, because the frontend stops claiming a webview element is playing when there is none. `html5_playing` is Rust's record of "a webview `