Show subtitle state on title detail #201

Closed
opened 2026-08-24 21:28:40 +01:00 by naps62-yolo · 1 comment
Owner

Show subtitle state on the title detail surface (§9.6) — the answer to "does
this have Portuguese, and where did it come from".

Per media file, one row of chips: each language present, its origin (embedded,
extracted, the provider name, or the engine that translated it), forced or SDH
where applicable, and whether alass synced it or its sync was rejected. A
machine translation reads as machine-made at a glance; that is the whole point
of tracking origin.

A language that is wanted and missing shows as missing, with the reason from
the attempt row — searching, no candidates, waiting on budget, translation
failed. Not an empty space.

This is area/web: go through the impeccable skill before writing markup,
per CLAUDE.md. Chips and fixed structure, no horizontal scroll, consistent
with the release deck.

Depends on: #199

Show subtitle state on the title detail surface (§9.6) — the answer to "does this have Portuguese, and where did it come from". Per media file, one row of chips: each language present, its origin (embedded, extracted, the provider name, or the engine that translated it), forced or SDH where applicable, and whether `alass` synced it or its sync was rejected. A machine translation reads as machine-made at a glance; that is the whole point of tracking origin. A language that is wanted and missing shows as missing, with the reason from the attempt row — searching, no candidates, waiting on budget, translation failed. Not an empty space. This is `area/web`: go through the `impeccable` skill before writing markup, per `CLAUDE.md`. Chips and fixed structure, no horizontal scroll, consistent with the release deck. Depends on: #199
naps62-yolo added this to the Subtitles milestone 2026-08-24 21:28:40 +01:00
naps62-yolo added the area/webdifficulty/moderatephase/9-subtitlestype/feature labels 2026-08-24 21:28:40 +01:00
naps62-yolo added a new dependency 2026-08-24 21:29:28 +01:00
naps62-yolo added a new dependency 2026-08-24 21:29:28 +01:00
Author
Owner

Implemented per §9.6/§15: a subtitle-status chip row on both the movie and series detail pages, one row per media file. Present languages get a chip carrying origin (embedded/extracted/provider name/translated engine), SDH, forced and a "sync flagged" marker for a rejected alass run; a machine translation carries a dashed border in addition to the MT text marker so it reads as machine-made at a glance without hovering. A wanted-but-missing language gets its own amber chip naming why — searching, no candidates, capped, or failed with the last-failure detail (truncated, full text in the tooltip).

Backend: added GET /api/movies/{id}/subtitles/status, GET /api/episodes/{id}/subtitles/status, and a bulk GET /api/series/{id}/subtitles/status (mirroring series::files's join, so the series detail page costs one call rather than one per episode). Satisfaction is computed from subtitle_files, never from subtitle_attempts.state, per §15's explicit rule that the attempt row must never become a second source of truth. A missing language with no attempt row yet (the reconcile loop hasn't reached it — #196 isn't wired in yet) reads the same as searching.

Frontend: new web/src/subtitles.ts data module (interfaces + fetch functions + label formatting), wired into movieMain/seriesMain in main.ts, styled with the existing token system — reused the release deck's data-verdict="eligible" green and the library's data-movie-state="missing" amber ramps rather than inventing new colours, plus one new .chip[data-mt="true"] dashed-border rule. Went through the impeccable skill; the detector found nothing. Verified live with agent-browser against a seeded dev database (desktop + 390px mobile — wraps cleanly, no horizontal scroll) for both the movie and the series/episode surfaces.

Branch: subtitles/201-title-subs-ui (pushed).

just ci: passed (658 tests, clippy, fmt, machete, biome, tsc, check-tokens all green).

Findings

  • Issue #196 (reconcile-subs) has not landed on blitz/subtitles yet — nothing currently calls arr_db::subtitles::want(), so subtitle_attempts rows only exist for languages a manual grab/translate/search has already touched. This UI is correct either way (falls back to searching for an untouched wanted language), but until #196 lands, most real files will show missing chips as searching even when nothing has actually searched yet — worth knowing when reviewing screenshots against a live library.
  • Subtitle extraction from text-format embedded tracks (§15) and embedded-track recording in general don't appear to be wired into the import/probe pipeline anywhere yet — arr_db::NewSubtitleFile::embedded/::extracted are only ever constructed in arr-api's own tests. Out of scope here, but the "embedded" and "extracted" origin chips this issue asks for won't have real data until that lands.
Implemented per §9.6/§15: a subtitle-status chip row on both the movie and series detail pages, one row per media file. Present languages get a chip carrying origin (embedded/extracted/provider name/translated engine), SDH, forced and a "sync flagged" marker for a rejected `alass` run; a machine translation carries a dashed border in addition to the `MT` text marker so it reads as machine-made at a glance without hovering. A wanted-but-missing language gets its own amber chip naming why — `searching`, `no candidates`, `capped`, or `failed` with the last-failure detail (truncated, full text in the tooltip). Backend: added `GET /api/movies/{id}/subtitles/status`, `GET /api/episodes/{id}/subtitles/status`, and a bulk `GET /api/series/{id}/subtitles/status` (mirroring `series::files`'s join, so the series detail page costs one call rather than one per episode). Satisfaction is computed from `subtitle_files`, never from `subtitle_attempts.state`, per §15's explicit rule that the attempt row must never become a second source of truth. A missing language with no attempt row yet (the reconcile loop hasn't reached it — #196 isn't wired in yet) reads the same as `searching`. Frontend: new `web/src/subtitles.ts` data module (interfaces + fetch functions + label formatting), wired into `movieMain`/`seriesMain` in `main.ts`, styled with the existing token system — reused the release deck's `data-verdict="eligible"` green and the library's `data-movie-state="missing"` amber ramps rather than inventing new colours, plus one new `.chip[data-mt="true"]` dashed-border rule. Went through the `impeccable` skill; the detector found nothing. Verified live with `agent-browser` against a seeded dev database (desktop + 390px mobile — wraps cleanly, no horizontal scroll) for both the movie and the series/episode surfaces. Branch: `subtitles/201-title-subs-ui` (pushed). `just ci`: passed (658 tests, clippy, fmt, machete, biome, tsc, check-tokens all green). ## Findings - Issue #196 (reconcile-subs) has not landed on `blitz/subtitles` yet — nothing currently calls `arr_db::subtitles::want()`, so `subtitle_attempts` rows only exist for languages a manual grab/translate/search has already touched. This UI is correct either way (falls back to `searching` for an untouched wanted language), but until #196 lands, most real files will show missing chips as `searching` even when nothing has actually searched yet — worth knowing when reviewing screenshots against a live library. - Subtitle extraction from text-format embedded tracks (§15) and embedded-track recording in general don't appear to be wired into the import/probe pipeline anywhere yet — `arr_db::NewSubtitleFile::embedded`/`::extracted` are only ever constructed in `arr-api`'s own tests. Out of scope here, but the "embedded" and "extracted" origin chips this issue asks for won't have real data until that lands.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#201