Integrate real Conductarr TV title and recovery providers

This commit is contained in:
justinzeus 2026-11-06 00:10:27 +01:00
commit 78322f588a
550 changed files with 5029 additions and 293 deletions

View file

@ -59,9 +59,9 @@ cover the Wolfskin nav.
The independent `:wolfskin-design` module owns the shell components, fixed person
colours, focus policy, per-person root-view memory, palette and motion. Its
`tokens.json` is the copied approved TV-spec source, retaining the locked prototype
SHA and provenance. `shell-tokens.json` adds shared shell, grid and sheet measurements;
`home-tokens.json` and `discover-tokens.json` add their screen geometry from that
same locked prototype. Generate the committed Kotlin, CSS and checksum manifest together:
SHA and provenance. `shell-tokens.json` adds shared shell/grid/sheet measurements;
`title-tokens.json`, `home-tokens.json` and `discover-tokens.json` add their screen
measurements from the same locked prototype. Generate Kotlin, CSS and the checksum manifest together:
```sh
python3 wolfskin-design/generate_tokens.py
@ -295,9 +295,10 @@ uv run --with cairosvg==2.9.1 python wolfskin-design/generate_request_marks.py
```
`RequestsCaptureTest` writes `st-081`…`st-086`, `st-088` and `st-111` at 1920×1080,
density 2.0. Title selection currently uses an explicitly labelled
`request-title/...` destination until the independent title screen is integrated;
title pages, release and device deployment remain outside the Requests component.
density 2.0. Title selection opens the shared native title destination by media
type and TMDB identity. Back restores the exact Coming-in row and expansion;
the temporary `request-title/...` placeholder is removed. TV sign-in, release
and device deployment remain separate.
## Libraries and See all
@ -328,8 +329,10 @@ Home/Discover shelves open a full list through
The optional suspend page provider returns `LibraryPage(items, hasMore)` using the
generated Conductarr client. The grid reads within ten cards of its end, deduplicates
title identities, and pauses on Try again or Show more rather than looping.
Title activation pushes the explicitly named `library-title/{id}` destination for
the title-screen implementation; this slice does not implement title pages.
Title activation opens `title/{id}` through `Destination.Wolfskin` for prototype
identities, or the existing typed media destination for real Jellyfin identities.
Movies, series, episodes and people use the native title family; collections and
music keep their upstream destinations.
`LibraryCaptureTest` renders the sixteen approved library/See all states offline.
`LibraryKeysTest` reads the approved JSON path and reaches Spirited Away in exactly
@ -337,6 +340,66 @@ the title-screen implementation; this slice does not implement title pages.
failure/retry, stale copies, and person/server isolation. Library captures are written
to `build/wolfskin-captures/library/`; Discover's independent captures use `discover/`.
## Title pages and people
`TitleSurface` owns the film/series/episode header, season chips, episode tiles,
cast and related shelves. `PersonSurface` owns the real biography and continuous
six-column filmography. Both reuse the shell/nav/back ladder, `LibraryPoster`,
the adaptive `LibraryRatingPlate`, shared captions and the single bounded
`WolfskinImages` loader. Image requests use exact slot sizes, RGB565 for opaque
posters, no crossfade, four concurrent reads and next-row prefetch. The fixed
title backdrop remains visible behind deep rows; only off-screen row images
leave composition.
The options row preserves complete labels beyond the copy-column measure.
Version choices close after selection; Audio and subtitles stays open, including
an explicit Off choice. Real source IDs use the Jellyfin SDK UUID parser, and
stream indexes remain the server's indexes rather than positions in a list.
Selections persist through the same playback repository as the upstream player.
Marking a whole series asks first; an explicit episode remains the exact play and
watched target even while another season is selected. Nested person/title visits
retain their entry's focus and selection state.
Hardware and remote Back share the sheet/picker/request-overlay precedence, then
pop a pushed title/person from content **or** nav. Initial loading has no content
target: Down stays in nav, while Back can still leave the title. A missing rating
set emits no layout node, so it cannot reserve an invisible extra facts gap.
`TitleReadFacade` confines SDK reads, server/user-scoped last-good disk copies,
person credits and playback mutations to one boundary. `ConductarrTitlePages`
adds real detail, cast, related titles, performer/filmography, readiness/receipts
and source ratings. A catalogue title gains held/play controls only after an
exact TMDB lookup returns this Jellyfin user's actual item identity. Episode
votes enrich those real episode identities; no catalogue stub becomes playable.
Debug-only `wolfskin-titles.json` and local artwork remain behind the explicit
Advanced fixture switch; release never packages or selects them.
Held trailers use Jellyfin remote/local trailer data. YouTube URLs target an
installed YouTube handler such as SmartTube, never a browser. No handler leaves
the title visible with a calm message. Titles nobody holds have no Trailer
button until Conductarr's trailer field exists. Title requests use the same
actor-safe chooser and identity-lifetime uncertain-submission ledger as Discover.
Signed-out and offline pages cannot submit; no Seerr fallback is used.
`TitleCaptureTest` writes the 34 approved title/person/options frames plus native
text weights, colours, line boxes and bounds at 1920×1080, density 2.0. The approved
typeface ruling keeps platform Roboto, not the browser's DejaVu Sans: no per-title
compensation and no wrapping-parity claim. Explicit CSS line boxes constrain title
headers, sheets, cast and poster captions without changing shared type tokens.
Fractional stack placement rounds only the accumulated position, preventing a
one-pixel-per-row drift. Focused controls share the canonical lift shadow.
`TitleKeysTest` replays real Android keys, including modal Back/hold, nearest-row
entry, remembered columns, long-sheet visibility and full media-table geometry.
`TitleAsyncFocusTest` delays title and person replies through the real destination
and view-model, then exercises content focus and hardware Back. Subtitles Off
maps to the player's explicit disabled sentinel, not its unspecified default.
`TitleLaunchKeysTest` starts at Who's watching and replays the full 4/8/15/11
sequences through the real Home hero/shelves and title/person surfaces.
`title-press-paths.json` supplies every event; no prefix is skipped or padded.
The shorter 1/5/6/2 title-entry replays remain as focused regressions. SDK cache,
exact-episode, track-index, identity, locale-layout and trailer-handler tests
run offline with in-process service replies and local art.
## Offline states and recovery
Owned screens share `RecoveryToast`: the warning glyph, 760 px maximum width,
@ -379,7 +442,7 @@ captures configure the Android resource locale as well as the JVM locale.
Conductarr transport and identity live outside the state components. A revoked
sign-in discards the current person's authenticated projections; an outage keeps
last-known-good (LKG) data and its Saved timestamp. No Seerr fallback or offline
request queue is used. Title-family screen adoption remains a separate integration.
request queue is used. Title and performer pages use the same authenticated cache.
## Conductarr production wiring
@ -405,21 +468,22 @@ cache and returns to sign-in states; Try again reconstructs and verifies the cli
| Request sheet | Capability-limited requesters, season metadata, POST receipt, receipt lookup before any resubmit; actor comes only from the token |
| Requests | Household activity, stage precedence, actor/requester evidence and display posters; foreground/30-second refresh |
| Ratings | Ratings-only read after 350 ms card dwell; immediate hero/title/Coming-in reads; per-type six-hour/300-entry cache; episode season votes |
| Additive title provider | Detail, cast, related, season/votes, performer, filmography, enrichment, display, title activity and uncached title receipts |
| Titles and people | Detail/enrichment/display, cast, related, real Jellyfin season episodes plus votes, performer/filmography, title activity and uncached receipts; shared request/hide workflows |
The pinned client exposes exactly the 22 TV operations, including ratings and
display. Conditional reads send ETags and reuse the saved body on 304. The fake
server tests use representative wire JSON, never a live Conductarr or Jellyfin.
`ConductarrSessionTest`, `ConductarrProjectionTest` and `ConductarrSurfaceTest`
cover identity/cache isolation, 401/retry, saved offline data, actor-safe requests,
ratings timing and a native real-provider Discover surface. The existing fixture
screen tests remain in the normal `make test` gate.
`ConductarrSessionTest`, `ConductarrProjectionTest`, `ConductarrTitlePagesTest`,
`ConductarrSurfaceTest`, `ConductarrHomeSurfaceTest` and `ConductarrTitleSurfaceTest` cover identity/cache
isolation, 401/retry, saved offline data, actor-safe requests, ratings timing,
title playback permissions/episode votes and native real-provider surfaces.
The existing fixture screen tests remain in the normal `make test` gate.
Not done here: adopting the new provider inside the independently reviewed title
screen family; deploying the dedicated hostname or widening server authority;
Not done here: deploying the dedicated hostname or widening server authority;
hardware installation/playback verification; releases, signing or version changes.
The existing title integration destinations remain explicit rather than being
silently replaced with invented catalogue/player data.
Unheld trailers remain absent because the server contract has no trailer field.
Saved Jellyfin logins are not fabricated from Conductarr profiles: a person
without an upstream token remains signed out. Fixtures require explicit debug opt-in.
### Manual smoke after endpoint deployment (no build command)
@ -428,8 +492,9 @@ Jellyfin login. No token needs to be copied, displayed or entered into Wolfskin.
1. In Advanced settings, set the sandbox URL with fixtures **off**. Select Shared,
then another saved person; verify their operator/requester and independent views.
2. Open Home, Discover, a service, Hidden picks and Requests. Check real catalogue
text, stage/actor labels, card dwell ratings and continuous See all loading.
2. Open Home, Discover, a service, Hidden picks and Requests. Open a title, cast
member and filmography credit; check real facts, related titles, episode votes,
stage/actor labels, card dwell ratings and continuous See all loading.
3. Make the sandbox return 503 after a successful read. Reopen: saved content and
Saved time remain, requests cannot submit, and Try again performs another read.
4. Return 401, then restore authentication. Verify ratings/request actions disappear;
@ -439,6 +504,13 @@ Jellyfin login. No token needs to be copied, displayed or entered into Wolfskin.
6. Restore the blank/default URL when the dedicated TV hostname and server's
22-operation authority are deployed. This checklist is manual, not a live gate.
Title and person surfaces publish saved timestamps through the same per-person
`RememberedView` partition and nav Saved indicator. Their timed notices render
the shared `RecoveryToast`; failed Try again and playback use `RecoveryMessage`
without transport details. A selected-person mismatch never renders the former
person's title/person projection. Title and performer disk copies remain isolated
by both server and authenticated user; native retry and cache-isolation regressions
exercise these boundaries without a live service.
## Toolchain and disk use