Per-row audio codec summary (distinct lowercased codecs across an
item's audio streams) via scalar subquery on media_streams, rendered
as "ac3 · aac" in a new monospace Audio column.
v2026.04.15.9
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single landing page: stats grid up top, then scan controls + progress,
then recent items log. Drops the 'click → bounce to Scan' indirection.
- ScanPage pulls /api/dashboard for the stats card grid; refetches when
a scan completes so totals reflect the new state
- Scan page also owns the setup-complete redirect to /settings (was on
Dashboard) and the empty-library 'click Start Scan' nudge
- / route now renders ScanPage; /scan route deleted
- DashboardPage and its feature dir gone
- Nav: drop 'Dashboard', repoint 'Scan' to /
The pipeline tab fully replaces the audio list: same items, better
workflow. What the old list contributed (per-item details + skip/approve)
now lives inline on each pipeline card.
- delete src/routes/review/audio/index.tsx + src/features/review/AudioListPage.tsx
- /review/ now redirects to /pipeline (was /review/audio, which no longer exists)
- AudioDetailPage back link goes to /pipeline
- nav: drop the Audio link
- PipelineCard: three buttons on every card — Details (TanStack Link to
/review/audio/$id — the detail route stays, it's how you drill in),
Skip (POST /api/review/:id/skip), Approve (POST /api/review/:id/approve).
Remove the old 'Approve up to here' button (it was computing against
frontend ordering we don't want to maintain, and it was broken).
- SeriesCard: drop onApproveUpTo, pass new approve/skip handlers through
to each expanded episode card
- server: remove now-unused POST /api/review/approve-batch (no callers)
Subtitle extraction lives only in the pipeline now; a file is 'done' when it
matches the desired end state — no embedded subs AND audio matches the
language config. The separate Extract page was redundant.
- delete src/routes/review/subtitles/extract.tsx + SubtitleExtractPage
- delete /api/subtitles/extract-all + /:id/extract endpoints
- delete buildExtractOnlyCommand + unused buildExtractionOutputs from ffmpeg.ts
- detail page: drop Extract button + extractCommand textarea, replace with
'will be extracted via pipeline' note when embedded subs present
- pipeline endpoint: doneCount = is_noop OR status='done' (a file in the
desired state, however it got there); UI label 'N files in desired state'
- nav: drop the now-defunct 'Extract subs' link, default activeOptions.exact
to false so detail subpages (e.g. /review/audio/123) highlight their
parent ('Audio') in the menu — was the cause of the broken-feeling menu
Nav only exposed a subset; Dashboard, Audio review, Subtitle extract, Jobs,
and Paths were reachable only via URL. Add links for every top-level route:
- left: Dashboard, Scan, Pipeline, Audio, Extract subs, Subtitle mgr, Jobs
- right: Paths, Settings
Split the two subtitle pages explicitly (Extract subs = per-item extraction
queue, Subtitle mgr = language summary + title harmonization) so their
distinct purpose is visible from the nav instead of hidden under one label.
- execute: actually call isInScheduleWindow/waitForWindow/sleepBetweenJobs in runSequential (they were dead code); emit queue_status SSE events (running/paused/sleeping/idle) so the pipeline's existing QueueStatus listener lights up
- review: POST /:id/retry resets an errored plan to approved, wipes old done/error jobs, rebuilds command from current decisions, queues fresh job
- scan: dev-mode DELETE now also wipes jobs + subtitle_files (previously orphaned after every dev reset)
- biome: migrate config to 2.4 schema, autoformat 68 files (strings + indentation), relax opinionated a11y/hooks-deps/index-key rules that don't fit this codebase
- routeTree.gen.ts regenerated after /nodes removal
- remove nodes table, ssh service, nodes api, NodesPage route
- execute.ts: local-only spawn, atomic CAS job claim via UPDATE status
- wrap job done + subtitle_files insert + review_plans status in db transaction
- stream ffmpeg output per line with 500ms throttled flush
- bump version to 2026.04.13
useState was called after conditional return (React hooks rules violation),
causing the settings page to crash. moved all hooks to top level.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- show version (from package.json) in nav bar, warn on frontend/server mismatch
- apply path_mappings to file access check and command string at execution time
so existing scans with old jellyfin paths work without re-scanning
- add clear done/errors button on execute page
- bump version to 2026.03.04
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rewrite from monolithic hono jsx to react 19 spa with tanstack router
+ hono json api backend. add scan, review, execute, nodes, and setup
pages. multi-stage dockerfile (node for vite build, bun for runtime).
previously, server/ and src/shared/lib/ were silently excluded by
global gitignore patterns (/server/ from emacs, lib/ from python).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>