Skip to content

Latest commit

 

History

History
808 lines (613 loc) · 72.9 KB

File metadata and controls

808 lines (613 loc) · 72.9 KB

Changelog

All notable changes to Sublarr are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.36.4-beta] - 2026-03-30

Fixed

  • Health status — Ollama no longer critical — removed Ollama connectivity from the overall health flag; Ollama is an optional translation backend and its unavailability only affects translation, not core subtitle management; the status bar now correctly shows Online when Sublarr itself is reachable

[0.36.3-beta] - 2026-03-29

Fixed

  • Preview Player — Firefox subtitle crash (definitive fix) — replaced createTrack("/sub.ass") with createTrackMem(content, length) in the libass-wasm worker's onRuntimeInitialized; bypasses ass_read_file() (which returns NULL in Firefox even with valid WASM FS content) by passing the placeholder ASS directly in memory via ass_new_track + ass_process_data; real subtitle continues to load post-init via setTrackByUrl()

[0.36.2-beta] - 2026-03-29

Fixed

  • Preview Player — Firefox subtitle crash — fixed ass_read_file returning NULL in the libass-wasm worker (Firefox); the worker's onRuntimeInitialized always calls createTrack("/sub.ass") — now initialised with a valid placeholder ASS so the init-time call succeeds; real subtitle is loaded post-init via setTrackByUrl() through the worker's message buffer; also fixes CSP wasm-unsafe-eval and fallback font (default.woff2 via fonts-liberation in Docker)

[0.36.1-beta] - 2026-03-29

Fixed

  • Preview Player — subtitle rendering — subtitles now render correctly in the preview player; fixed canvas overlay positioning (libass canvasParent inserted inside relative wrapper), worker auth (subContent instead of unauthenticated subUrl), and CJS constructor interop for libass-wasm
  • Preview Player — subtitle toggle latency — eliminated 10–20 s reappearance delay when toggling subtitles off/on; worker is now kept alive across track changes and reuses setTrack()/freeTrack() instead of a full WASM worker restart

[0.36.0-beta] - 2026-03-29

Added

  • Scoring — video_codec weight — x264/x265/AV1 codec match adds +2 points to episode and movie scores (Bazarr parity)
  • Language Profiles — mustContain / mustNotContain — AND-logic filter: only accept subtitles matching ALL mustContain terms; any mustNotContain term rejects (Bazarr parity); new DB columns on language_profiles
  • Language Profiles — cutoff — stop searching for a language once a subtitle is already present on disk
  • Language Profiles — audioExclude — skip downloading a subtitle if the audio track is already in the target language
  • Provider Infrastructure — CircuitBreaker persistence — CB OPEN state written to ProviderStats.disabled_until; survives application restarts; is_open property added
  • Provider Infrastructure — rate-limit throttle — configurable extended throttle on ProviderRateLimitError via provider_rate_limit_throttle_minutes
  • Download Quality — upgrade chain trackingupgraded_from_id foreign key on subtitle_downloads records which subtitle was replaced; enables full upgrade audit trail
  • Download Quality — post-download commandpost_download_command config executes an arbitrary shell command after each successful download; supports {subtitle_path}, {language}, {provider}, {score} variable substitution
  • Sync — manual alass endpointPOST /api/v1/sync/alass triggers alass subtitle synchronisation on demand

Added

  • Standalone Mode — Auto-activationis_standalone_mode() helper auto-activates standalone mode when no *arr is configured; StandaloneStatus extended with arr_configured and auto_activated fields
  • Connections — Standalone scan button — manual scan button added to the Standalone section in Connection Settings

Changed

  • Settings — Connections — removed central API Keys section; API keys are now managed inline within each connection's own settings panel
  • Translation — Beta marking — Translation card on Settings overview now shows "BETA" pill; Translation Settings page shows a warning banner

Fixed

  • Language Profiles — mustContain AND logic — corrected to require ALL terms instead of ANY term (Bazarr parity fix)
  • Post-download hook — guard added via getattr(self, 'settings', None) to prevent crash when settings are not available
  • OpenSubtitles — Anime season-1 collapse — fallback search now maps S02+ episodes to Season 1 with the original episode number (not absolute episode); moviehash stripped from fallback params to allow title-based lookup
  • UI — WebSocket events — corrected event names (upgrade_complete, wanted_scan_complete); added wanted_item_searched handler
  • UI — Wanted page — per-row independent loading state (shared isPending was spinning all rows simultaneously)
  • UI — Episode Search Panel — null-safety guards on target_results and source_results

[0.35.0-beta] - 2026-03-22

Added

  • Movie Detail — Subtitle Management — wanted items section below file info shows missing subtitles per language; inline Search / Skip / Re-enable buttons; wired to /wanted?movie_id= filter
  • Backend — /wanted movie filter — new ?movie_id= query param filters wanted items by standalone_movie_id; enables movie detail subtitle management without loading the full wanted list

Changed

  • Series Detail — Episode Grid — restored full feature set: per-row checkboxes, SubBadge per subtitle language (teal = ASS optimal, purple = SRT upgradeable, orange = missing), audio-track badges, sidecar subtitle actions (delete, download, NFO export, subtitle menu, health badge, preview, edit), batch toolbar (Search / Extract / Translate / Cleanup), Skip / Accept inline actions wired to useUpdateWantedStatus
  • Dashboard — AutomationBanner — subtitle line now shows live "Last completed: X ago" derived from scannerStatus.last_scan_at; replaces hardcoded placeholder text
  • Library — fixed anime_only=False filter that was hiding non-anime content; all library entries now visible regardless of type

Fixed

  • Settings — API Keys — removed duplicate TMDB and TVDB entries; fixed updateApiKey request body format that was causing 400 errors on save
  • Security — CSP / Permissions-PolicyContent-Security-Policy and Permissions-Policy response headers added to all responses (F-23)
  • Security — Webhook SSRFvalidate_service_url() applied to webhook create and update endpoints; blocks dangerous URL schemes (F-21)
  • Security — Auth warning — startup SECURITY WARNING log emitted when both API key and UI auth are disabled, alerting operators to the open-API exposure (F-17/F-18 root cause)

[0.33.0-beta] - 2026-03-20

Added

  • Providers — Subf2m — new subtitle provider supporting 60+ languages via Subf2m.co
  • Providers — Subsource — new subtitle provider (multi-language, movie & TV)
  • Providers — YIFY Subtitles — movie-only provider using IMDB-based JSON API
  • Providers — Zimuku — Chinese subtitle provider (simplified & traditional)
  • Providers — BetaSeries — French subtitle provider for TV series
  • Providers — Titlovi — Balkan subtitle provider (Croatian, Serbian, Bosnian, Slovenian, Macedonian)
  • Providers — EmbeddedSubtitles — integrates embedded subtitle tracks from media files directly into the search and scoring pipeline
  • Subtitle Processing Pipeline — post-download processing hook; 18 fix functions (HI removal, common formatting corrections, OCR artifact cleanup); configurable per-series via series detail panel
  • Settings — Processing Pipeline — new settings section for configuring post-processing behavior (fix modules, interjection list)
  • Series Detail — Batch Process — button to run post-processing on all existing subtitles for a series; progress log modal

Changed

  • Settings — Fansub / Release Groups — global release-group preference fields moved from Wanted tab to Scoring tab where they belong conceptually
  • Series Detail — Fansub Preferences — replaced the always-visible card with a compact toolbar button; active overrides highlighted in accent color; per-series settings in a modal dialog

Fixed

  • Security — SSRF — URL validation in PUT /api/v1/config now covers dot-notation extension keys (e.g. whisper.subgen.url) that previously bypassed the _URL_FIELDS check
  • Security — SocketIO log sanitizationSocketIOLogHandler now strips DB-internal error details (table names, column names, query fragments) before emitting to WebSocket clients
  • Backend — startup crashvalidate_service_url was imported in routes/config.py but never implemented; added full SSRF-safe implementation

[0.32.0-beta] - 2026-03-19

Changed

  • Settings — Navigation — Restructured from 7 groups / 23 tabs to 5 logical groups (Connections, Languages & Subtitles, Providers, Automation, System); no tabs removed
  • Providers — Priority — Replaced move-up/down buttons in edit modal with drag & drop handles on provider tiles

