Merge current TV recovery states into Conductarr wiring

This commit is contained in:
justinzeus 2026-11-05 23:24:21 +01:00
commit a00db96dc8
43 changed files with 1041 additions and 199 deletions

View file

@ -92,10 +92,10 @@ Settings list. Native Up traversal stays in content until the first focusable ro
then enters the nav. Turning the switch on renders Wolfskin Home and Libraries.
Discover and Requests are integrated Wolfskin surfaces; title hand-offs remain labelled.
Debug people, tabs and initials come from the shell-only projection of the locked
prototype fixture in `app/src/debug/assets/wolfskin-shell.json`; release resources
do not package it. Saved Jellyfin identities use existing upstream services. The
shell makes no Conductarr calls and does not add Conductarr sign-in.
Saved Jellyfin identities use the existing upstream session. Conductarr supplies
household labels, requester capabilities and library audiences after sign-in.
The locked shell projection in `app/src/debug/assets/wolfskin-shell.json` is used
only when the Advanced debug fixture switch is enabled; release excludes it.
Discover adds the picks hero, type chips, catalogue shelves, service pages, continuous
See all grids, Hidden picks, request chooser and receipts. Its `discover-title/...`
@ -104,13 +104,12 @@ request and pick actions. Unheld titles have no trailer control until the pinned
Conductarr title contract supplies a trailer.
The debug-only fixture and local art live in
`app/src/debug/assets/wolfskin-discover/`. Release builds remain on the explicit
Conductarr sign-in state. The fixture host cannot construct a network client:
debug request outcomes and dismissals stay in memory. The real mutation adapter
accepts an injected `ConductarrClient` and uses only its typed routes; it is not
wired to a live server. Unknown request outcomes force a fresh title-requests read
instead of blindly submitting again, including after closing a sheet or choosing
the same uncertain recipient from another chooser. Whole-series requests require every season.
`app/src/debug/assets/wolfskin-discover/`. The Advanced preview switch selects a
network-free fixture host; real providers are the default in debug and release.
The mutation adapter accepts the selected person's `ConductarrClient` and uses
only typed routes. Request submission first reads title receipts. Unknown outcomes
are checked, never blindly re-sent, including after closing a sheet or choosing
the same uncertain recipient again. Whole-series requests require season metadata.
Discover hero and Restore text use explicit per-line CSS allocations so native
paragraph overflow cannot enlarge the bottom-anchored stack. Restore subtitles
@ -205,15 +204,15 @@ disk cache. A missing saved library becomes a recovery panel; a saved library
stays visible when Jellyfin is unreachable. Playback resolves an actual film or
episode and checks Jellyfin before opening upstream's player. No season,
recommendation stub or different person's session is promoted to playback.
An unresolved person switch shows the same sign-in guard for Home as for native
library content, before mounting Home's source reads.
An unresolved person switch guards Home and both owned/upstream library content
before mounting any source read with the prior person's Jellyfin session.
Debug variants additionally package the locked fixture and local artwork under
`wolfskin-design/src/debug/assets/wolfskin-home/`; release variants exclude them.
The fixture provider supplies the approved household data and Home states without
network reads in tests. Conductarr sign-in is not implemented here: real signed-out
Home hides every rating and shows "Discover needs sign-in". No Seerr fallback is
used. Catalogue data and ratings in debug captures are fixture-backed.
The explicit fixture provider supplies approved household data and Home states
without network reads in tests. Real signed-out Home hides every rating and shows
"Discover needs sign-in"; signed-in Home reads the Conductarr continuation and
ratings-only endpoint. No Seerr fallback is used.
Home, Libraries and Discover reuse `TitleCard`, `Caption` and `LibrarySeeAllCard`;
those screens and Requests share one `WolfskinImages` loader. Exact-slot requests use RGB565 for posters,
@ -251,13 +250,12 @@ for Back, and closing the sheet restores its opener. Selecting a root or changin
person clears the title-return link. Shared's authenticated actor always reads
“by Shared”, never “by you”.
The debug-only provider loads `app/src/debug/assets/wolfskin-requests.json` and
its eight local posters, projected from the locked prototype fixture. Six stage
rows feed the running debug shell; the Shared and focused-rating records feed
native acceptance states. The identical projection is in design-test resources.
No “sample” or “example stages” pill is drawn. Release builds package none of
these assets and show Requests unavailable until Conductarr sign-in supplies
the generated-client data path. There is no Seerr fallback or handwritten HTTP.
The debug fixture switch loads `app/src/debug/assets/wolfskin-requests.json` and
its eight local posters. Shared and focused-rating records also feed the native
acceptance states; the identical projection is in design-test resources.
No “sample” or “example stages” pill is drawn. The default provider instead maps
Conductarr household activity, authenticated actors and display artwork into
the same sheet. Release excludes all fixture assets. There is no Seerr fallback.
Foreground refreshes once; an open sheet or unsettled requests refresh every
30 seconds while started. The immutable debug projection is loaded once, not
decoded again on every poll.
@ -299,8 +297,7 @@ 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;
this slice does not claim to implement title pages, TV sign-in, release or device
deployment.
title pages, release and device deployment remain outside the Requests component.
## Libraries and See all
@ -323,8 +320,8 @@ The debug-only `wolfskin-library.json` and `library/assets/` contain the locked
prototype's titles and local poster art. Release libraries read through
`LibraryReadFacade`, with 200-item Jellyfin chunks and ten-minute per-server/person
snapshots using the existing atomic disk cache. Saved data is painted before a
quiet refresh; a failed refresh keeps it. Production ratings remain hidden until
Conductarr sign-in exists.
quiet refresh; a failed refresh keeps it. Production ratings remain hidden before
Conductarr sign-in and use the shared ratings-only provider afterwards.
Home/Discover shelves open a full list through
`WolfskinSeeAllLists.open(person, key, title, fullList, navigation, loadNextPage)`.
@ -340,6 +337,109 @@ 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/`.
## Offline states and recovery
Owned screens share `RecoveryToast`: the warning glyph, 760 px maximum width,
22/28 px padding, 14 px icon gap, 22 px corners and CSS-sized body line boxes.
Network failures never expose transport exception text. Playback failure reads
“Can’t reach Jellyfin to play. Check the network and try again.” and leaves the
originating card focused; no simulated player hand-off panel is shipped. Retry
failure reads “Still can’t reach it. The last saved view stays on screen.”
Library headers and the nav publish the same saved-copy timestamp. Failed library
refreshes retain cards and expose a reachable Try again tail. Discover retains its
current catalogue after a failed refresh, adds a recovery tail and disables request
and dismissal writes until recovery; a revoked sign-in discards that saved projection.
Previously observed plates can remain visible offline, but moving to an uncached
card cannot start a ratings read. `SavedPill` shares the compact cached-header
treatment across Home, Libraries and Requests; the nav retains its larger wifi chip.
Requests focus, observed ratings and retained rows are partitioned by selected person.
A failed authenticated read preserves the last rows and their timestamp; Down from
the final row reaches Try again and Up returns to the row. Signed-out data is not
retained as an authenticated copy. Returning to a foreground person triggers that
person's refresh rather than waiting for the prior person's polling timer.
Home disk snapshots remain server/user scoped. A new identity starts with its own
skeleton or saved copy, never the previous person's projection; late responses from
an older read cannot overwrite the current identity. A disk-write failure does not
turn a successful Jellyfin read into an outage. Skeleton-only Home, Library and
Discover screens keep focus in the nav even when Down is pressed repeatedly.
Library/Discover posters use the same title/glyph/160-degree gradient fallback as
Home, including an actual Coil read failure. Discover hero image failures use the
shared ambient background; missing artwork is not a successful data refresh.
`RecoveryStateTest`, `RecoveryCaptureTest`, `HomeRecoveryTest` and `PlayerHandoffTest`
exercise state transitions, person isolation, native recovery focus/geometry and the
real upstream playback destination with offline fixtures. New native captures and
rendered text/font metrics go to `build/wolfskin-captures/recovery/`; Dutch score
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.
## Conductarr production wiring
The default endpoint is `BuildConfig.CONDUCTARR_BASE_URL`:
`https://wolfskin-tv.justinvisser.org`. Override **Conductarr server URL** beside
**Wolfskin screens** in Advanced settings; blank restores the build default.
HTTPS is required except for loopback fake servers. **Wolfskin preview fixtures**
is a debug-only, off-by-default switch; it never sends HTTP mutations.
`ConductarrSession` borrows the currently selected user's existing Jellyfin access
token. It sends only `Authorization: MediaBrowser Token="<token>"`, with no cookie,
API key or second credential store. Person/server/token/endpoint changes discard
the old client and in-memory providers. LKG files are isolated by person and client
identity in app-private, backup-excluded storage. A 401 clears that identity's
cache and returns to sign-in states; Try again reconstructs and verifies the client.
| Screen seam | Production supplier |
| --- | --- |
| People and nav | Operator, profiles, library audiences; saved Jellyfin users remain the only selectable logins |
| Home continuation | Recommendation feed, catalogue options/discovery; Coming soon stays Discover-only |
| Discover | Feed/contributions, services, New on, Trending, genres, Coming soon, parameterized service shelves, dismissal reads/writes |
| Catalogue paging | Two-shelf read-ahead, distinct title identities, per-parameter disk pages, retry/duplicate-only tails; no page counters |
| 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 |
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.
Not done here: adopting the new provider inside the independently reviewed title
screen family; 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.
### Manual smoke after endpoint deployment (no build command)
Use an already authorized test build and a sandbox that accepts its selected
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.
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;
Try again restores data without changing Jellyfin login or copying a token.
5. Have the sandbox accept a Shared-on-behalf request but lose its POST response.
Use Check again: the receipt says Shared, and the sandbox records one POST.
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.
## Toolchain and disk use
`Dockerfile` pins Temurin JDK `21.0.11+10` by image digest, command-line tools