- delete server/services/jellyfin.ts, webhook.ts, mqtt.ts and their tests
- strip jellyfin/mqtt imports and startup calls from index.tsx and settings.ts
- remove /jellyfin, /mqtt, /mqtt/status, /mqtt/test, /jellyfin/webhook-plugin endpoints from settings router
- clean ENV_MAP and isEnvConfigured of jellyfin/mqtt keys
- add db/index.ts migrations for series_key, duration_seconds, scan_status, scan_error, last_scanned_at (new columns absent on older dev DBs)
- move idx_media_items_series_key out of SCHEMA into migrate() so it runs after the column is added
- fix all test fixtures: drop jellyfin_id/series_jellyfin_id column refs, update MediaItem/MediaStream object literals to match current types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove Jellyfin web links, MqttBadge, MqttSection, jellyfinUrl prop chain,
and all jellyfin_id/seriesJellyfinId references from frontend types and
components. Replace series_jellyfin_id with series_key, remove jellyfinUrl
from PipelineData, drop language_display from MediaStream.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
remove resolveSeriesTvdb from LanguageResolverConfig, rename jellyfinFallback
to probeFallback, replace Jellyfin-based TVDB resolution with series_name
title search over Sonarr library, update tests accordingly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move language normalization out of jellyfin.ts into its own module so
non-Jellyfin services (ffmpeg, radarr, sonarr, analyzer) no longer
depend on the Jellyfin service file. jellyfin.ts re-exports
normalizeLanguage for backward compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
every "← Back to inbox" button (review, queue, done, per-item unapprove,
per-item reopen) now does the same thing: reset plan to pending/unsorted,
clear auto_class, delete non-running jobs. previously unapprove left
sorted=1 (sent to review instead of inbox) and each column had its own
SQL. now consistent: back to inbox always means "needs re-processing."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
when the original language track has fewer channels than a kept non-OG
track (e.g. Japanese mono vs English 5.1), classify as auto_heuristic
instead of auto so the user can decide whether to prefer the dub.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
upsertJellyfinItem no longer runs analyzeItem or creates stream_decisions.
it inserts a minimal review_plans stub (pending, unsorted). all analysis
happens in processInbox. this means after scan, ALL items land in the
inbox — the "needs action" count equals the inbox count until processing
classifies them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
scan emits pipeline_changed every 25 items, rescan endpoints emit on
completion. pipeline page listens and throttle-reloads all column data
(1s debounce) so inbox fills progressively without manual navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
the oven/bun:1-slim tag resolves to a new digest frequently, busting
the apt-get ffmpeg layer (5+ minutes). switching to debian:bookworm-slim
keeps that layer stable. bun is copied as a single binary from the
build stage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jellyfin on the left, languages + radarr + sonarr + schedule on the right.
auto-process toggle removed (available from inbox column). danger zone
stays full width at the bottom.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
processing runs in background, items appear in columns progressively via
SSE-triggered reloads. stop button aborts mid-run, remaining items stay in
inbox. remove skip/skip-all from inbox. fix column header height jitter by
giving subtitle slot a fixed height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
the resolver was calling sonarrLang with episode-level TVDB IDs first,
missing the library, triggering an HTTP round-trip per episode. now checks
the library and resolves the correct series TVDB before calling sonarrLang.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Per-item rescan now resets plan to inbox (sorted=0, auto_class=NULL),
clears external_raw, deletes pending jobs, then lets processInbox
handle language resolution + analysis when auto_processing is on
- Add getSeriesEpisodes() to jellyfin service for bulk episode discovery
- Add POST /api/review/rescan-series endpoint accepting seriesJellyfinId
+ optional seasonNumber — discovers new episodes, resets all matching
items to inbox, refreshes streams from Jellyfin, auto-processes if on
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>