Added

  • Score Breakdown — Hover tooltip on score badges in search results shows per-component point breakdown (series title, season, episode, format bonus, provider modifier, etc.)
  • Wanted — Failure Details — Failed items now show inline error reason, attempt count, and next retry countdown
  • Wanted — Batch Progress — Progress bar with found/failed counters during "Search All" operation
  • Dashboard — Automation Widget — New widget showing automation status (enabled/disabled), today's found/failed subtitle stats, last/next run times, and Run Now button
  • Onboarding — Language Step — New wizard step to configure target and source language during first-time setup
  • Onboarding — Automation Step — New wizard step to configure automatic search interval and subtitle upgrade behavior

[0.31.0-beta] — 2026-03-19

Changed

  • Backend — Test Foundation — added 29 new tests covering WantedSearchService, ProviderManager, and quality-validation logic; total suite now 736 tests at 47.76% coverage
  • Backend — Type Safety + Lint — resolved all ruff errors and mypy type warnings across the entire backend; no new ignores added
  • Backend — File Splits — 8 oversized files (800–2921 lines) decomposed into focused packages: routes/hooks/, routes/library/, routes/wanted/, routes/translate/, routes/system/, routes/tools/; service packages translator/ and wanted_search/; shared batch state extracted to routes/batch_state.py
  • Backend — Architectureproviders/registry.py with PROVIDER_METADATA dict replaces three class-level dicts; nested Settings views (GeneralSettings, TranslationSettings, ProviderSettings, MediaServerSettings, ScanningSettings) with read-only delegation; singleton lifecycle via get_scanner()/get_provider_manager() checking app.extensions
  • Frontend — SyncControls splitSyncControls.tsx decomposed into OffsetTab, SpeedTab, FramerateTab, ChapterTab, StandardActions, SyncTabBar; orchestrator retains all state and handlers
  • Frontend — useApi splituseApi.ts decomposed into six domain files: useLibraryApi, useWantedApi, useTranslationApi, useProvidersApi, useIntegrationApi, useSystemApi; barrel re-exports all public hooks
  • Frontend — Error BoundariesErrorBoundary component wraps Library, Wanted, and Settings routes; runtime errors are caught per-route instead of crashing the full app

Fixed

  • Backend — monkeypatch targets — updated test_wanted_search_reliability.py patch paths to point to the submodule where each function is called after the Phase 3 package split
  • Frontend — verbatimModuleSyntax — added import type to all interface-only imports in VideoPlayer.tsx, PlayerModal.tsx, SubtitleTrackSelector.tsx to satisfy verbatimModuleSyntax: true in tsconfig
  • Frontend — TypeScript strict errors — fixed all errors from tsc --project tsconfig.app.json: toast call signature (toast.success/errortoast(msg, type)), 'warning' toast type (→ 'error'), missing RefreshCw import, handleDeleteSidecar return type, duplicate style JSX attribute, Recharts Formatter type mismatch, duplicate subscene provider key, implicit any in Logs filter callback, useSeriesDetail nullable parameter, missing libass-wasm type declaration

[0.30.0-beta] — 2026-03-16

Added

  • Standalone — NFO metadata integration — standalone scanner reads .nfo sidecar files to resolve series/movie title, year, TVDB/TMDB ID, and episode metadata without requiring an API lookup; falls back to filename parsing when no NFO is present
  • Standalone — Skip extra files — trailers, featurettes, samples and other non-episode extras are now excluded from subtitle discovery during standalone filesystem scan; follows Jellyfin/Kodi naming convention (-trailer, -featurette, -behindthescenes, -deleted, -interview, -scene, -short, -sample, -theme); configurable via standalone_skip_extras toggle in Settings → Library Sources (advanced)

Fixed

  • Standalone — symlinks and SQLAlchemy text() compatibilityos.walk(followlinks=True) now follows symlinked directories; raw SQL wrapped in sqlalchemy.text() to fix deprecation warnings
  • Standalone — app context — scanner operations that write to DB now correctly run inside Flask app context to avoid RuntimeError: No application context
  • Standalone — library view — standalone series/movies now appear in Library with correct poster URLs and breadcrumb navigation
  • Standalone — series detail fallback — SeriesDetail page gracefully handles episodes without a Sonarr instance; subtitle sidecar endpoint falls back to standalone path resolution
  • Standalone — poster endpoint — path security enforced via is_safe_path(); URL generation updated to use /api/v1/ prefix consistently
  • Standalone — NFO/poster lookup in Season subfolder — scanner now finds poster.jpg and .nfo files inside Season XX/ subdirectories, not only in the series root
  • Settings — nav redirect — Setup page correctly redirects to /settings after initial configuration; NavLink isActive prop removed (invalid in React Router v6)
  • Wanted — scroll list layout — replaced hardcoded calc(100vh - 300px) with flex-1 / min-h-0 chain; list now fills the full remaining viewport at any window size

Changed

  • Dependencies — jsdom 28 → 29; 13 npm minor/patch updates

[0.29.0-beta] — 2026-03-14

Added

  • Web Player — Streaming endpointGET /api/v1/media/stream?path= serves video files with HTTP 206 range-request support; is_safe_path() enforced; Content-Type resolved by extension; SUBLARR_STREAMING_ENABLED setting (default true) allows disabling the endpoint
  • Web Player — PlayerModal — portal-based HTML5 <video> player with play/pause/seek/volume/fullscreen; opens via "Preview" button on episode cards in SeriesDetail
  • Web Player — ASS/SRT subtitle overlay — SubtitleOctopus (libass WASM) renders styled ASS subtitles natively in-browser; subtitles-octopus-worker.js and .wasm served from /public/
  • Web Player — Subtitle track selector — dropdown to switch between all available sidecar subtitle files for the episode; "Off" option disables overlay
  • Web Player — Seek-to-cue — clicking a cue row in SubtitleEditorModal jumps the player to that timestamp via onSeekRequest bridge
  • Web Player — Settings togglestreaming_enabled toggle in Settings → Automation (advanced section)

[0.28.0-beta] — 2026-03-14

Added

  • AI Glossary Builder — DB schema — adds term_type (character/place/other), confidence (float 0–1), approved (boolean) columns to glossary_entries; Alembic migration f1a2b3c4d5e6
  • AI Glossary Builder — Extractor serviceglossary_extractor.py performs frequency analysis over subtitle sidecar files to surface recurring proper-noun candidates without requiring an LLM
  • AI Glossary Builder — Suggest endpointPOST /api/v1/series/<id>/glossary/suggest triggers auto-detection and returns ranked candidates for human review
  • AI Glossary Builder — TSV exportGET /api/v1/glossary/export downloads all approved glossary terms as a tab-separated file for external use
  • AI Glossary Builder — CRUD extended — existing POST/PUT /api/v1/glossary endpoints accept the new term_type, confidence, and approved fields
  • AI Glossary Builder — ConfigSUBLARR_GLOSSARY_ENABLED (default true) and glossary_max_terms per-series cap (default 100) in Settings → Translation (advanced section)
  • AI Glossary Builder — LLM injection — approved terms injected as <glossary> system prompt prefix during translation; capped at 50 terms; V8-compatible term → translation comma format retained; single-line fast-path added (Translate to German: {line}) when subtitle contains exactly one cue
  • AI Glossary Builder — GlossaryPanel UI — Suggest button (Wand2 icon) triggers candidate detection; candidate list with approve/pre-fill/reject actions; TermTypeBadge (character/place/other); Export TSV button; all wired via new suggestGlossaryTerms and exportGlossaryTsv hooks

[0.27.0-beta] — 2026-03-14

Added

  • NFO Export — Auto sidecarauto_nfo_export config flag (off by default) writes an XML .nfo file alongside every downloaded or translated subtitle; contains provider, source/target language, score, translation backend, BLEU score, timestamp, and Sublarr version
  • NFO Export — API routesPOST /api/v1/subtitles/export-nfo?path=<path> for single-subtitle export; POST /api/v1/series/<id>/subtitles/export-nfo for bulk export of all subtitles in a series; per-file is_safe_path() validation enforced on all paths
  • NFO Export — Settings toggleauto_nfo_export toggle in Settings → Automation (advanced section); expert feature, hidden behind "Show advanced"
  • NFO Export — SeriesDetail buttonFileCode button on each subtitle sidecar badge in SeriesDetail triggers single-file NFO export with toast feedback

[0.26.0-beta] — 2026-03-14

