Commit Graph

106 Commits

Author SHA1 Message Date
felixfoertsch cbae475281 harmonize pipeline buttons: headers get icon+text, cards get icon-only on directional side
Build and Push Docker Image / build (push) Successful in 1m13s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 12:10:43 +02:00
felixfoertsch b738f6878d make auto-process inbox a continuous polling loop instead of one-shot
Build and Push Docker Image / build (push) Successful in 59s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:38:58 +02:00
felixfoertsch 39fcac10b5 reanalyze stale noops on processInbox so title fixes actually get queued
Build and Push Docker Image / build (push) Successful in 1m30s
Items previously marked is_noop=1 by the old analyzer (before the title
check) have reasons=NULL. processInbox now reanalyzes these once — if the
title check flips them to non-noop, they enter the pipeline and get their
titles fixed by ffmpeg. One-time catch-up: subsequent runs skip them
because reasons is populated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:10:11 +02:00
felixfoertsch 50e1ea66f4 show processing reasons as pills on pipeline cards
Build and Push Docker Image / build (push) Successful in 1m1s
Analyzer now computes structured reason tags (Remove tracks, Reorder,
Extract subs, Transcode, Fix default, Fix language tag, Fix title) and
stores them as JSON in review_plans.reasons. Pipeline cards show these
as badges next to the copy/transcode pill so users know why a file
needs processing. Replaces the old transcode_reasons computed from
stream_decisions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:01:54 +02:00
felixfoertsch 0a8996dc33 always require correct harmonized audio title, including when title is null
Build and Push Docker Image / build (push) Successful in 1m57s
Every kept audio track must have our canonical title format (e.g.
"ENG - AAC · 5.1"). Files with missing or wrong titles are not noop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:47:13 +02:00
felixfoertsch 56be720494 detect wrong audio titles as not-noop, stop processing on auto-process uncheck
Build and Push Docker Image / build (push) Successful in 1m30s
- analyzer: files with non-harmonized audio titles (e.g. "Chinese - Dolby
  Digital - 5.1" instead of "ZHO - EAC3 · 5.1") are no longer marked as
  desired-state noop. Only fires when a title already exists — null titles
  are left alone to avoid processing every file just to add titles.
- export trackTitle from ffmpeg.ts so analyzer can compute expected title
- settings: unchecking auto-processing now calls stopProcessInbox() to
  abort the running inbox processor
- 3 new analyzer tests for title mismatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:44:50 +02:00
felixfoertsch f31e84e186 rescan file on reopen/unapprove so returning to inbox refreshes stream metadata
Build and Push Docker Image / build (push) Successful in 1m43s
Single-item "← Back to inbox" (reopen, unapprove) now re-probes the file
via ffprobe and re-upserts media_items + media_streams. Covers cases where
the original scan errored or the file was replaced on disk. Bulk operations
(unsort-all, reopen-all) skip the rescan to avoid hammering ffprobe.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:33:53 +02:00
felixfoertsch 78d569189f fix stop buttons: centralize processInbox launch through abort-aware startProcessInbox
Build and Push Docker Image / build (push) Successful in 1m18s
All three processInbox callers (manual button, auto-processing toggle,
post-scan auto-process) now go through startProcessInbox() which manages
the shared abort controller. Previously only the manual button set the
abort controller, so Stop Sorting had no effect when processing was
triggered from the settings toggle or after scan completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:18:57 +02:00
felixfoertsch 6721b8caf3 reset stale running jobs on startup, prevent stuck processing after container restart
Build and Push Docker Image / build (push) Successful in 2m9s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:07:25 +02:00
felixfoertsch 7900f450a7 sort dropdown on every column, auto-process checkbox stays visible during sorting
Build and Push Docker Image / build (push) Successful in 1m10s
- ColumnShell: new sort row below header border with sortOptions/sortValue/onSortChange
- inbox: ↓↑ scan time, ↓↑ name (dropdown moved from button row to sort row)
- review: classification (default), ↓↑ scan time, ↓↑ name
- queue/done: ↓↑ added, ↓↑ name (client-side sort on already-fetched arrays)
- auto-process checkbox stays visible during inbox processing, progress shows below it
- backend: unified GroupSort type replaces InboxSort, review bucket accepts sort param

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:36:20 +02:00
felixfoertsch 789a9f7bfe fix inbox sort during scan, move dropdown to button row, per-item Process button
Build and Push Docker Image / build (push) Successful in 48s
- sort state lifted to PipelinePage so loadGroups includes the sort param
  on every reload (scan SSE events no longer reset the sort)
- sort dropdown moved from subtitle to ColumnShell middle slot (left of
  Process Inbox button)
- ColumnShell.skip renamed to middle, accepts ReactNode or ColumnAction
- per-item "Process →" button on inbox movie cards and series cards:
  POST /:id/process resolves language + reanalyzes + sorts a single item
- dashboard stat pills refresh during scan (every 25 items)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:18:52 +02:00
felixfoertsch 6325bdb3e9 resumable scan: skip already-scanned files, clear stale scan_running on startup
Build and Push Docker Image / build (push) Successful in 57s
On resume, the scan filters out files already marked 'scanned' in the DB
so only unscanned files are probed. Also clears the scan_running flag on
DB init so a container killed mid-scan doesn't permanently block new scans.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:06:48 +02:00
felixfoertsch 7fa2404d13 remove backfill that moved unclassified inbox items to review on restart
Build and Push Docker Image / build (push) Has been cancelled
The one-shot backfill for the inbox rollout ran on every startup,
setting sorted=1 on rows with sorted=0 AND auto_class IS NULL —
which is exactly the state of freshly scanned items. After the
drop-jellyfin migration wipes all tables, the backfill is obsolete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:05:18 +02:00
felixfoertsch 9d65dd12be pipeline ux: actionable errors, inbox sorting, danger stop buttons, overlapping checkmarks
Build and Push Docker Image / build (push) Successful in 2m10s
- clickable error count in header shows file names + error messages
- inbox sort dropdown (scan time / name, asc / desc)
- inbox movies no longer minimal (show available badges)
- stop buttons use solid danger style, descriptive labels (Stop Scan, Stop Job, Stop Sorting)
- double checkmarks overlap like WhatsApp read receipts
- processInbox logs start/completion to stdout for Docker visibility
- fix byTitle in language-resolver test, bump to 2026.04.21.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 08:58:08 +02:00
felixfoertsch 6f4d265385 bump version to 2026.04.21
Build and Push Docker Image / build (push) Successful in 1m32s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 06:42:22 +02:00
felixfoertsch 4fe651f822 add sonarr title fallback for episode language resolution, bump version
Build and Push Docker Image / build (push) Successful in 2m34s
2026-04-20 21:12:14 +02:00
felixfoertsch c11ba795e6 compact icon buttons on cards: ↻ rescan, ✓ approve, ✓✓ approve all, ↑ above
Build and Push Docker Image / build (push) Successful in 1m17s
all with title hover text for discoverability. saves horizontal space
so buttons never overflow their card boundaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:45:55 +02:00
felixfoertsch e0a43f1a54 approve-above button uses absolute positioning, no overflow
Build and Push Docker Image / build (push) Successful in 1m33s
the button floats top-left on hover without displacing the action row
buttons (rescan, approve series) which stay in fixed positions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:43:03 +02:00
felixfoertsch 089cfe8640 fix series card action row overflow: allow button wrapping
Build and Push Docker Image / build (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:41:46 +02:00
felixfoertsch abdfa2a790 move inbox stop button to left side (backward slot)
Build and Push Docker Image / build (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:41:13 +02:00
felixfoertsch 65acc683b9 stop button aborts queue loop, remaining jobs stay pending in queue
Build and Push Docker Image / build (push) Successful in 6m48s
previously stop only killed the running ffmpeg process — the loop
immediately picked up the next pending job. now the abort signal
breaks the loop between jobs, so remaining items stay in the queue
column as pending jobs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 15:46:31 +02:00
felixfoertsch c985bb9f04 move auto-process queue checkbox from Processing to Queue column
Build and Push Docker Image / build (push) Successful in 1m24s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 14:25:15 +02:00
felixfoertsch df08065aa4 processing bar full width, stop button shows just "Stop"
Build and Push Docker Image / build (push) Successful in 1m35s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 14:20:59 +02:00
felixfoertsch 1e749e0188 OG quality check compares against all non-OG streams in file, not just kept
Build and Push Docker Image / build (push) Successful in 3m55s
the Dead Zone case: OG is Japanese mono, English 5.1 exists but is removed
by config (audio_languages=[]). the previous check only looked at kept
streams, so it never fired. now compares OG channels against all non-OG
audio in the file — if a superior dub exists, flag for review regardless
of whether it's currently configured to be kept.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 14:00:10 +02:00
felixfoertsch e6d3c179fe bump version to 2026.04.20.9
Build and Push Docker Image / build (push) Successful in 1m19s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 12:08:30 +02:00
felixfoertsch f00360b128 bump version to 2026.04.20.8
Build and Push Docker Image / build (push) Successful in 3m45s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:47:05 +02:00
felixfoertsch 2701441a1c background processing with stop, simplify pipeline header stats, remove inbox skip
Build and Push Docker Image / build (push) Successful in 1m40s
- processInbox runs in background, returns immediately, items appear in
  columns progressively via SSE-triggered reloads
- stop button aborts mid-run, processed items stay in their destination
- fix language resolver: resolve series TVDB before sonarrLang HTTP fallback
- remove skip/skip-all from inbox (not meaningful in process-based flow)
- pipeline header shows only: total, needs action, queued, errors
- fix column header height jitter with fixed subtitle slot height

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:06:44 +02:00
felixfoertsch c251d22638 pipeline is root page, full viewport width, remove pipeline nav link
Build and Push Docker Image / build (push) Successful in 2m1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 09:23:55 +02:00
felixfoertsch 648a17b763 rename sortInbox to processInbox, integrate language resolution into process step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 09:04:18 +02:00
felixfoertsch 94610d05b7 strip Sonarr/Radarr lookups from scan path, make upsertJellyfinItem Jellyfin-only
RescanConfig now only carries audioLanguages. Radarr/Sonarr library
loading, language resolution, and resolveSeriesTvdb callback removed
from rescan.ts, scan.ts, and webhook.ts. RescanResult no longer tracks
radarrHit/sonarrHit/missingProviderId counters. Tests updated: removed
authoritative-source and resolved-TVDB-enables-Sonarr tests (moving to
processInbox in a later task), added assertion that scan never sets
sonarr/radarr as language source.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 08:54:43 +02:00
felixfoertsch a1da644aa3 fix sonarr OG-language miss when Jellyfin omits SeriesProviderIds
Build and Push Docker Image / build (push) Successful in 3m49s
episodes without SeriesProviderIds.Tvdb (older Jellyfin, un-refreshed items)
fell back to the episode-level TVDB ID, which never matched Sonarr's
series-keyed library. add resolveSeriesTvdb callback that fetches the series
item from Jellyfin to get the correct series TVDB ID, with per-scan caching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 07:39:24 +02:00
felixfoertsch c1baf3e476 audio detail: delete-file + delete-and-refetch via Radarr/Sonarr
Build and Push Docker Image / build (push) Successful in 3m24s
escape hatch for items the audio pipeline can't usefully fix — e.g. a
release whose only audio track is English commentary and needs to be
purged so *arr can find a better one. two buttons on the audio detail
page:

  🗑 Delete file            — unlinks the file and drops our DB rows
                              (cascades streams, plans, decisions, jobs)
  🗑 Delete & refetch       — same, then asks Radarr/Sonarr to rescan
                              (to notice the deleted file) and trigger
                              an indexer search for a replacement

backend: POST /api/review/:id/delete { refetch }. the refetch step is
best-effort and its result ships under `refetch` on the response so the
UI can surface partial wins — file deleted + db clean even if Radarr
doesn't have the movie. helpers live on the existing *arr service
modules (triggerMovieRefetch, triggerEpisodeRefetch) and do the command
lookups + POST /api/v3/command calls themselves.

UI uses native confirm dialogs showing the file path. on success,
navigates back to /pipeline since the detail page points at a row that
no longer exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 00:21:46 +02:00
felixfoertsch ee9add076a collapse Library page into a compact Pipeline header
Build and Push Docker Image / build (push) Successful in 1m17s
the separate Library page was mostly duplicating what the pipeline
columns + the item detail page already show. move its two useful bits —
the stats row (total / scanned / needs action / no change / approved /
done / errors) and the scan control bar — into a compact two-row header
above the pipeline columns, drop the library items table entirely, and
redirect "/" to "/pipeline".

the scan SSE buffering logic moves verbatim into PipelineHeader.tsx so
the progress bar and the stats refresh on completion keep working. the
dead /api/scan/items endpoint and its parseScanItemsQuery +
buildScanItemsWhere helpers (plus their tests) go away with the UI;
/api/scan, /api/scan/start, /api/scan/stop, /api/scan/events stay.

nav loses "Library" — Pipeline is the only entry point now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 00:16:21 +02:00
felixfoertsch 8112bfeb65 per-track language override on audio detail page
Build and Push Docker Image / build (push) Successful in 3m3s
adds stream_decisions.custom_language (ISO 639-2 code or null) so the
user can correct a mislabeled audio track — e.g. a Spanish dub tagged
"und" in the container — without going through Jellyfin. the override
wins over stream.language everywhere it matters: the analyzer reads it
for keep/remove decisions and track ordering, the ffmpeg command builder
writes it as both the language metadata tag and the harmonized track
title, and reanalyze preserves it across reruns and rescans.

on the audio detail page, each pending audio row swaps its language
cell for an inline <select> populated from LANG_NAMES. picking the raw
file language clears the override; anything else sets it and triggers a
server-side reanalyze so keep/remove + target_index update immediately.
a small ✎ hint marks overridden tracks. rebuilt commands tag the output
accordingly so Jellyfin reads the corrected language.

PATCH /api/review/:id/stream/:streamId/language validates the code
against LANG_NAMES (accepts ISO 639-1/2/2B aliases, rejects garbage)
and runs reanalyze inside.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 00:05:31 +02:00
felixfoertsch fada511ecc pipeline: optimistic auto-process checkbox state
Build and Push Docker Image / build (push) Successful in 2m42s
both auto-process toggles (Inbox, Queue) used a fully-controlled checkbox
whose checked prop was driven by data.autoProcessing / autoProcessQueue.
that made clicks feel frozen on anything slower than localhost: react
reconciles the DOM back to the pre-click value between onChange firing
and setData landing after loadAll, so a click could look like it snapped
back before the server answered. uncheck in particular showed up as
"can't turn it off" when the queue was idle.

mirror the prop in a local useState + sync via useEffect so the box
flips on click and the server value reconciles on the next pipeline
refresh. matches the pattern SettingsPage already uses for the same
toggle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 23:47:05 +02:00
felixfoertsch 05a1345750 rip subtitle manager → sibling project, keep extraction only
Build and Push Docker Image / build (push) Successful in 2m31s
subtitle management (list/detail pages, /api/subtitles, subtitle_files
table, SubtitleFile types, predictExtractedFiles, nav link) moved to a
new sibling project at ~/Developer/netfelix-subtitles-manager/ where
it'll be rebuilt standalone later. this project now owns audio fixing
+ subtitle extraction only.

extraction still runs end to end: analyzeItem still marks every subtitle
stream as "remove from container", buildExtractionOutputs still wires
the -map 0:s:N + sidecar outputs into the ffmpeg command, and execute.ts
still flips review_plans.subs_extracted so verify.ts can check desired
state — just derived from the streams directly instead of by writing a
row per file to the now-gone subtitle_files table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:50:52 +02:00
felixfoertsch 0fd3624d9f pipeline: uniform column headers, auto-process queue toggle, reopen → inbox
Build and Push Docker Image / build (push) Successful in 4m3s
column headers are now a fixed three-row layout (title / subtitle / button
row). every column always reserves all three rows so headers line up
regardless of contents; actions render disabled when their column is
empty instead of disappearing, which keeps the header height stable as
state changes.

the processing column gets a new "Auto-process Queue" checkbox that
mirrors the inbox's "Auto-process Inbox" toggle. backend adds an
auto_process_queue config, a maybeStartQueueProcessor() helper, a
POST /api/settings/auto-process-queue endpoint, and a hook in
enqueueAudioJob so approvals drain the queue hands-off when the toggle
is on.

reopen-all and per-item reopen now send items to the Inbox (sorted=0)
instead of back to Review. the done column's label and tooltip become
"← Back to inbox" to match, and the clear button moves to the right
slot so the header pattern (left=backward, right=forward) stays
consistent across columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:57:13 +02:00
felixfoertsch a21bcefb54 stream auto review progress over SSE so large inboxes don't feel frozen
Build and Push Docker Image / build (push) Successful in 1m18s
sortInbox is now async, yields every 10 items, and emits inbox_sort_start
+ inbox_sort_progress via optional hooks. the pipeline route handler
wires those hooks to the existing job events stream and guards against
a second concurrent sort with a 409.

the inbox column swaps its Auto Review button for a live "Sorting N/T"
counter and progress bar while the sort is in flight; the auto-process
toggle hides to give the progress the full subtitle line. the previous
behaviour was a frozen button for the entire duration of the sort.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:56:51 +02:00
felixfoertsch 76a16ba84c reanalyze plans during auto review so config changes take effect
Build and Push Docker Image / build (push) Successful in 4m0s
sortInbox used to distribute plans by the auto_class and stream_decisions
captured at scan time, which meant toggling an audio_languages entry and
then running "back to inbox" + "auto review" re-queued the item with the
stale decisions. now sortInbox re-runs the analyzer per plan against the
current audio_languages before distributing, matching the user's mental
model that auto review = re-apply the rules.

reanalyze() takes audioLanguages explicitly so callers can pass it in
once and tests can drive it without reaching into the singleton db.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:38:14 +02:00
felixfoertsch 91d8ed67b8 fix authoritative language sources getting demoted to needs_review
Build and Push Docker Image / build (push) Successful in 2m22s
rescan flagged every item where radarr/sonarr disagreed with jellyfin's
audio-track guess as needs_review=1, but the analyzer's authoritativeOg
check demands needs_review=0 — so the very items we had an authoritative
answer for were the ones dumped into the "Needs decision" bucket. Lost
(german dubs first on most files, sonarr authoritatively english) was
the perfect worst case: nearly every episode misclassified as manual.

trust the authoritative source unconditionally and reset needs_review=0
when it fires, mismatch or not.
2026-04-19 15:20:18 +02:00
felixfoertsch 794680ec17 pipeline: align ↑ approve above next to approve (right side), not skip
Build and Push Docker Image / build (push) Successful in 4m11s
2026-04-19 13:32:58 +02:00
felixfoertsch 201c02c810 pipeline: stop header buttons from wrapping
Build and Push Docker Image / build (push) Successful in 2m11s
use grid-cols-[auto_1fr_auto] so backward/forward take natural width
and the flexible middle column centers skip; add whitespace-nowrap;
bump column min-width 64→80 so all three fit on the skinniest column.
2026-04-19 13:27:52 +02:00
felixfoertsch 495a40a6c6 restructure pipeline column headers with backward/skip/forward slots, rename ready → auto-approve
Build and Push Docker Image / build (push) Successful in 1m52s
- columnshell: three-row header (title, subtitle, backward · skip · forward grid)
- inbox: auto-processing checkbox moves into subtitle, page top-right toggle removed
- review: add ← back to inbox, rename approve-all-ready to approve auto
- queue: clear button relabeled ← back to inbox (matches existing behaviour)
- done: add ← back to review (reopen-all), clear moves to middle skip slot
- seriescard: action row mirrors movie card, now at the top
-  ready badge →  auto-approve with tooltip, review subtitle uses "auto · need decisions"
- new endpoints: /api/review/unsort-all, /api/review/reopen-all (+ tests)
2026-04-19 13:16:29 +02:00
felixfoertsch 84e669922b clear queue → inbox, move pipeline card actions to the top
Build and Push Docker Image / build (push) Successful in 3m54s
- execute/clear now also resets sorted=0 so cleared items land back in the
  inbox where the distributor can re-classify them; previously they got
  stranded in Review with auto_class='auto', unreachable by both
  "Approve all ready" and "Auto Review"
- pipelinecard: action row moves to the top so Skip/Approve/Back-to-review
  sit in the same place regardless of card body height; title row follows;
  file info (copy/transcode reasons) gets a dedicated row with the
  ready/needs-decision badge pushed to the right
- tests: clearQueue preserves running/completed jobs, only pending plans
  flip back to sorted=0 pending

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:34:49 +02:00
felixfoertsch 0d560743f3 fix manual language overrides wiped on rescan, use series tvdb for sonarr, split seriescard controls
Build and Push Docker Image / build (push) Successful in 3m31s
- rescan: skip jellyfin/radarr/sonarr lookups when orig_lang_source='manual' so user pins survive webhook + full scans
- jellyfin: request SeriesProviderIds so episodes can resolve to the series-level tvdb id
- sonarr: drop the lookup[0] fallback that silently returned unrelated shows on tvdb misses
- seriescard: split badges+language and approve buttons onto separate rows; seasongroup header wraps with ml-auto so buttons don't overflow the narrow pipeline column
- tests: cover manual override preservation and episode → series tvdb resolution

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 07:41:08 +02:00
felixfoertsch 331cc7b58e v2026.04.18.1 — inbox column, auto-processing toggle, classifier-driven sort
Build and Push Docker Image / build (push) Successful in 1m16s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 10:53:06 +02:00
felixfoertsch 0c595a787e library: batch audio-codec lookup — per-row subquery was O(page×streams)
Build and Push Docker Image / build (push) Successful in 1m11s
The scalar subquery I added in 7d30e6c ran one aggregate scan of
media_streams per row. On a real library (33k items / 212k streams)
a single page took 500+ seconds synchronously, blocking the event
loop and timing out every other request — Library AND Pipeline both
stopped loading.

Swap it for a single batched `GROUP_CONCAT ... WHERE item_id IN (?...)`
query over the current page's ids (max 25), then merge back into rows.

v2026.04.15.10

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:42:23 +02:00
felixfoertsch 7d30e6c1a6 library: rename Scan nav/page to Library, show audio codecs per row
Build and Push Docker Image / build (push) Successful in 1m4s
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>
2026-04-15 19:10:00 +02:00
felixfoertsch a2bdecd298 rework scan page, add ingest-source browsing, bump version to 2026.04.15.8
Build and Push Docker Image / build (push) Successful in 4m56s
2026-04-15 18:33:08 +02:00
felixfoertsch c6698db51a improve running-job responsiveness, bump version to 2026.04.15.7
Build and Push Docker Image / build (push) Successful in 1m41s
2026-04-15 16:58:53 +02:00