Commit Graph

37 Commits

Author SHA1 Message Date
2ada728e50 fix approve-up-to: client sends explicit visible plan id list
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m12s
The server's old /approve-up-to/:id re-ran its own SQL ORDER BY against
ALL pending plans (no LIMIT) to decide which rows fell 'before' the target.
The pipeline UI uses a different ordering — interleaving movies with
series cards, sorting by confidence tier without a name tiebreaker, and
collapsing every episode of a series into one card. Visible position
therefore did not map to the server's iteration position, and clicking
'Approve up to here' could approve far more (or different) items than
the user expected.

- replace POST /approve-up-to/:id with POST /approve-batch { planIds: [...] }
  — server only approves the plans the client lists, idempotent: skips
  ids that are no longer pending, were already approved, or are noop
- ReviewColumn now builds visiblePlanIds in actual render order
  (each movie's id, then every episode id of each series in series order)
  and 'approve up to here' on any card sends slice(0, idx+1) of that list
- works the same for both PipelineCard (movie) and SeriesCard (whole series
  through its last episode)
2026-04-13 10:16:58 +02:00
4a378eb833 pipeline: equal-width columns + per-column clear/stop button
All checks were successful
Build and Push Docker Image / build (push) Successful in 39s
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.
2026-04-13 10:08:42 +02:00
ec28e43484 make pipeline responsive at scale: cap review query, debounce sse reload, indexable done count
All checks were successful
Build and Push Docker Image / build (push) Successful in 37s
The pipeline endpoint returned every pending plan (no LIMIT) while the audio
list capped at 500 — that alone was the main lag. SSE compounded it: every
job_update (which fires per-line of running ffmpeg output) re-ran the entire
endpoint and re-rendered every card.

- review query: LIMIT 500 + a separate COUNT for reviewTotal; column header
  shows 'X of Y' and a footer 'Showing first X of Y. Approve some to see
  the rest' when truncated
- doneCount: split the OR-form into two indexable counts (is_noop + done&!noop),
  added together — uses idx_review_plans_is_noop and idx_review_plans_status
  instead of full scan
- pipeline page: 1s debounce on SSE-triggered reload so a burst of
  job_update events collapses into one refetch
2026-04-13 10:00:08 +02:00
9ee0dd445f remove standalone subtitle extract, unify done semantics, fix nav active matching
All checks were successful
Build and Push Docker Image / build (push) Successful in 49s
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
2026-04-13 09:41:46 +02:00
af410cb616 fix server typecheck: use tsconfig project references, await bun file in spa fallback
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- split tsconfig.json into project references (client + server) so bun-types and DOM types don't leak into the other side; server now resolves Bun.* without diagnostics
- client tsconfig adds vite/client types so import.meta.env typechecks
- index.tsx spa fallback: use async/await + c.html(await …) instead of returning a Promise of a Response, which Hono's Handler type rejects
- subtitles normalize-titles: narrow canonical to string|null (Map.get widened to include undefined)
2026-04-13 07:51:10 +02:00
874f04b7a5 wire scheduler into queue, add retry, dev-reset cleanup, biome 2.4 migrate
- 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
2026-04-13 07:41:19 +02:00
f11861658e add bun:test coverage for analyzer + ffmpeg + validate, emit ffmpeg progress sse
- analyzer.test.ts: audio keep rules (OG + configured langs, unknown OG, undetermined lang, iso alias), ordering (OG first, reorder noop), subtitle forced-remove, transcode targets
- ffmpeg.test.ts: shellQuote, sortKeptStreams canonical order, buildCommand tmp+mv, type-relative maps (0:a:N), disposition, buildPipelineCommand sub extraction + transcode bitrate, predictExtractedFiles dedup
- validate.test.ts: parseId bounds + isOneOf narrowing
- execute: parse ffmpeg Duration + time, emit job_progress SSE events throttled at 500ms so ProcessingColumn progress bar fills in (it already listened)
- package: switch test script from placeholder echo to 'bun test'
2026-04-13 07:35:24 +02:00
93ed0ac33c fix analyzer + api boundary + perf + scheduler hardening
- analyzer: rewrite checkAudioOrderChanged to compare actual output order, unify assignTargetOrder with a shared sortKeptStreams util in ffmpeg builder
- review: recompute is_noop via full audio removed/reordered/transcode/subs check on toggle, preserve custom_title across rescan by matching (type,lang,stream_index,title), batch pipeline transcode-reasons query to avoid N+1
- validate: add lib/validate.ts with parseId + isOneOf helpers; replace bare Number(c.req.param('id')) with 400 on invalid ids across review/subtitles
- scan: atomic CAS on scan_running config to prevent concurrent scans
- subtitles: path-traversal guard — only unlink sidecars within the media item's directory; log-and-orphan DB entries pointing outside
- schedule: include end minute in window (<= vs <)
- db: add indexes on review_plans(status,is_noop), stream_decisions(plan_id), media_items(series_jellyfin_id,series_name,type), media_streams(item_id,type), subtitle_files(item_id), jobs(status,item_id)
2026-04-13 07:31:48 +02:00
cdcb1ff706 drop multi-node ssh execution, unify job runner to local + fix job completion atomicity
- 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
2026-04-13 07:25:19 +02:00
1762f070a9 pipeline UI polish: transcode reasons, scroll fix, series card overflow, rounded corners
All checks were successful
Build and Push Docker Image / build (push) Successful in 51s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 10:35:51 +01:00
9c5a793a47 pipeline UI polish: jellyfin deep-links on titles, hover-to-show approve buttons, series approve-up-to
All checks were successful
Build and Push Docker Image / build (push) Successful in 37s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 01:14:19 +01:00
fd72a6d212 add pipeline API: approve-up-to, series language, pipeline summary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:49:14 +01:00
9cffdaac47 fix reanalyze: pass container to analyzeItem, store new pipeline fields 2026-03-27 01:47:40 +01:00
9a19350f7e add job scheduler: sleep between jobs, schedule window, FFmpeg progress parsing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:46:41 +01:00
97e60dbfc5 add buildPipelineCommand: single FFmpeg command for sub extraction, audio cleanup, transcode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:46:26 +01:00
ecb0732185 store confidence, apple_compat, job_type, transcode_codec during scan 2026-03-27 01:45:56 +01:00
b1cf0fca38 unify analyzer: 3-step pipeline with apple compat, transcode decisions, extended is_noop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:44:22 +01:00
c2e5b70b02 add schema migrations for unified pipeline: confidence, apple_compat, job_type, transcode_codec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:42:18 +01:00
c017ca09d4 add apple compatibility service: codec checks, transcode target mapping 2026-03-27 01:41:21 +01:00
38b0faf55a add job_type column, simplify execute page: remove node/command columns, add type badge, make item title clickable
All checks were successful
Build and Push Docker Image / build (push) Successful in 37s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:01:11 +01:00
2f10037e93 fix subtitle summary 404 by moving /summary route before /:id catch-all, bump version
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m8s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:46:00 +01:00
76d3b1acfb remove path mappings, add subtitle summary endpoint, cache setup page, bump version
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m50s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:02:26 +01:00
99274d3ae8 add execute page filtering + colored FilterTabs component, fix ffmpeg audio-less files
- server-side filter + LIMIT 200 + totalCounts on GET /api/execute
- shared FilterTabs component with status-colored active tabs
- execute page: filter tabs, SSE live count updates, module-level cache
- replace inline tab pills in AudioListPage, SubtitleListPage with FilterTabs
- fix buildExtractOnlyCommand: skip -map 0:a when no audio streams exist
- bump version

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:01:27 +01:00
511a3c1ace remove path mappings from settings UI, fix clear-scan blocking by deleting children first
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m6s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:52:26 +01:00
da668b2d36 add paths page to check volume accessibility after scan
All checks were successful
Build and Push Docker Image / build (push) Successful in 38s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:31:05 +01:00
923f9afafc add collapsible series/season/episode view to subtitle list page
All checks were successful
Build and Push Docker Image / build (push) Successful in 21s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:22:56 +01:00
f562cb42d9 show file name in scan log, fix progress total by using Jellyfin page callback
All checks were successful
Build and Push Docker Image / build (push) Successful in 33s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:19:45 +01:00
a4d5eb59e1 add configurable audio languages, sortable language lists in settings
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m9s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 09:51:03 +01:00
588a3d8f1f remove subtitle streams from container after extraction, remove job list limit, fix audio detail display
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m9s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 09:17:39 +01:00
39a924e6de log ffmpeg command to stdout for docker/unraid log viewer
All checks were successful
Build and Push Docker Image / build (push) Successful in 17s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:47:11 +01:00
41476a2f9b run jobs sequentially per target, parallel across local + nodes
All checks were successful
Build and Push Docker Image / build (push) Successful in 19s
"run all" now groups pending jobs by target (local or node), runs them one by
one within each group, but runs different targets in parallel. single "run"
button still fires immediately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:44:42 +01:00
3682ee98e0 add structured logging with timestamps for docker/unraid log viewer
All checks were successful
Build and Push Docker Image / build (push) Successful in 15s
all server output now prefixed with ISO timestamp and level [INFO/WARN/ERROR].
logs requests, scan start/complete, job lifecycle, errors. skips noisy SSE
endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:29:22 +01:00
818b0d1396 add version badge in nav, apply path mappings at execution time, clear done/error jobs
All checks were successful
Build and Push Docker Image / build (push) Successful in 58s
- 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>
2026-03-04 17:22:14 +01:00
37fae33bbc add clear queue button on execute page
All checks were successful
Build and Push Docker Image / build (push) Successful in 20s
deletes all pending jobs, reverts their review plans back to pending so they
can be re-reviewed and re-approved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:11:18 +01:00
ef785de955 add path mappings to translate jellyfin library paths to container mount paths
All checks were successful
Build and Push Docker Image / build (push) Successful in 20s
jellyfin may use different internal paths (e.g. /tv/) than container mounts
(/series/). path_mappings config (or PATH_MAPPINGS env var) translates at scan
time. configurable via setup ui or env var format: /tv/=/series/,/data/=/movies/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:57:22 +01:00
d5f4afd26b split audio/subtitle concerns, remove docker-in-docker, add per-node path mapping
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m54s
- install ffmpeg in dockerfile (fixes exit code 127)
- buildCommand() now audio-only remux, no subtitle extraction
- add unapprove endpoint + ui button for approved items
- add batch extract-all subtitles endpoint + ui button
- audio detail page shows only video+audio streams
- remove global movies_path/series_path config, add per-node path mapping
- remove docker-in-docker command building (buildDockerCommand, buildDockerExtractOnlyCommand)
- ssh execution translates /movies/ and /series/ to node-specific paths
- remove media paths section from setup page
- add unraid-template.xml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:48:00 +01:00
5ac44b7551 restructure to react spa + hono api, fix missing server/ and lib/
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>
2026-03-02 22:57:40 +01:00