Added

  • Single-Account Login — First-run setup wizard — on first visit, /setup presents two choices: set a password or leave the UI open; no forced registration
  • Single-Account Login — Flask session authbefore_request hook enforces session-or-X-Api-Key on all /api/ routes when enabled; session secret auto-generated and persisted in config_entries; bcrypt password hashing
  • Single-Account Login — Auth APIGET /api/v1/auth/status, POST /auth/setup (first-run), POST /auth/login, POST /auth/logout, POST /auth/change-password, POST /auth/toggle; API key auth (X-Api-Key) remains independent
  • Single-Account Login — React routingAuthGuard component redirects to /setup or /login as needed; auth pages render full-screen without Sidebar
  • Settings → Security tab — toggle UI auth on/off; change-password form (shown only when auth enabled)
  • Sidebar — Logout button — shown when auth.enabled && auth.authenticated; navigates to /login on success

[0.25.3-beta] — 2026-03-14

Added

  • List Virtualization — Library table view — replaced client-side pagination (25/page) with @tanstack/react-virtual virtual scroll using the padding-row technique; <table>/<tr> DOM structure preserved; sticky header; scroll resets on filter/sort; grid view retains pagination; VirtualLibraryTable + LibraryShared components extracted to frontend/src/components/library/
  • List Virtualization — Wanted list — Wanted now fetches all matching items in a single request (up to 9 999) and renders with virtual scroll; useWantedVirtualizer hook in frontend/src/components/wanted/VirtualWantedTable.tsx; removes multi-page navigation

[0.25.2-beta] — 2026-03-13

Added

  • Subtitle Diff Viewer — Per-cue accept/rejectPOST /tools/diff computes a cue-level diff using pysubs2 + difflib.SequenceMatcher; returns structured diff entries (unchanged/modified/added/removed) with timing in seconds. POST /tools/diff/apply recomputes the diff server-side, merges accepted/rejected changes into the modified SSAFile (preserving header and styles), creates a .bak backup, and writes atomically via os.replace. Frontend SubtitleDiff.tsx rewritten from CodeMirror merge view to a filterable per-cue table; users can accept or reject each change individually or via Accept All / Reject All; applying navigates back to preview and invalidates the subtitle-content cache.

[0.25.1-beta] — 2026-03-13

Added

  • CLI — sublarr search — search subtitle providers for all wanted items in a series via --series-id <id>; calls GET /wanted + POST /wanted/batch-search
  • CLI — sublarr translate — translate a subtitle file via POST /translate/sync; supports --force flag; prints output path (sync) or job ID (queued)
  • CLI — sublarr sync — sync subtitle timing to a video file via POST /tools/auto-sync; --engine ffsubsync|alass
  • CLI — sublarr status — show active translation jobs and background task state; --running to filter in-progress jobs only
  • CLI — Entry pointbackend/sublarr_cli.py; configure via SUBLARR_URL and SUBLARR_API_KEY env vars or --url/--api-key flags

[0.25.0-beta] — 2026-03-13

Added

  • Jellyfin — Play-start webhook — Sublarr now triggers the subtitle search+translate pipeline automatically when Jellyfin starts playing an episode; receives PlaybackStart events from the Jellyfin Webhook Plugin; resolves item path via configured Jellyfin/Emby media server instances
  • Settings → Automation — Jellyfin play-translate — new toggle enables automatic translation on Jellyfin playback start (SUBLARR_JELLYFIN_PLAY_TRANSLATE_ENABLED, default off)

[0.24.4-beta] — 2026-03-13

Added

  • Chapter Detection — ffprobe-based chapter list — Sublarr reads chapter metadata from video files; results cached per-file (mtime-invalidated) to avoid repeated ffprobe calls; path validated via is_safe_path()
  • Advanced Sync — Chapter Range — offset operations can now be scoped to a chapter window; only subtitle events within the selected chapter are shifted; preview mode samples only in-range events
  • SyncControls — Chapter Tab — new "Chapter" tab visible when chapters are detected; chapter dropdown (title + timestamps), ±offset presets, preview, and two-step confirm-apply flow

[0.24.3-beta] — 2026-03-13

Added

  • Fansub Preferences — per-series preferred and excluded groups — configure preferred and excluded fansub groups per series; preferred groups receive a configurable score bonus, excluded groups are effectively filtered out; accessible from Series Detail
  • SeriesFansubPrefsPanel — new panel in SeriesDetail with comma-separated preferred/excluded group inputs, bonus score field, and Save/Reset buttons

[0.24.2-beta] — 2026-03-13

Added

  • SeriesSettings — per-series Whisper audio track — pin a preferred audio track index for Whisper transcription per series; clearing the setting (set to null) resumes automatic track selection
  • SeriesAudioTrackPicker — new component in SeriesDetail; lazy-loads available audio tracks via ffprobe; dropdown sets the per-series Whisper transcription preference

[0.24.1-beta] — 2026-03-12

Added

  • OP/ED Detector — detects Opening and Ending cue regions in subtitle files using ASS style name matching and position/duration heuristics; read-only detection returns {type, start_ms, end_ms, event_count, method} without modifying the file; configurable detection window via SUBLARR_OP_WINDOW_SEC (default 300 s)

Changed

  • SubtitleEditorModal — Quality Tools — added Detect OP/ED button after Remove Credits button

[0.24.0-beta] — 2026-03-12

Added

  • Credit Remover — credit_remover.py — detects and removes credits-only subtitle lines from ASS/SSA/SRT files using 4 independent heuristics: role markers ((Translator), (QC), etc.), credit prefix patterns (Credits:, Staff:, etc.), duration heuristic (events near end of file), and isolated capitalized names (John Smith); dry_run mode for preview without modification
  • POST /api/v1/tools/remove-credits — new endpoint to strip detected credits; dry_run=true returns preview of lines that would be removed (capped at 50); dry_run=false creates .bak backup then writes cleaned file; returns original_lines, cleaned_lines, removed, backed_up
  • Config — credit_threshold_sec — new setting (SUBLARR_CREDIT_THRESHOLD_SEC, default 90s) controls how many seconds from the end of a file are considered the credits region

Changed

  • SubtitleEditorModal — Quality Tools — added Remove Credits button alongside existing Remove HI button

[0.23.0-beta] — 2026-03-12

Added

  • Batch Translate — POST /wanted/batch-translate — re-translate multiple subtitle files in one request; accepts item_ids array; returns per-item success/failure map
  • Batch Search ExtendedPOST /wanted/batch-search now accepts series_ids array for multi-series search in a single call
  • Library — Series Checkboxes — multi-select series in Library view with floating batch toolbar (Search All Missing)
  • SeriesDetail — Episode Checkboxes — multi-select episodes with floating batch toolbar (Search / Extract)
  • Filter Presets — save, load, and delete named filter configurations on Library, Wanted, and History pages; persisted in filter_presets DB table via GET|POST|DELETE /api/v1/filter-presets
  • Global Search (Ctrl+K) — fuzzy search across series, episodes, and subtitles; keyboard-accessible command palette
  • Auto-Extract on Scanscan_auto_extract + scan_auto_translate settings; scanner automatically extracts embedded subs on first detection

[0.22.0-beta] — 2026-03-11

Added

  • Marketplace — GitHub Plugin Discovery — new Settings → Providers → Marketplace tab; discovers community plugins via topic:sublarr-provider GitHub topic search; caches results in marketplace_cache DB table with 1-hour TTL
  • Marketplace — Official/Community Badges — plugins from official-registry.json receive a verified "Official" badge; community plugins show a neutral "Community" label; is_official flag persisted in DB
  • Marketplace — SHA256 Integrity Verificationinstall_plugin_from_zip() verifies SHA256 hash before extraction; SHA256 is required (empty string rejected with HTTP 400); prevents install of corrupted or tampered plugins
  • Marketplace — Capability WarningsCapabilityWarningModal warns users before installing non-official plugins that declare filesystem or subprocess capabilities; confirmation required before proceeding
  • Marketplace — Installed Plugins DBinstalled_plugins table tracks name, version, capabilities, SHA256, plugin dir, and install timestamp; persists across restarts
  • Marketplace — Hot-ReloadPOST /marketplace/install hot-reloads the plugin manager after successful installation via manager.reload() + invalidate_manager()
  • Marketplace — RefreshPOST /marketplace/refresh force-fetches latest plugin list from GitHub, bypassing the 1-hour cache TTL
  • Marketplace — Update Detection — UI compares installed version against registry version; highlights available updates with a yellow badge
  • Config — github_token — new optional SUBLARR_GITHUB_TOKEN setting; used for authenticated GitHub API requests to avoid rate limiting
  • DB Migration a2b3c4d5e6f7 — adds marketplace_cache and installed_plugins tables via Alembic

