The video worker manager's coverage cards only switched the view — they never
re-queued failed items, so not_found/error items sat in the retry cooldown and
the worker reported 'all matched' / idle while failures piled up. Clicking a
Movies/Shows coverage card now pins that group AND resets its failed matches
(not_found/error -> pending) so the worker sweeps them, mirroring the music
worker manager. (Episodes are a sync cascade, not a match queue, so unchanged.)
OMDb now has the same setup as TMDB/TVDB: a yellow dashboard orb (★ glyph) that
spins/idles in the worker-orb animation, an entry in Manage Workers (Ratings
coverage cards, pause/resume, retry, search), and a BACKGROUND ratings pass.
- Worker 'ratings mode' (is_ratings): instead of a match queue it pulls
ratings_next() (library items with an imdb_id and ratings_synced=0), fetches
IMDb/RT/Metacritic, applies + marks synced. So the whole library gets ratings,
not just titles you open (schema v7: ratings_synced).
- enrichment_breakdown/unmatched/retry get an 'omdb' branch (coverage =
ratings-filled, not matched). build_clients includes omdb; the lazy on-view
backfill uses the omdb worker's client.
- Dashboard orb + Manage Workers entry (★ glyph fallback where there's no logo),
yellow accent.
Seam tests: omdb worker rates the queue (ratings mode), ratings breakdown.
Brings the video modal to parity with music's:
- 'Process first everywhere' control (Movies/Shows/Auto) in the topbar — a global
setting that pins which kind every worker processes first. enrichment_next takes
a priority kind; the worker reads enrichment_priority each loop; GET/POST
/api/video/enrichment/priority persists it. Reuses music's .em-global styling.
- Needs-matching bar now has a live count, status filter (All unmatched / Not
found / Pending) and a debounced search (reusing .em-select / .em-search),
matching the music modal. Episode view stays read-only.
- Live glow (scoped to #vem-overlay): pulsing running dot, accent glow on the
selected worker row + active process-first/kind, and a pulsing 'now processing'
chip in the worker accent. Music's shared .em-* styles untouched.
Seam tests: priority pins kind in enrichment_next + worker honors the setting,
priority endpoint GET/POST + validation, modal feature markup pinned.
Episodes ride along with their show instead of being a separate (tens-of-thousands)
queue: when the TMDB worker matches a show, it now backfills every season's
episodes — still / overview / rating — via /tv/<id>/season/<n> (one call per
season, gap-only so server data is never clobbered). Also backfills season
overviews.
The worker manager 'knows about it': the TMDB breakdown gains an Episodes
coverage entry (matched = has art, rest = pending), shown as its own card; the
Episodes view lists episodes still missing art. It's coverage-only, kept out of
the worker's idle/pending calc so it never blocks 'Complete'.
Seam tests: client season parse, worker cascade fills episodes, gap-only backfill
+ season overview, breakdown coverage (tmdb only), missing-art list, idle calc
ignores episode coverage.
A failed lookup CALL (network/429/5xx/timeout, or an expired TVDB token) was
recorded as 'not_found' — permanently logging a transient blip as 'no match'
and parking the item for retry_days. Now mirrors the music workers' proven
pattern:
- New 'error' status, distinct from 'not_found'; enrichment_next retries BOTH
after retry_days, so errors recover and the queue still advances (no poison
loop). breakdown/unmatched/retry-all and the modal account for it (shown with
the outstanding/pending bucket).
- TMDB/TVDB clients raise on non-200 (429/5xx) so the worker records 'error',
not a false not_found.
- TVDB re-authenticates once on a 401 (expired token) instead of failing every
match for the rest of the run.
Seam tests: error!=not_found, error retried after window, 429 raises, TVDB
token refresh, UI accounts for errors.
TVDB enriches shows only, but selectWorker hardcoded state.kind='movie', so
picking TVDB queried tvdb+movie (always empty) and rendered a bogus Movies
view. Each worker now declares its kinds (tmdb: movie+show, tvdb: show) and the
panel defaults to the worker's first kind. Backend was already safe (returns
empty for unsupported service+kind); pinned that invariant with a seam test.
The modal set no accent vars, so it fell back to the default purple. Now it
sets --row-accent on each rail row and --em-accent / --em-accent-rgb on the
panel from the selected worker — exactly how the music modal themes itself. The
modal's 23 rgba(var(--em-accent-rgb)) / var(--row-accent) rules now render in
TMDB light-blue / TVDB purple per selection.
Root cause both buttons looked black: --ve-accent was space-separated
(1 180 228) but used in rgba(var(--ve-accent), a) -> invalid 'rgba(1 180 228, a)'
so the color silently failed. Switched --ve-accent to comma-separated (matching
music's --accent-rgb) and fixed all fallbacks -> the accent + glow now render.
- TMDB: vibey light blue (56,189,248); TVDB: purple (168,85,247).
- TVDB brand mark inverted everywhere (dashboard button, modal rail, settings
frame) so the dark logo reads on the dark UI.
- Logos (the URLs you gave) everywhere the services are listed: dashboard
buttons, Manage-Workers modal rail, and the settings API-config frames —
matching how music shows enrichment-service logos.
- Dashboard hover tooltips now reuse music's shared .tooltip-content/-header/
-body/-status/-current/-progress classes + the same positioning, so they look
identical to the music enrichment tooltips (Status / current item / Progress)
instead of my bespoke style. Music CSS untouched (shared classes, reused).
The dashboard 'Manage Workers' button now opens a video enrichment modal that
reuses music's global .em-* modal CSS (identical look) but is entirely its own,
isolated JS: own #vem-overlay, event-delegated (no inline handlers, no music
function calls), targets /api/video/enrichment, shows only TMDB/TVDB with
movie/show coverage.
- Rail of workers (status dot + coverage), panel with pause/resume, per-kind
coverage cards (matched/not-found/pending segmented bars), and a paged
unmatched browser with retry (item + retry-failed).
- Polls the selected worker every 3s. The few invented sub-classes are styled
scoped to #vem-overlay so music is never affected. 87 tests green.