Conversation
…king
- Replace upstream username with neutral terms ("upstream") in display text
- Update binaries.json and announcements.py to use forked LavX/bazarr-binaries
- Remove PayPal donate button from UI navbar and README
- Remove Feature Upvote and Discord links from issue template
- Update frontend/package.json author and URLs to this fork
- Rewrite "Why This Fork Exists" to focus on technical gap, not PR history
- Collapse original README section to just the provider list
- Update copyright years to 2025-2026
chore: minimize upstream maintainer mentions
When batch translation cannot find an external subtitle file for the source language, it now checks for embedded subtitle tracks in the video container. If found, it extracts the track to an SRT file using ffmpeg and uses it as the translation source. This enables the Mass Translate feature to work with media that only has embedded subtitles (stored as [lang, None, None] in the DB). Changes: - Add extract_embedded_subtitle() that uses ffprobe metadata to find the correct subtitle stream and ffmpeg to extract it to SRT - Add a third pass in find_subtitle_by_language() for embedded subs - Extract to /config/extracted_subs/ to avoid Jellyfin picking them up - Strip Windows carriage returns from extracted subtitles - Resolve alpha2 language codes to full names before sending to the AI translator (e.g. "pb" -> "Portuguese (Brazil)") Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New logo: play button with smiling subtitle lines, golden gradient on dark - Generated all icon sizes: favicon, PWA, apple-touch, mstile, android-chrome - Updated brand color palette from purple to golden/amber (#ff9900) to match logo - Added source SVG for future edits
The custom _escape() function did not escape shell metacharacters like backticks and dollar-sign substitution inside double-quoted strings, allowing potential command injection via externally-sourced subtitle provider data when post-processing is enabled. Replace with shlex.quote() which properly handles all shell special characters.
The throttled_providers.dat file was deserialized using eval(), which could execute arbitrary code if the file contents were tampered with. Replace with JSON serialization using json.loads()/json.dumps() and store datetime objects as ISO 8601 strings.
The CSRF state validation for Plex OAuth PIN checks only logged a warning but did not reject the request. Add abort(403) to actually block requests that fail state validation.
Replace unsalted MD5 password hashing with PBKDF2-SHA256 (150k iterations, random 16-byte salt, timing-safe comparison). - Legacy MD5 hashes (upstream format) are always accepted for login - On login with MD5 hash, UI prompts user to upgrade (modal dialog) - User can decline; prompt reappears on next login - Upgrade action re-hashes password and persists to config - New passwords set via settings always use PBKDF2 - Upstream bazarr users can migrate seamlessly (MD5 accepted on first login)
Add in-memory per-IP failed login attempt tracking. After 5 failed attempts within 5 minutes, the IP is locked out with HTTP 429. Counter resets on successful login or after lockout period expires. No new dependencies required (uses stdlib threading and time).
Add URL validation to the /test proxy endpoint to block requests to link-local addresses (169.254.x.x) which includes cloud metadata endpoints. Private LAN IPs are still allowed since the endpoint is used to test connectivity to Sonarr/Radarr/Plex instances.
…wser Filter out /proc, /sys, /dev, /run, /snap, /boot, and /lost+found from the filesystem browser API. These directories expose system internals and are never valid media paths. Users can still browse /home, /mnt, /media, /data, and other typical media storage locations.
Add verify_ssl config option for sonarr, radarr, and plex services (default: false for backward compatibility). Replace all hardcoded verify=False with get_ssl_verify() calls that read from config. Users with proper TLS certificates can now enable verification.
Prefer X-API-KEY header for authentication. Query string and form data API keys still work for backward compatibility but now log a deprecation warning with the endpoint path. Header auth is checked first to avoid unnecessary logging for well-behaved clients.
shlex.quote() is POSIX-only and produces single quotes that cmd.exe does not understand. Use subprocess.list2cmdline() on Windows for proper double-quote escaping. Also handle None inputs explicitly rather than converting to the string 'None'.
- Remove string fallback in set_throttled_providers (require dict) - Add None guard on throttle_until.isoformat() - Use atomic write (tmp file + os.replace) - Better migration log message for legacy format files - Remove finally:return antipattern - Extract path to _throttled_providers_path() helper - Catch specific exceptions (JSONDecodeError, KeyError, ValueError) separately from unexpected errors
Flip the guard logic: check if the server issued a state_token for this PIN (stored_state exists), and if so, require the client to provide a matching state param. Previously an attacker could bypass by simply omitting the state parameter. Also: remove redundant 'return' before abort(), use generic error message to avoid leaking implementation details.
Address review findings: - Fix AxiosResponse unwrap (return response.data, not response) - Replace plaintext password in sessionStorage with opaque token - Server stores password in Flask session (server-side only) - Upgrade action uses token to retrieve password from session - No credentials ever stored client-side or re-transmitted
- Increase PBKDF2-SHA256 iterations from 150k to 600k per OWASP 2023 - Extract iteration count to PBKDF2_ITERATIONS constant (no duplication) - Rollback in-memory password on write_config() failure to prevent split-brain state between running process and config file
The frontend received the state token from createPin() but never sent it back in checkPin() requests. Now the state is threaded through: Pin type -> usePlexPinCheckQuery -> checkPin API call. Combined with the server-side guard that requires state when issued, this closes the CSRF bypass.
- Use request.remote_addr only (not X-Forwarded-For/X-Real-IP) to prevent trivial rate limit bypass via header spoofing - Cap _login_attempts dict at 10k entries using OrderedDict with LRU eviction to prevent memory exhaustion from distributed attacks
Address review findings: - Resolve DNS once and pin request to resolved IP to prevent TOCTOU DNS rebinding attacks (attacker returns safe IP on first query, metadata IP on second) - Block both link-local AND loopback addresses (not just link-local) - Fail-closed: block request if DNS resolution fails (was fail-open) - Use Host header to maintain proper routing with pinned IP
Use settings.get('service.verify_ssl') instead of chained .get()
which could silently return False on typos. Add service name
validation with frozenset allowlist.
- Use hmac.compare_digest() for API key comparison to prevent timing side-channel attacks - Use %-formatting in logging.warning() instead of f-string to prevent potential log injection via request.path - Use request.headers.get() instead of manual 'in' check
/etc contains sensitive configs (shadow, passwd, keys), /root is the root user's home directory, /tmp may contain session data. None are valid media storage paths.
If the stored hash has 'pbkdf2:' prefix but malformed data (bad hex, missing colon), catch ValueError/TypeError and return False with a log message instead of crashing on every login attempt.
Use lambda in re.sub to avoid backslash escape interpretation in replacement strings. Windows paths like C:\Program Files would have \P treated as a bad regex escape. The lambda returns the escaped value literally without re.sub processing backslashes.
On dual-stack hosts, a .local hostname may resolve to both a private LAN address and a link-local IPv6 address. Instead of rejecting if ANY address is link-local, find a safe (non-link-local, non-loopback) address to pin to. Only reject if ALL addresses are blocked.
Linux systems using GNOME/KDE automount removable and network media under /run/media/. Blocking /run entirely prevents the file picker from reaching those valid storage locations.
Bazarr's own Plex webhook URLs use ?apikey= in their callback URLs. Skip the deprecation warning for /webhooks/ paths to avoid log spam from every Plex event.
fix(security): shell injection in post-processing
…er tasks to improve feedback in System-->Tasks and prevent users from starting the same task multiple times.
…` parameter to streamline update checks during application initialization.
… operations. Added garbage collection after sync tasks to improve memory management. morpheus65535#3241
…e impact of movie edition. Removed the custom scoring system and cleaned up the config file. Updated history tables schema to include `score_out_of` column for improved normalization of score percentage over time. morpheus65535#3232
… flat config Batch update of all frontend dependencies to match upstream development: - Mantine 8.3.9 -> 8.3.16, React 19.2.4, React Router 7.13.1 - ESLint 8 -> 10 with flat config (eslint.config.mjs replaces .eslintrc.json) - TypeScript 5.9.3, Vite 7.3.1, Vitest 4.0.18, Recharts 3.8.0 - FontAwesome 7.2.0, TanStack Query 5.90.21, axios 1.13.6 - Remove deprecated ban-types disable comments, fix jest v30 compat - Drop eslint checker from vite-plugin-checker (matches upstream)
Cherry-pick of upstream 27ea75e: bulk update of all vendored Python libraries in libs/. Kept our fork's Python 3.12 minimum requirement (stricter than upstream's 3.10). Updated CI matrices to match. Removed ga4mp analytics dist-info that was re-added by upstream (we intentionally removed this library from our fork).
- Remove flask_restx model validation decorator from batch POST to fix jsonschema/flask_restx incompatibility (registry vs resolver) - Update hardcoded score thresholds to use dynamic score_out_of column - Guard react-router blocker proceed/reset calls with state check to fix "Invalid blocker state transition" error in react-router 7.13 - Increase vitest timeout and use forks pool for test stability
chore: sync vendored Python libs from upstream
…uard - When a subtitle file has been renamed or re-downloaded (e.g. .hu.hi.srt to .hu.srt), the upgrade system now finds the current file for the same language instead of silently dropping the candidate. - Fix time_until_midnight callers passing datetime instead of timezone (regression from upstream cherry-pick of 263d01c). - Guard against None score in forced_minimum_score to prevent TypeError on manual download history entries.
Show which provider is being searched in real-time in the jobs manager (e.g. "Searching opensubtitles (2/5)"). Adds a provider_progress_callback on the provider pool that gets called before each provider query. Both individual and specific subtitle searches now report progress.
The terminate/restart logic should not be inside finally, which runs even if os.remove fails, masking the exception.
Verify the scraper service is reachable during provider initialization by hitting /health. If unreachable, raise ServiceUnavailable so the provider is throttled for 20 minutes and shows the issue in System > Providers, instead of silently failing on every search.
fix: upgrade path fallback, provider progress tracking
The upstream scoring refactor shifted weight from series name matching to source/release_group matching, making 90% unreachable for subtitles that match on series/year/season/episode but not release specifics.
When build_type is 'custom', the VERSION file was set to the custom tag name (e.g. 'beta') instead of the actual version. Now uses version_tag input when provided (e.g. 'v2.0.0-beta.1').
The else block only ran on jsdelivr success, so the fallback to raw GitHub would fetch but never save the file. Also add raise_for_status to catch HTTP errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bazarr+ v2.0.0 (Eclipse)
First stable release as an independent hard fork. 197 commits since v1.5.6-lavx "Neon Pulse".
Fork-Exclusive Features
Provider Priority Search
Dual mode: sequential priority order with early stop when minimum score is met, or classic simultaneous search. Requested upstream for 6 years (62 votes), rejected as "won't happen".
OpenSubtitles.org Web Scraper
Self-hosted FastAPI microservice via CloudScraper with FlareSolverr fallback. Full v1 REST API with metadata-aware search, intelligent result ranking, 30-language mapping. No API key or VIP needed.
AI Subtitle Translation (OpenRouter)
Access to 300+ LLMs (Claude, Gemini, GPT, LLaMA, Grok) plus custom model IDs. Dedicated 4-zone settings page with pricing, cost estimates, token usage, speed metrics. Batch translation for entire series/libraries. Translate-from-missing menu with source language picker.
11 Batch Operations
Sync, translate, OCR fixes, common fixes, remove HI, remove tags, fix uppercase, reverse RTL, emoji removal, scan disk, search missing, upgrade. Supports up to 10,000 items per batch with real-time progress.
Subtitle Viewer
Read-only preview with SRT/VTT/ASS parsing, cue table, timestamps, file size, format detection.
Per-Provider Progress Tracking
Subtitle searches show which provider is being queried in real-time (e.g. "Searching opensubtitles (2/9)").
Mass Subtitle Sync
Sync entire libraries from System Tasks or Mass Edit. Requested upstream (249 votes), rejected.
Advanced Table Filters
Include/exclude audio language (multi-select), missing subtitle filter, title search with active filter chips.
AES-256-GCM API Key Encryption
Encryption for translator API keys in transit with HMAC-SHA256 authentication.
Security Hardening (10 areas)
Atmospheric Dark Redesign
Upstream Sync (this release)
Cherry-picked 24 commits from morpheus65535/bazarr development:
Bug Fixes (this release)
Infrastructure
Verification