Security

  • SSRF Preventionzip_url validated to be HTTPS-only before download (urlparse scheme check)
  • Path Traversalis_safe_path() applied to all install/uninstall plugin directory operations
  • XSS Preventiongithub_url validated with startsWith('https://') before rendering as <a href>

[0.21.1-beta] — 2026-03-11

Added

  • Accessibility — Toast aria-liveToastContainer now has role="status", aria-live="polite", and aria-atomic="true"; screen readers announce toast messages without interrupting focus
  • Accessibility — Skip-to-Main Link — visually-hidden skip link added as first focusable element in the render tree; activating it moves focus to #main-content; visible on keyboard focus
  • Accessibility — Modal role="dialog" — all 7 modals (SubtitleEditorModal, WidgetSettingsModal, GlobalSearchModal, SubtitleCleanupModal, SyncModal, AddProviderModal, ProviderEditModal) now have role="dialog", aria-modal="true", aria-labelledby pointing to the modal title, and autoFocus on the close button
  • Accessibility — Semantic Tables — all <th> elements in Library, History, Blacklist, and Wanted tables have scope="col"; Library sort headers update aria-sort dynamically (ascending / descending / none)
  • Accessibility — Form LabelsAddProviderModal and ProviderEditModal inputs have aria-label or <label htmlFor> associations; SettingRow renders a semantic <label> when htmlFor is provided
  • Accessibility — Client-Side ValidationAddProviderModal and ProviderEditModal validate required fields on blur and on submit attempt; inline <p role="alert"> error messages with aria-invalid / aria-describedby on inputs
  • StatusBadge — Lucide Icons — each status now renders a lucide icon alongside the color dot for colorblind accessibility (CheckCircle2, XCircle, Clock, Loader2, AlertCircle, Search, MinusCircle); Loader2 animates with animate-spin; color dot removed
  • Page-Specific SkeletonsLibrarySkeleton, TableSkeleton, ListSkeleton, FormSkeleton added to PageSkeleton.tsx; Library, History, Queue, Blacklist, Wanted, and Settings Suspense boundaries use their matching skeleton instead of the generic one
  • prefers-reduced-motion — CSS media query added to index.css; overrides all animation/transition durations to 0.01ms for users who opt out of motion

Changed

  • Library Grid — Tablet Breakpoint — added md:grid-cols-5 between sm:grid-cols-4 and lg:grid-cols-6; smooths the column jump at 768px viewport
  • Stagger Animation — 300ms Cap — Library grid cards and Wanted list rows apply animationDelay: Math.min(i * 30, 300)ms; late items on large lists no longer appear broken
  • CSS Hover — Remove JS StateRecentActivityWidget and ProviderTile replaced useState/onMouseEnter/onMouseLeave background-color handlers with a .hover-surface:hover CSS utility class; eliminates unnecessary re-renders

[0.20.0-beta] — 2026-03-10

Added

  • PostgreSQL — First-Class Support — full migration guide, PG-compatible Alembic migrations, dialect-aware health endpoints (GET /database/health), VACUUM guard (returns 501 on PostgreSQL); docker-compose.postgres.yml for batteries-included PG stack; docs/POSTGRESQL.md covers fresh install, SQLite→PG migration via pgloader, pool tuning, backup/restore
  • Incremental Metadata Cache — ffprobe results cached persistently in DB with mtime-based invalidation; GET /api/v1/cache/ffprobe/stats and POST /api/v1/cache/ffprobe/cleanup endpoints; batch wanted-scanner probes now use cache (use_cache=True); eliminates redundant ffprobe calls on unchanged files
  • Background Wanted Scanner — Batch Commits — scanner now batches all DB writes per series/movie into a single commit (instead of one commit per episode); thread-local _batch_mode flag ensures batch mode in the scanner thread never blocks concurrent API request commits; SUBLARR_SCAN_YIELD_MS setting (default: 0) adds optional CPU yield between series to reduce contention
  • Parallel Translation Workers — Configurable CountSUBLARR_TRANSLATION_MAX_WORKERS setting (default: 4) controls the thread pool size of the in-memory job queue; /translate async endpoint now routes through the shared job queue (same as /translate/sync) so concurrency is always bounded and observable via GET /api/v1/jobs
  • Redis Job Queuebackend/worker.py RQ worker entry point with AppContextWorker subclass — each job runs inside a Flask app context; docker-compose.redis.yml stack with Redis 7 + Sublarr + rq-worker; scale workers with --scale rq-worker=N; graceful fallback to MemoryJobQueue when Redis is unreachable

[0.19.2-beta] — 2026-03-10

Fixed

  • Remux Engine — mkvmerge wrong track ID_remux_mkvmerge was referencing an undefined stream_index variable (NameError) and the call site was passing subtitle_track_index (0-based subtitle-only index, e.g. 0) instead of the global ffprobe stream index (e.g. 2); mkvmerge's --subtitle-tracks !N flag uses global Track IDs matching ffprobe's stream_index — passing !0 targeted the video track and left the subtitle untouched; now _remux_mkvmerge receives and uses the correct global stream_index; validated with mkvmerge v92.0 inside Docker

[0.19.1-beta] — 2026-03-10

Fixed

  • Dockerfile — mkvtoolnix missing — added mkvtoolnix to the Docker image apt-get install step; without it mkvmerge was unavailable inside the container and all MKV stream removal jobs failed with "mkvmerge not found"

[0.19.0-beta] — 2026-03-10

Added

  • Stream Removal — Safe Remux Engine — remove embedded subtitle streams from video containers without re-encoding; mkvmerge used for MKV/MK3D, ffmpeg for all other containers (MP4, AVI, etc.); backend auto-detected by file extension; ffprobe verification after remux validates duration (±2s), video/audio stream counts, subtitle count (exactly -1), and file size (≥50% of original)
  • Trash-Folder Backups — Configurable Retention — original video moved to centralized <media_root>/<remux_trash_dir>/trash/<YYYY-MM-DD>/<file>.<ts>.bak before each remux (TinyMediaManager-style); absolute trash path supported; falls back to sibling .bak on permission error; CoW reflink attempted first on Btrfs/XFS for near-instant copies; remux_trash_dir (default .sublarr) and remux_backup_retention_days (default 7) configurable in Settings → Automation
  • Async Remux JobsPOST /api/v1/library/episodes/<ep_id>/tracks/<index>/remove-from-container starts a background job; GET /api/v1/remux/jobs and GET /api/v1/remux/jobs/<job_id> expose status; real-time updates via Socket.IO remux_job_update events; optional Sonarr/Radarr folder-monitoring pause during remux
  • Backup Management APIGET /api/v1/remux/backups lists all .bak files in trash directories; POST /api/v1/remux/backups/cleanup deletes backups older than retention period (supports dry_run mode)
  • Undo / RestorePOST /api/v1/remux/backups/restore atomically restores backup to original video path via os.replace(); both paths validated with is_safe_path() to prevent path traversal; "Rückgängig" button appears in TrackPanel after successful stream removal and restores in one click

[0.18.0-beta] — 2026-03-10

Added

  • HI Support — Hearing Impaired Preference — new hi_preference setting (include / prefer / exclude / only); provider results scored accordingly: prefer adds +30, exclude / only apply ±999 penalty; hi_removal_enabled toggle for future HI-tag stripping
  • Forced Subtitle Support — Forced Preference — new forced_preference setting (include / prefer / exclude / only) with same ±30/±999 scoring logic; bonuses stack when both HI and forced preferences match
  • TRaSH Scoring Presets — Importable Community Profilesbackend/scoring_presets/ package with three bundled presets (anime, tv, movies); GET /api/v1/scoring/presets, GET /api/v1/scoring/presets/<name>, POST /api/v1/scoring/presets/import endpoints; Settings → Events & Hooks → Scoring tab shows preset selector and custom JSON import; import validates schema and calls invalidate_scoring_cache()
  • Anti-Captcha Integration — Provider 403 Bypass — new CaptchaSolver class supporting Anti-Captcha.com and CapMonster via identical createTask / getTaskResult REST API; anti_captcha_provider + anti_captcha_api_key settings; Kitsunekko calls _try_solve_captcha_and_retry() on HTTP 403 — submits reCAPTCHA v2 token and retries; falls back gracefully if no solver configured; Anti-Captcha section added to Providers tab in Settings

