across review, processing, and done columns the movie/episode name is
now a link to /review/audio/\$id — matches the usual web pattern and
removes an extra click through the now-redundant Details button on
pipeline cards. jellyfin's deep link moves to a small ↗ affordance
next to the title so the 'open in jellyfin' path is still one click
away without hijacking the primary click.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
one-click for the common case: anything whose language came from
radarr/sonarr (confidence='high') is trusted enough to skip manual
review. low-confidence items stay pending.
- POST /api/review/auto-approve filters on rp.confidence='high' and
enqueues audio jobs through the same dedup-guarded helper
- ColumnShell now takes actions[] instead of a single action, so the
Review header can show Auto Review + Skip all side by side
Two issues with the old bar:
1. progress state was never cleared between jobs — when a job finished,
its 100% bar lingered on the next job's card until that job emitted
its first progress event. Clear progress on any job_update where
status != 'running', and on the column side ignore progress unless
progress.id matches the current job.id.
2. labels were misleading: the left/right times were ffmpeg's *input*
timestamp position (how far into the source it had read), not wall-
clock elapsed/remaining. For -c copy jobs ripping a 90-min file in
5 wall-seconds, the user saw '0:45 / 90:00' jump straight to
'90:00 / 90:00' which looks broken.
New display: 'elapsed M:SS N% ~M:SS left'. Elapsed is wall-clock
since the job started (re-renders every second), percent comes from
ffmpeg input progress as before, ETA is derived from elapsed × (100-p)/p
once we have at least 1% to avoid wild guesses.
Extract a ColumnShell component so all four columns share the same flex-1
basis-0 width (no more 24/16/18/16 rem mix) and the same header layout
(title + count + optional action button on the right).
Per-column actions:
- Review: 'Skip all' → POST /api/review/skip-all (new endpoint, sets all
pending non-noop plans to skipped in one update)
- Queued: 'Clear' → POST /api/execute/clear (existing; cancels pending jobs)
- Processing: 'Stop' → POST /api/execute/stop (new; SIGTERMs the running
ffmpeg via a tracked Bun.spawn handle, runJob's catch path
marks the job error and cleans up)
- Done: 'Clear' → POST /api/execute/clear-completed (existing)
All destructive actions confirm before firing.
- 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