[0.17.0-beta] — 2026-03-10

Added

  • Duplicate Detection — SHA-256 download dedup — skips provider downloads when SHA-256 hash matches an existing subtitle in the same directory; stale hash entries are auto-cleaned on startup; toggleable via SUBLARR_DEDUP_ON_DOWNLOAD; hash registered on every successful file write
  • Smart Episode Matching — multi-episode + OVA/Special — multi-episode filenames (S01E01E02) parsed to full episode list; OVA/Special/SP detection via guessit + filename regex; release_group, source, resolution, absolute_episode propagated to VideoQuery for all providers
  • Video Hash Pre-Computefile_hash computed once in build_query_from_wanted() and reused across all providers; eliminates redundant file reads when multiple providers are queried in parallel
  • Release Group Filtering — include/exclude subtitle results by release group, codec, or source tag; score bonus for preferred groups; release metadata auto-extracted from filename via guessit; configurable at Settings → Wanted
  • Provider Result Re-ranking — auto-adjusts per-provider score modifiers from download history; formula: success rate + avg score vs. global average + consecutive failure penalty; throttled hourly; preview endpoint and manual trigger available
  • Subtitle Upgrade Scheduler — periodic re-check for higher-quality subtitles; eligibility: score < 500 OR non-ASS format; configurable upgrade_scan_interval_hours at Settings → Automatisierung; manual trigger via /tasks/upgrade-scan/trigger
  • Translation Quality Dashboard — daily quality trend chart (avg score + issue count) and per-series quality table (sortable, color-coded bars) added to Statistics page
  • Custom Post-Processing Scripts — subtitle_downloaded eventsubtitle_downloaded event now emitted from save_subtitle(); shell hooks at Settings → Events & Hooks receive SUBLARR_SUBTITLE_PATH, SUBLARR_PROVIDER_NAME, SUBLARR_SCORE, SUBLARR_LANGUAGE, and SUBLARR_SERIES_TITLE environment variables

[0.15.2-beta] — 2026-03-03

Added

  • Activity — Parsed media titles — file column now shows parsed series/episode name and episode number instead of raw filename; full path still accessible in the expanded row; parseMediaTitle() utility added to lib/utils.ts
  • History — Blacklist confirmation dialog — ban icon on history entries now opens a confirmation modal showing provider and title instead of blacklisting immediately; optional "Also delete subtitle file" checkbox deletes the sidecar file and invalidates the history cache in one atomic flow
  • SeriesDetail — Delete confirmation dialog — deleting a subtitle sidecar now opens a confirmation modal with an "Also add to blacklist?" checkbox; when checked, the provider record is looked up from subtitle_downloads and added to the blacklist before the file is moved to trash
  • Activity — Expanded row layout — expanded detail row redesigned with cleaner label/value grid, stats section, and better visual hierarchy

Fixed

  • Wanted — wanted_auto_translate=False not respectedprocess_wanted_item() always started a translation job regardless of the wanted_auto_translate setting; now the flag is checked and translation is skipped when disabled
  • Backend — DELETE /library/subtitles — accepts optional blacklist: bool body parameter; when true, looks up the provider record in subtitle_downloads (LIKE-match on video base path + language) and calls add_blacklist_entry() before trashing the sidecar

[0.15.1-beta] — 2026-03-01

Fixed

  • App — SPA 404 on page reloadstatic_url_path="" caused Flask's built-in static file route to intercept /wanted, /library etc. and return 404 before the serve_spa() catch-all; fixed by setting static_folder=None so only the custom handler runs
  • App — PostgreSQL startup warningsrowid in wanted_items dedup query replaced with id (primary key); MIN(title) aggregate added to search index rebuild query to satisfy PostgreSQL GROUP BY rules; _patch_pre_alembic_columns() detects and adds the source column to subtitle_downloads for databases created before Alembic was introduced
  • Scoring — _DEFAULT_EPISODE_WEIGHTS import — re-exported from db.scoring so routes/hooks.py can import them without reaching into the repository layer

[0.15.0-beta] — 2026-03-01

Added

  • Sidebar — Update available badge — a pulsing badge appears in the sidebar when a newer GitHub release is available; the version is fetched from the GitHub Releases API once on load and cached; clicking opens the release page directly

Fixed

  • Wanted — Search and download — provider search and download were broken due to missing Flask app context in background threads and stale cache; fixed by passing the app instance explicitly and resetting the provider cache on each call

[0.14.2-beta] — 2026-03-01

Added

  • Wanted — Extracted status — extracting an embedded subtitle no longer removes the item from Wanted; instead it stays visible with a new teal Extracted badge so the user can see what was extracted and trigger translation or cleanup as a follow-up step
  • Wanted — Sidecar Cleanup — new POST /api/v1/wanted/cleanup endpoint and matching UI button (with confirmation dialog) that deletes non-target-language .ass/.srt sidecar files next to media files of extracted items; supports dry_run mode and optional item_ids filter; path-traversal protected via is_safe_path()
  • Wanted — Extracted filter tab — new filter tab in the status row allows filtering the Wanted list to show only items with status extracted

Changed

  • Wanted — Extract behaviorPUT /wanted/<id>/status now accepts extracted as a valid status value in addition to wanted, ignored, failed

[0.14.1-beta] — 2026-03-01

Added

  • Library — Grid/Thumbnail view — toggle button (table ↔ grid) next to series/movies tabs; grid renders poster images from Sonarr/Radarr with missing-count badge; preference persisted to localStorage; fallback film-slate SVG when no poster available
  • Library — Status and profile filters — dropdown to filter items by status (all / has missing / complete) and by profile name; filtering applied client-side via useMemo with no additional API calls
  • Wanted — Error and retry display — failed wanted items now show the failure reason as a truncated ⚠ message tooltip in the status column; upcoming retry time shown as Retry: Xm/Xh below the badge when retry_after is set
  • Settings — Search field — text input at the top of the settings sidebar filters tabs by name in real-time; Migration tab is excluded from search results regardless of the Advanced toggle
  • SeriesDetail — EpisodeActionMenu — replaces 8 unlabelled icon-only action buttons with two primary labelled buttons (Search, Edit) and a ⋯ More dropdown grouped by category (Preview/Compare, Timing, Analyse, History); extracted into standalone EpisodeActionMenu component

Fixed

  • Sidebar — Version display — version fallback changed from the hardcoded v0.1.0 to v… while the health endpoint is loading; version now always reflects backend/VERSION correctly
  • i18n — SeriesDetail action buttons — all 12 episode action button tooltips (Preview, Edit, Compare, Sync Timing, Auto-Sync, Video Sync, Health Check, Embedded Tracks, Search, Interactive Search, History, Back) were hardcoded English; replaced with t('library:episode_actions.*') keys available in both DE and EN
  • i18n — Wanted page — "Scan Embedded" button label, "Scanning…" state text, and "Upgrades Only (N)" filter badge were hardcoded; replaced with t('library:wanted.*') keys
  • i18n — FilterBar / FilterPresetMenu — "Add filter", "Clear all", "Presets", "No saved presets", "Preset name…", "Save current filters" were hardcoded English; now use t('common:filters.*') keys
  • Settings — Migration tab visibility — Migration tab was always visible in the System group; now only rendered when the Advanced toggle is active and the settings search field is empty

Changed

  • Statistics — empty state message — placeholder text updated to mention subtitle searches in addition to translations so users understand both workflows populate the chart
  • Statistics — download trackingrecord_subtitle_download() in db/providers.py now also writes to the daily_stats table via record_stat(); provider downloads were previously invisible on the Statistics page (only translation jobs were tracked)

[0.14.0-beta] — 2026-03-01

Added

  • Provider UI — Deaktivieren vs. Entfernen — Power button grays out a provider tile in-grid (50% opacity, "Deaktiviert" badge) while Trash button removes it to the + pool entirely; new providers_hidden config key separates "off but visible" from "removed from grid"
  • Provider — Subscene — 55-language community subtitle database, no account required; HTML scraping with BeautifulSoup4, rate limit 10/60 s
  • Provider — Addic7ed — 36 languages, TV-series specialist with episode-exact matching; optional login credentials increase daily download limit; BeautifulSoup4, rate limit 10/60 s
  • Provider — TVSubtitles — 35 languages, TV-series only, no auth; BeautifulSoup4, rate limit 15/60 s
  • Provider — Turkcealtyazi — Turkish subtitle community site, login required; BeautifulSoup4, rate limit 10/60 s
  • Language expansion_LANGUAGE_TAGS expanded from 25 to ~70 ISO 639-1 codes; SUPPORTED_LANGUAGES constant with 63 ordered entries served via GET /api/v1/languages (cached 1 h)
  • LanguageSelect component — searchable dropdown for source/target language settings that updates both the language code and _name fields simultaneously

Changed

  • Settings — source/target language — fields now use the new LanguageSelect dropdown instead of plain text inputs
  • Provider reactive health checks — status is fetched on-demand only (no background polling); ProviderManager.update_providers() does selective enable/disable without full reinit; providers_hidden key excluded from provider reinit trigger
  • Provider UI grid — complete tile-grid redesign: ProviderTile shows status badge, success rate, language count, and credential type; AddProviderModal replaces flat list with searchable cards; ProviderEditModal uses structured config_fields; header shows N aktiv / M konfiguriert counts; + tile only visible when hidden providers exist
  • CIactions/checkout, actions/setup-node, actions/setup-python bumped to v6

[0.13.2-beta] — 2026-02-28

Security

  • Path traversal hardeningis_safe_path() from security_utils now enforced on all 8 remaining routes that accepted user-supplied file paths: tools.py, video.py, whisper.py, spell.py, integrations.py, webhooks.py, translate.py (4 endpoints + batch directory), subtitles.py; inline ad-hoc os.path.abspath().startswith() checks replaced throughout (CRITICAL)
  • WebSocket authentication — Socket.IO connect handler now rejects connections with an invalid or missing API key when SUBLARR_API_KEY is set; frontend WebSocketContext passes the key via socket auth dict (HIGH)
  • Secret masking in API responsesget_safe_config() extended to deep-mask JSON blob fields (sonarr_instances_json, radarr_instances_json, media_servers_json) — credential sub-keys (api_key, password, token, secret, pin) replaced with "***"; notification_urls_json always masked; routes/config.py blocklist extended with 8 additional sensitive keys (HIGH)
  • Request size limitMAX_CONTENT_LENGTH = 16 MB added to Flask app factory to prevent DoS via oversized request bodies (HIGH)
  • Hook script path restrictioncreate_hook and update_hook now validate script_path against /config/hooks/ using is_safe_path(); arbitrary filesystem execution blocked (HIGH)
  • SQL injection in Bazarr migrator — table names read from the Bazarr SQLite file validated with ^[a-zA-Z_][a-zA-Z0-9_]*$ regex before interpolation into queries; invalid names skipped with a warning (HIGH)
  • XZ decompression bomb protectionAnimeTosho._decompress_xz() now enforces a 10 MB limit on decompressed output; payloads exceeding the limit raise ValueError (MEDIUM)
  • Container hardening — port binding changed from 0.0.0.0 to 127.0.0.1; read_only: true + tmpfs: [/tmp] added to docker-compose.yml (MEDIUM)

Changed

  • Dev/prod requirements split — test and lint tools (pytest, ruff, mypy, bandit, locust, etc.) moved from requirements.txt to new requirements-dev.txt; production image no longer installs dev dependencies
  • CI — backend job now installs requirements-dev.txt alongside requirements.txt so lint and test tools are available

[0.13.1-beta] — 2026-02-28

Added

  • Sidecar discovery APIsGET /api/v1/library/series/<id>/subtitles scans all episode files in parallel (ThreadPoolExecutor) and returns sidecar metadata keyed by Sonarr episode ID; GET /api/v1/library/episodes/<id>/subtitles for single-episode scan; response includes path, language, format, size, and mtime for each sidecar file
  • Sidecar delete APIDELETE /api/v1/library/subtitles moves one or more sidecar files to a .sublarr_trash/ folder (manifest.json per entry) instead of permanently deleting; only files inside SUBLARR_MEDIA_PATH are accepted — path-traversal attempts return 403
  • Trash management APIsGET /api/v1/library/trash lists recoverable files; POST /api/v1/library/trash/<id>/restore moves the file back; DELETE /api/v1/library/trash/<id> permanently removes it; auto-purge of entries older than subtitle_trash_retention_days (default: 7 days) runs on every delete call
  • Batch delete APIPOST /api/v1/library/series/<id>/subtitles/batch-delete removes sidecars across all episodes of a series filtered by language and/or format; all deletions go through the trash system
  • Inline sidecar badges — SeriesDetail episode rows now show a badge for every sidecar file found on disk (language + format label); non-target-language sidecars are displayed in a dimmed style with a × delete button; clicking × soft-deletes the file and immediately refreshes the row
  • Subtitle Cleanup Modal — series-level "Clean up" button opens a modal grouped by language showing file count and total size per language; "Keep target languages only" quick action pre-selects all non-target languages for deletion; preview shows file count and MB to be moved to trash before confirming
  • Live extraction progressbatch-extract-tracks emits a batch_extract_progress WebSocket event after each episode; SeriesDetail shows a progress banner (file name + X / N episodes) with a progress bar and animated spinner while extraction is running; Extract button is disabled during the operation
  • Activity page visibilitybatch-extract-tracks now creates a DB job record (runningcompleted/failed) so every extraction run appears on the Activity page with succeeded, failed, and skipped episode counts; the job is visible within one poll cycle (~3 s) of starting
  • Always-visible series toolbar — new action row pinned to the SeriesDetail hero header containing three buttons: "Extract Tracks" (triggers batch-extract-tracks for the whole series, shows live X/N counter), "Clean up" (opens Subtitle Cleanup Modal), and "Search N missing" (moved here from the language row); all three actions are available without selecting individual episodes
  • Auto-cleanup settings — three new config fields: auto_cleanup_after_extract (boolean toggle), auto_cleanup_keep_languages (comma-separated ISO 639-1 codes, e.g. de,en), auto_cleanup_keep_formats (ass / srt / any); when enabled, sidecars not matching the keep rules are moved to trash automatically at the end of each batch-extract-tracks run
  • Settings UI — three new fields added to the Automation tab; subtitle_trash_retention_days field also added to control automatic trash purge interval
  • Wanted Batch Search carduseWantedBatchStatus() was previously wired but never rendered; now shown as an amber card with a progress bar and found/failed/skipped item counts while a batch search is running
  • Batch Probe card — live progress card appears while batch-probe is running; shows total tracks scanned, found, extracted, and failed counts plus the currently processed file path; teal accent with animated Layers icon
  • Wanted Scanner card — new GET /api/v1/wanted/scanner/status endpoint exposes the full live state of the background wanted scanner (is_scanning, is_searching, phase label, current/total progress, added/updated counters); rendered as a green card with an optional phase badge and progress bar; adaptive polling — 3 s while active, 30 s idle
  • The Queue page now shows all four background operations simultaneously: Batch Translation, Wanted Batch Search, Batch Probe, and Wanted Scanner — each with a distinct colour accent and its own progress indicator

Changed

  • Subtitle badge semantics — three visual states: teal = ASS/embedded-ASS (optimal), violet = SRT/upgradeable, orange = missing; non-target-language sidecar files shown in a separate dimmed group with × delete button
  • Language code normalisationnormLang() maps ISO 639-2 three-letter codes (ger, eng, jpn, fre, …) to ISO 639-1 two-letter codes (de, en, ja, fr, …) so MKV track tags and sidecar filenames no longer generate duplicate badges for the same language
  • SeriesDetail subtitle column — changed from a fixed w-40 (160 px) width to flex-1 min-w-[200px] so badge rows expand to fill available space and avoid excessive wrapping on wide screens
  • Sidecar query live refresh['series-subtitles'] TanStack Query polls every 4 s while extraction is running; on completion both ['series-subtitles'] and ['series'] are invalidated so episode rows update without a manual reload
  • Queue page polling — job list refetch interval reduced from 15 s to 3 s so short-lived translation jobs are reliably visible while the Queue page is open

Fixed

  • Batch-extract series_id 400batch_extract read page.get("items", []) but get_wanted_items() returns {"data": [...]}, causing every series-level extraction triggered from SeriesDetail to return 400 "item_ids or series_id required"; fixed to page.get("data", [])
  • Batch-probe deadlock — a database error inside get_wanted_items() during a probe run left probe.running = True permanently until process restart; the call is now wrapped in try/except so the flag is always cleared on failure
  • wanted_item_searched event dropped — the wanted_item_searched signal was emitted in routes/wanted.py but never registered in events/catalog.py, causing the event to be silently discarded by the unknown-name guard in emit_event(); catalog entry and signal registration added
  • Duplicate language badgesger MKV track tag and target language de previously rendered as two separate badges; normLang() now normalises both sides before comparison so they collapse to a single badge

[0.12.3-beta] — 2026-02-28

Security

  • ZIP Slipmarketplace.py plugin installation now uses safe_zip_extract() that validates every entry before extraction (CRITICAL)
  • Git clone SSRF/RCEvalidate_git_url() enforces HTTPS + domain allowlist (github.com, gitlab.com, codeberg.org) for plugin installs (CRITICAL)
  • Path traversalis_safe_path() guard added to video segment, audio waveform/extract and OCR endpoints (HIGH)
  • Symlink deletion bypassdedup_engine.py now skips symlinks and validates paths against media_path before deletion (HIGH)
  • Hook env injectionsanitize_env_value() strips newlines and null-bytes from event data before passing to shell scripts (HIGH)
  • CORS wildcard Socket.IO — replaced "*" with configurable SUBLARR_CORS_ORIGINS (default: localhost dev origins) (MEDIUM)
  • New backend/security_utils.py — canonical security utilities used by all of the above

Changed

  • CI — paths-filter skips backend/frontend jobs when only the other side changed; concurrency cancels duplicate runs
  • Claude Code Review — project context in review prompt; concurrency cancels stale reviews on new commits

[0.12.0-beta] — 2026-02-23

Added

  • Settings UX Redesign — card-based sub-grouping in all tabs; each logical block has a header with icon, title, description and optional connection badge
  • SettingsCard component — reusable card wrapper with divided body rows and ConnectionBadge slot
  • ConnectionBadge component — 4-state indicator (connected/error/unconfigured/checking) for Sonarr, Radarr and media server tabs
  • Advanced Settings toggle — global "Erweitert" checkbox in the Settings header persisted to localStorage; hides annotated advanced fields by default with orange left-border marker
  • SettingRow descriptions — all 38 config fields now show always-visible description text beneath each label; 10 fields marked as advanced
  • InfoTooltip improvements — ESC-key dismiss, keyboard focus/blur handlers, full ARIA accessibility (aria-describedby, role="tooltip", useId), motion-safe: animation prefix
  • Dirty-state Save button — Save button disabled and grayed when no changes exist; enabled with amber indicator when fields differ from loaded config
  • Navigation warninguseBlocker (React Router v6) + window.beforeunload prevent accidental navigation away with unsaved changes
  • ProvidersTab descriptions — credential and endpoint fields annotated with contextual help text
  • MediaServersTab & WhisperTab descriptions — all SettingRow fields annotated
  • TranslationTab descriptions — backend credential fields annotated; PromptPresetsTab shows available template variables
  • MigrationTab improvements — hardcoded Tailwind color classes replaced with CSS custom properties; context header added

[0.11.1-beta] — 2026-02-22

Added

  • Scan Auto-Extractwanted_auto_extract + wanted_auto_translate settings; scanner extracts embedded subs immediately on first detection when enabled
  • Batch Extract EndpointPOST /api/v1/wanted/batch-extract extracts embedded subs for multiple wanted items in one request
  • Multi-Series Batch SearchPOST /api/v1/wanted/batch-search now accepts series_ids array to trigger search across multiple series at once
  • SeriesDetail Batch Toolbar — episode checkboxes with Search / Extract bulk actions
  • Library Batch Toolbar — series checkboxes with Search All Missing bulk action

[0.11.0-beta] — 2026-02-22

Added

  • Track Manifest (Phase 29) — list all embedded subtitle/audio streams in MKV files, extract them as standalone files, or use one as the translation source; TrackPanel component in Library/Series Detail
  • Video Sync Backend (Phase 30) — POST /api/v1/tools/video-sync starts async ffsubsync/alass job; GET polls progress; fallback timeout 300s
  • Video Sync Frontend (Phase 31) — SyncModal with engine selector (ffsubsync / alass), live progress bar; auto-sync after download configurable per-download
  • Waveform Editor (Phase 32) — Waveform tab in the subtitle editor: wavesurfer.js visualization with per-cue region markers; backend extracts audio via ffmpeg with in-memory waveform cache
  • Format Conversion (Phase 33) — convert ASS ↔ SRT ↔ SSA ↔ VTT via pysubs2; convert dropdown in TrackPanel for any non-image subtitle track
  • Batch OCR Pipeline (Phase 34) — async POST /api/v1/ocr/batch-extract + GET /api/v1/ocr/batch-extract/<job_id> for extracting text from PGS/VobSub image-based subtitle tracks via Tesseract; parallel 4-worker frame processing
  • Quality Fixes Toolbar (Phase 35) — one-click editor buttons: Overlap Fix, Timing Normalize, Merge Lines, Split Lines, Spell Check; all endpoints create .bak backup before modifying

Fixed

  • ESLint react-hooks/set-state-in-effect in SubtitleEditorModal — replaced synchronous setState calls in useEffect with React's "adjust during render" pattern

[0.10.0-beta] — 2026-02-22

Added

  • Context Window Batching (Phase 19) — subtitle cues grouped into context-window-aware chunks for coherent LLM translation
  • Translation Memory Cache (Phase 20) — SHA-256 exact-match + difflib similarity cache avoids retranslating identical/near-identical lines; .quality.json sidecar file tracks per-line scores
  • Per-Line Quality Scoring (Phase 21) — LLM scores each translated line 0–10; low-scoring lines retried automatically; quality badge in Library/Series Detail
  • Bulk Auto-Sync (Phase 22) — auto-sync buttons in Library, Series Detail, and subtitle editor; POST /api/v1/tools/bulk-auto-sync batch endpoint
  • Machine Translation Detection (Phase 23) — detects OpenSubtitles mt/ai flags; orange MT badge on search results and in Library
  • Uploader Trust Scoring (Phase 24) — 0–20 score bonus based on provider uploader rank; emerald Trust badge for top-ranked uploaders
  • AniDB Absolute Episode Order (Phase 25) — anidb_sync.py fetches anime-lists XML weekly; providers query absolute_episode for correct numbering; routes/anidb_mapping.py + db/repositories/anidb.py
  • Whisper Fallback Threshold (Phase 26) — configurable minimum Whisper confidence score; subs below threshold fall back to LLM retry
  • Tag-Based Profile Assignment (Phase 27) — Sonarr/Radarr series/movie tags automatically assign language profiles via TagProfileMapping table; processed in webhook handler
  • LLM Backend Presets (Phase 28) — 5 built-in prompt templates (Anime, Documentary, Casual, Literal, Dubbed); Settings UI "Add from Template" button; user-editable custom presets

Fixed

  • _translate_with_manager: batch_size chunking now applied correctly (regression in v0.9.6)
  • Prompt presets: {source_language} / {target_language} placeholders substituted at runtime, not stored pre-substituted

[0.9.6-beta] — 2026-02-21

Fixed

  • Zombie jobs: jobs stuck in "running" state after backend restart are cleaned up on startup
  • Wanted page: pagination counter now reflects active filter, not full DB total
  • Duplicate wanted_items: UniqueConstraint(file_path, target_language, subtitle_type) prevents race-condition duplicates
  • get_series_missing_counts(): excludes existing_sub = 'srt' and 'embedded_srt' (upgrade candidates) from "missing" count

[0.9.5-beta] — 2026-02-21

Added

  • Global Glossary — per-language term overrides applied during all translations; configurable in Settings → Translation
  • Per-Series Glossary — series-specific term overrides; accessible from Series Detail
  • Provider test: works without explicit Content-Type: application/json header (force=True JSON parsing)

[0.9.0-beta] — 2026-02-16

Added

  • Plugin architecture with hot-reload for custom subtitle providers

  • Plugin discovery from /config/plugins/ with manifest validation

  • Plugin-specific configuration stored in config_entries database table

  • Watchdog-based hot-reload with 2-second debounce (opt-in via plugin_hot_reload)

  • Plugin developer template and documentation

  • Gestdown — Addic7ed proxy with REST API, covers both Addic7ed and Gestdown content

  • Podnapisi — Large multilingual database with XML API and lxml parsing

  • Kitsunekko — Japanese anime subtitles via HTML scraping (BeautifulSoup optional)

  • Napisy24 — Polish subtitles with MD5 file hash matching (first 10MB)

  • Whisper-Subgen — External ASR integration, returns low-score placeholder in search

  • Titrari — Romanian subtitles via polite scraping (no auth required)

  • LegendasDivx — Portuguese subtitles with session authentication and daily limit tracking

  • Per-provider response time tracking with weighted running average

  • Auto-disable after consecutive failure threshold (default: 10 failures)

  • Configurable cooldown period (provider_auto_disable_cooldown_minutes, default: 30 min)

  • Provider health dashboard with success rate, response time, and download counts

  • DeepL backend with glossary caching by (source, target) language pair

  • LibreTranslate backend for self-hosted translation (line-by-line for 1:1 mapping)

  • OpenAI-compatible backend supporting any OpenAI API endpoint with CJK hallucination detection

  • Google Cloud Translation backend with fresh client per call for credential rotation

  • Per-profile backend selection in language profiles

  • Automatic fallback chains with configurable backend priority

  • Circuit breakers per translation backend (reuses provider circuit breaker pattern)

  • Translation quality metrics tracked per backend

  • Plex support with lazy plexapi connection (optional dependency)

  • Kodi support with JSON-RPC VideoLibrary.Scan (directory-scoped)

  • Unified media server settings page with multi-server configuration

  • MediaServerManager.refresh_all() notifies all configured servers after subtitle changes

  • Legacy Jellyfin configuration auto-migrated to new multi-server format

  • faster-whisper backend with lazy model loading and device/compute_type caching

  • Subgen backend for external Whisper API integration

  • Case D translation pipeline: automatic Whisper fallback when all providers fail

  • Whisper job queue with configurable max concurrency and progress via WebSocket

  • Audio extraction via ffmpeg pipe (no temp files)

  • Language detection validation against expected source language

  • Folder-watch operation without Sonarr/Radarr dependency

  • TMDB metadata lookup (requires API key)

  • AniList metadata lookup (no API key required, 0.7s rate limiting)

  • TVDB metadata lookup with 24h JWT token caching

  • Anime detection via multi-signal heuristic (bracket groups, fansub groups, CRC32, absolute numbering)

  • guessit-based filename parsing with anime-aware mode

  • MediaFileWatcher with per-path debounce and file stability checks

  • StandaloneScanner groups files by series for efficient metadata lookup

  • Standalone items integrate with existing Wanted pipeline

  • Multi-signal forced subtitle detection (ffprobe flags, filename patterns, title analysis, ASS style analysis)

  • Per-series forced subtitle preference (disabled/separate/auto) in language profiles

  • OpenSubtitles foreign_parts_only filter for native forced search

  • Post-search forced classification for providers without native support

  • Forced subtitle type badges and filter buttons in Wanted UI

  • Internal event bus using blinker with signal isolation namespace

  • 22+ business events published (subtitle_downloaded, translation_complete, provider_failed, etc.)

  • Shell script hooks with environment variable payload and configurable timeouts

  • Outgoing webhooks with HTTP POST, JSON payload, and retry logic on failure

  • Event catalog with versioned payload schemas (CATALOG_VERSION=1)

  • SocketIO bridge for real-time event forwarding to frontend

  • Configurable scoring weights (hash, series, year, season, episode, release_group, ASS bonus)

  • Per-provider score modifiers (-100 to +100 range)

  • Scoring cache with 60s TTL and config-change invalidation

  • English and German translations for entire UI

  • react-i18next with static JSON imports (no HTTP backend)

  • Language preference stored in localStorage (sublarr-language)

  • LanguageSwitcher component in header

  • Dark/light theme toggle with system preference detection

  • Theme stored in localStorage (sublarr-theme) with 3 states: dark, light, system

  • Inline script in index.html prevents flash of wrong theme before React hydration

  • CSS variable-based theming

  • Full backup (config + database as ZIP) with in-memory buffer

  • Scheduled automatic backups with configurable interval

  • Restore from ZIP upload via Settings UI

  • Backup rotation with configurable retention count

  • Recharts-based charts with responsive containers

  • Time-range filters (7d, 30d, 90d, all)

  • Daily stats, provider usage, translation backend performance, format distribution

  • Subtitle download and upgrade history visualization

  • Timing adjustment (centisecond precision, H:MM:SS.cc format)

  • Encoding fix (detect and convert to UTF-8)

  • Hearing impaired tag removal

  • Style stripping (ASS to plain text)

  • All tools create .bak backup before modification

  • Path traversal prevention via os.path.abspath validation

  • OpenAPI 3.0.3 specification at /api/v1/openapi.json with 65+ documented paths

  • Swagger UI at /api/docs for interactive API exploration

  • apispec + apispec-webframeworks for YAML docstring-based spec generation

  • X-Api-Key security scheme for authenticated endpoints

  • Incremental wanted scan with timestamp tracking (only rescans modified items)

  • Full scan forced every 6th cycle as safety fallback

  • Parallel ffprobe via ThreadPoolExecutor (max 4 workers per series)

  • Parallel wanted search processing (removed 0.5s inter-item delay)

  • Route-level code splitting with React.lazy for all 13 page components

  • PageSkeleton loading component for Suspense fallback

  • Extended /health/detailed with 11 subsystem categories

  • Translation backend health checks per instance

  • Media server health checks per instance

  • Whisper backend health reporting

  • Sonarr/Radarr connectivity checks across all configured instances

  • Scheduler status reporting

Changed

  • Architecture — Application Factory pattern (create_app()) with 15 Flask Blueprints (from monolithic server.py)
  • Database — Split database.py into db/ package with 9 domain modules (from monolithic 2153-line file)
  • Frontend — React 19 + TypeScript + Tailwind v4 (upgraded from React 18 + Tailwind CSS)
  • Translation — Ollama configuration moved from dedicated tab to unified Translation Backends tab
  • Settings — Split 4703-line Settings.tsx monolith into 7 focused tab modules under Settings/ directory
  • Version numbering — Changed from v1.0.0-beta to v0.9.0-beta (standard pre-release convention -- v1.0.0 reserved for stable release)
  • Gunicorn — Single worker mode required for Flask-SocketIO WebSocket state consistency

Fixed

  • Case-sensitive email uniqueness in provider configurations
  • Hardcoded version strings ("0.1.0") replaced with centralized version.py
  • SPA fallback route now returns correct version string
  • Toast message and ThemeToggle label i18n gaps closed
  • Pre-existing integration test expectations updated for health endpoint response format

[1.0.0-beta] — 2026-02-14

Added

  • Provider System — Direct subtitle sourcing from AnimeTosho, Jimaku, OpenSubtitles, and SubDL
  • Wanted System — Automatic detection of missing subtitles via Sonarr/Radarr integration
  • Search & Download Workflow — End-to-end subtitle acquisition without Bazarr
  • Upgrade System — Automatic SRT-to-ASS upgrades with configurable score delta
  • Language Profiles — Per-series/movie target language configuration with multi-language support
  • LLM Translation — Integrated subtitle translation via Ollama (ASS and SRT formats)
  • Glossary System — Per-series translation glossaries for consistent terminology
  • Prompt Presets — Customizable translation prompt templates with default preset
  • Blacklist & History — Track downloads and block unwanted subtitle releases
  • HI Removal — Hearing impaired marker removal from subtitles before translation
  • Embedded Subtitle Detection — Extract and translate subtitles embedded in MKV files
  • AniDB Integration — TVDB-to-AniDB ID mapping for better anime episode matching
  • Webhook Automation — Sonarr/Radarr webhooks trigger scan-search-translate pipeline
  • Multi-Instance Support — Configure multiple Sonarr/Radarr instances
  • Notification System — Apprise-based notifications (Pushover, Discord, Telegram, etc.)
  • Onboarding Wizard — Guided first-time setup
  • Provider Caching — TTL-based search result caching per provider
  • Re-Translation — Detect and re-translate files when model/prompt/language changes
  • Config Export/Import — Backup and restore application configuration
  • Docker Multi-Arch — Builds for linux/amd64 and linux/arm64
  • Unraid Template — Community Applications template for Unraid