subflux

command module
v0.1.147 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: GPL-2.0, GPL-3.0 Imports: 51 Imported by: 0

README

subflux

Image Size Platforms Test coverage Mutation OpenSSF Best Practices OpenSSF Scorecard SBOM

A fast, small subtitle search, download, and sync engine for Sonarr and Radarr. A Go-based Bazarr alternative that ships as a ~14 MB container.

Alpha. Subflux is pre-1.0 and moving fast. It is functional and runs a 52k-episode production library today, but the UI, config format, and API can still change between releases, and rough edges remain. See ROADMAP.md for the path to 1.0.

What it does

Subflux finds, scores, downloads, and time-syncs subtitles for your Sonarr/Radarr library. It watches the *arr import history (default 30 s poll) so new downloads get subtitles within moments of importing, and runs scheduled full-library scans (default 24 h) to fill gaps and upgrade what's already there. Every result passes an identity check and a release-quality score; the best one is downloaded, synced against the video's own timing, cleaned up, and saved next to the media file. A web UI shows per-show coverage at a glance and lets you search, pick, and visually sync subtitles by hand when you want control.

Why subflux

Subflux was born from debugging Bazarr consuming 15-20 GB of RAM on a 52,000-episode library (a CPython allocator fragmentation problem, architectural rather than fixable). The answer was a rewrite with resource discipline as a design goal rather than an optimization pass:

  • ~14 MB compressed image, ffmpeg included. Distroless base, one static Go binary, no Python, no runtime dependencies.
  • The library that broke Bazarr runs in a 1 GB container limit. Arr responses are batch-fetched then iterated (the largest payload, 4,360 movies, decodes to 24 MB); goroutine pools are bounded; media probing streams instead of buffering.
  • A purpose-built ffmpeg (~5 MB, plus ~2 MB ffprobe): decoders for every mainstream video, audio, and subtitle codec, a single x264 encoder for the 360p preview, statically linked, no network support compiled in. It does track detection, subtitle/audio extraction, and the sync editor's live preview.
  • One file of state. Pure-Go bbolt (no SQLite, no CGO): crash-durable on commit, hot-backed-up on schedule, and reconciled against the filesystem so it heals itself after manual file changes.
  • amd64 + arm64 images, cosign-signed, with SBOM attestations.

Features

Search and scoring
  • Eight online providers (OpenSubtitles, Gestdown, SubSource, SubDL, BetaSeries, AnimeTosho, YIFY Subtitles, HDBits) plus embedded-track extraction, behind one interface.
  • Two-phase scoring: a hard identity gate (IMDB/TVDB/TMDB id, season/episode, title validation) before a 0-100 release-quality score used for ranking and upgrades. Upgrades replace an existing subtitle only when a strictly better release shows up.
  • Language rules keyed on the audio track: map detected audio languages to subtitle targets (Japanese audio can want different subs than English audio), with standard/forced/hi variants and per-target provider or min-score overrides.
  • Anime-aware numbering: searches run with aired, scene (TheXEM), and absolute (TVDB) numbering and merge the results, so long-running shows with weird episode orders still match.
  • Embedded-subtitle awareness: coverage counts text and bitmap tracks already in the container (SRT/ASS, PGS, VobSub, DVB), with per-codec ignore settings so an unwanted PGS track doesn't stop the search for a text alternative.
  • Adaptive backoff: per-provider exponential backoff for no-result media, season-level early termination, and per-provider timeouts, so failing or empty providers don't get hammered.
  • Manual override with locks: manual downloads are saved as numbered siblings (movie.fr.1.srt) and lock the item from automation; locks clear automatically when the files are deleted or the video is replaced.
The sync engine

Downloaded subtitles rarely match your exact file, so subflux syncs every download before it reaches the disk. The engine is a from-scratch Go port of alass plus subflux's own additions:

  • Constant-offset and split-aware alignment (the alass algorithms): a piecewise-linear overlap rating finds the global shift, and a dynamic-programming pass finds split points where the offset changes abruptly (commercial breaks, different cuts) and aligns each segment independently.
  • Framerate correction: drift detection via linear regression, snapping to known ratio pairs (23.976, 24, 25, 29.97, ...) with a golden-section search for non-standard ratios.
  • Cross-language anchor alignment (subflux's own): aligns subtitles across languages by matching language-independent anchors (numbers, proper nouns, cognates by edit distance) through a monotonic DP pass. This is what lets a French subtitle sync against the English track embedded in the file.
  • Audio sync with a re-tuned VAD: a pure-Go port of WebRTC's GMM voice-activity detector, reconfigured for film audio. The stock threshold under-detects movie dialogue; subflux runs a two-pass classification (a safe pass and a precise pass that must agree within 500 ms), which raised the benchmark pass rate from 82.5% to 91.4% across 326 files with ~120 ms residual error. Neural VADs (Silero, FunASR) were benchmarked and brought no improvement; the tuning is what matters. Voice activity is FFT-cross-correlated with the subtitle's dialogue signal, and ASS inputs are filtered for karaoke/signs/SDH first.
  • Confidence-weighted voting: the strategies run concurrently, results cluster by offset, agreeing clusters get boosted, and the winner is applied only above a confidence threshold. A sync that isn't confident is a sync that doesn't happen.
  • Knowing when not to sync: hash-matched subtitles and same-release matches skip sync (their timing is already right); forced subtitles skip it (too few cues to align reliably).
  • Post-processing: encoding normalization to UTF-8 (UTF-16, Windows-1252), hearing-impaired annotation removal, tag stripping, and whitespace cleanup, each step logged.

Auto-downloads sync against an embedded subtitle reference when the file has one (audio-based sync as an automatic fallback is opt-in). Audio sync and external-file references are always available manually from the sync dialog.

The web UI

A single-page app served by the same binary: framework-free TypeScript compiled to ~380 KB of first-party JS, loaded as native ES modules (no bundler), updating live over SSE.

  • Coverage table: every series and movie against your language rules, with per-target have/total badges, embedded-track counts, a missing-only filter, and text search.
  • Visual sync editor: subflux transcodes the actual video to a 360p stream on the fly (fMP4 over MSE, using the bundled ffmpeg) and renders the subtitle as a live caption track. Scrub the offset with a timecode control and the captions reload in place, so you verify timing with your own eyes; or run any sync strategy (embedded reference, external file, audio VAD) and preview its computed result before a byte is written.
  • Manual search: query all providers for any item, see each result's score breakdown and tier, and grab a specific pick. Downloading anything other than the top pick locks the item from automation until you release it.
  • Schema-driven settings: the entire config renders from a server-generated schema with tooltips; saves are validated, hot-reload the engine without a restart, and never echo secrets back to the browser.
  • History: every download and search attempt, filterable by type, language, and provider.

First run lands in unconfigured mode: the settings dialog auto-opens and the instance serves nothing else until a valid config is saved.

Auth, API, and operations
  • Multi-user auth (optional): local passwords (Argon2id), passkeys/WebAuthn, OIDC with PKCE, per-user API keys, and login rate limiting.
  • API and CLI: the UI drives a JSON API you can use too; a CLI runs searches and remote commands against a live instance (subflux search, scan, status, locks, backoff, score, ...).
  • Operations: Prometheus metrics at /metrics, structured slog logging (UTC), a distroless file-marker healthcheck, graceful shutdown, scheduled bbolt hot backups with a staleness metric, database reconciliation before each scan, and scan resume after a restart.

Run it

Images are published to both ghcr.io/cplieger/subflux and docker.io/cplieger/subflux; use whichever registry you prefer.

# compose.yaml
services:
  subflux:
    image: ghcr.io/cplieger/subflux:latest
    container_name: subflux
    restart: unless-stopped
    user: "1000:1000"  # match your host user
    ports:
      - "8374:8374"
    volumes:
      - "/opt/appdata/subflux:/config"  # config.yaml + bbolt state
      - "/path/to/media:/media"         # must NOT be read-only; subflux writes subtitle files

Open http://localhost:8374; the settings dialog opens on first run. Point it at your Sonarr/Radarr instances and configure providers and language rules.

Configuration

All settings are editable in the web UI (schema-driven form) and persist to config.yaml. The CLI also supports manual search and remote commands against a running instance via the SUBFLUX_URL env var; set SUBFLUX_API_KEY (created with subflux generate-api-key or in the web UI) to authenticate them when auth is enabled.

Running behind a reverse proxy

When subflux runs behind a reverse proxy (nginx, Caddy, Traefik, HAProxy, ...), the network peer subflux sees is the proxy, not the browser. Set trusted_proxies to the proxy's IP or CIDR so the real client IP, resolved from a trusted X-Forwarded-For header, is used for the audit log, the login rate limiter, the session IPAddress, and the request access log, instead of the proxy's address:

trusted_proxies:
  - 10.0.0.0/8
  - 192.168.0.0/16

Entries are CIDR ranges; write a single proxy as a /32 (IPv4) or /128 (IPv6). Only when the direct peer is one of these ranges is X-Forwarded-For consulted (walked right-to-left, spoof-safe); invalid CIDRs are rejected at config load. Leave trusted_proxies empty (the default) when subflux is directly exposed: the socket peer is used and X-Forwarded-For is ignored.

Alerting

subflux exposes Prometheus metrics on /metrics. Scrape it and evaluate these with Prometheus or the Mimir ruler; delivery is through your Alertmanager.

groups:
  - name: subflux
    rules:
      - alert: SubfluxHTTP5xx
        expr: sum(increase(subflux_http_requests_total{status=~"5.."}[10m])) > 5
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "subflux is returning HTTP 5xx"
          description: >
            subflux returned more than 5 server errors in 10m. Check upstream
            connectivity, provider config, and the subflux logs.
      - alert: SubfluxBackupStale
        expr: >
          subflux_backup_last_success_timestamp > 0
          and (time() - subflux_backup_last_success_timestamp) > 172800
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "subflux backup is stale"
          description: >
            No successful subflux backup recorded in over 48h. Check the backup
            task and the /config volume.

Thresholds are starting points; add your scrape job label to the selectors if you run more than one instance, and route by whatever labels your Alertmanager uses.

Security

Distroless gcr.io/distroless/static-debian13:nonroot (UID 65532, no shell). Provider URLs are validated against SSRF before every fetch; secrets are redacted from config API responses; archive extraction is zip-bomb-guarded; all external input is size-capped and validated. Images are published with cosign signatures and SBOM attestations.

Known limitations

  • The media volume must be writable. Subflux saves subtitle files next to the media, so mounting /media read-only silently prevents downloads from being saved.
  • Cloudflare-protected providers (subf2m, AvistaZ, CinemaZ) are not implemented.
  • Long-running anime with colliding aired/absolute numbering has a rare false-positive window: results matched by a stable ID skip title validation, so an aired SxxEyy that collides with another episode's absolute number can slip through.

Credits

  • alass by @kaegi: the subtitle alignment algorithm (constant-offset rating and split-aware DP) that subflux ports to Go.
  • The WebRTC project: the GMM voice-activity detector that subflux ports and re-tunes for film audio.
  • Bazarr: the project that defined this category; subflux is an original engine, not a fork, but Bazarr set the bar for what it has to do.

Contributing

Issues and pull requests are welcome; please open an issue first for larger changes so the approach can be discussed. Architecture notes and local build/test instructions are in CONTRIBUTING.md; operational runbooks live in docs/OPERABILITY.md.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude Opus and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

GPL-3.0. See LICENSE.

Documentation

Overview

providers.go is the single registration point for all subtitle providers. Each provider's factory function and settings schema are registered here; adding a new provider requires one Register + one RegisterSchema call. No init(), no blank imports, no global state.

Directories

Path Synopsis
cmd
wire-codegen command
Command wire-codegen generates TypeScript interfaces and validating decoders from Go wire types using the wiregen library (AST-based; github.com/cplieger/wiregen).
Command wire-codegen generates TypeScript interfaces and validating decoders from Go wire types using the wiregen library (AST-based; github.com/cplieger/wiregen).
internal
api
Package api defines the internal contracts between subflux components.
Package api defines the internal contracts between subflux components.
arrsvc
Package arrsvc composes the github.com/cplieger/arrapi Sonarr and Radarr clients with subflux's subtitle-specific operations: "wanted" iteration (which library items need a subtitle search), exclude-tag name resolution, and a fire-and-forget rescan.
Package arrsvc composes the github.com/cplieger/arrapi Sonarr and Radarr clients with subflux's subtitle-specific operations: "wanted" iteration (which library items need a subtitle search), exclude-tag name resolution, and a fire-and-forget rescan.
authstore
Package authstore re-exports the composite store interface from the standalone github.com/cplieger/auth/v2 library so subflux call sites can continue to refer to authstore.AuthStore without depending on the library's import path directly.
Package authstore re-exports the composite store interface from the standalone github.com/cplieger/auth/v2 library so subflux call sites can continue to refer to authstore.AuthStore without depending on the library's import path directly.
authstore/authstoretest
Package authstoretest provides a shared, engine-agnostic behavioral contract suite for the composite auth store (cplieger/auth/store.Composite).
Package authstoretest provides a shared, engine-agnostic behavioral contract suite for the composite auth store (cplieger/auth/store.Composite).
boltstore
Package boltstore is the bbolt-backed core store for subflux's search, subtitle, scan, sync-offset, and poll domains.
Package boltstore is the bbolt-backed core store for subflux's search, subtitle, scan, sync-offset, and poll domains.
cache
Package cache provides a generic TTL cache with singleflight coalescing.
Package cache provides a generic TTL cache with singleflight coalescing.
cliparse
Package cliparse adds help generation, unknown-flag detection (with typo suggestions), and required/type validation on top of subflux's existing flag-parsing helpers.
Package cliparse adds help generation, unknown-flag detection (with typo suggestions), and required/type validation on top of subflux's existing flag-parsing helpers.
clisearch
Package clisearch implements CLI subtitle search, resolution, and download.
Package clisearch implements CLI subtitle search, resolution, and download.
config
Package config loads, validates, and hot-reloads the subflux YAML configuration.
Package config loads, validates, and hot-reloads the subflux YAML configuration.
config/defaults
Package defaults provides shared configuration constants and helpers used by both config/ and config/schema/.
Package defaults provides shared configuration constants and helpers used by both config/ and config/schema/.
config/schema
Package schema generates the UI configuration schema for the web frontend.
Package schema generates the UI configuration schema for the web frontend.
httputil
Package httputil provides shared HTTP utilities for subtitle providers.
Package httputil provides shared HTTP utilities for subtitle providers.
metrics
Package metrics provides Prometheus-compatible metrics for Subflux.
Package metrics provides Prometheus-compatible metrics for Subflux.
provider
Package provider contains the provider registry and retry wrappers.
Package provider contains the provider registry and retry wrappers.
provider/anidb
Package anidb provides TVDB → AniDB mapping using the community-maintained anime-list.xml from https://github.com/Anime-Lists/anime-lists.
Package anidb provides TVDB → AniDB mapping using the community-maintained anime-list.xml from https://github.com/Anime-Lists/anime-lists.
provider/animetosho
Package animetosho implements the AnimeTosho subtitle provider.
Package animetosho implements the AnimeTosho subtitle provider.
provider/archive
Package archive provides subtitle extraction from ZIP and RAR archives.
Package archive provides subtitle extraction from ZIP and RAR archives.
provider/betaseries
Package betaseries implements the BetaSeries subtitle provider.
Package betaseries implements the BetaSeries subtitle provider.
provider/classify
Package classify provides subtitle classification (forced/HI) and language resolution utilities consumed by provider sub-packages.
Package classify provides subtitle classification (forced/HI) and language resolution utilities consumed by provider sub-packages.
provider/dlcache
Package dlcache provides a generic LRU download-data cache with heap-based eviction.
Package dlcache provides a generic LRU download-data cache with heap-based eviction.
provider/embedded
Package embedded detects embedded subtitles in video files.
Package embedded detects embedded subtitles in video files.
provider/gestdown
Package gestdown implements the Gestdown subtitle provider.
Package gestdown implements the Gestdown subtitle provider.
provider/hdbits
Package hdbits implements the HDBits.org subtitle provider.
Package hdbits implements the HDBits.org subtitle provider.
provider/mock
Package mock provides a configurable mock subtitle provider for functional testing.
Package mock provides a configurable mock subtitle provider for functional testing.
provider/opensubtitles
Package opensubtitles implements the OpenSubtitles.com REST API provider.
Package opensubtitles implements the OpenSubtitles.com REST API provider.
provider/subdl
Package subdl implements the SubDL subtitle provider.
Package subdl implements the SubDL subtitle provider.
provider/subsource
Package subsource implements the SubSource subtitle provider.
Package subsource implements the SubSource subtitle provider.
provider/yifysubtitles
Package yifysubtitles implements the YIFY Subtitles provider.
Package yifysubtitles implements the YIFY Subtitles provider.
scorer
Package scorer implements subtitle scoring based on release matching.
Package scorer implements subtitle scoring based on release matching.
search
Package search implements the subtitle search engine.
Package search implements the subtitle search engine.
search/release
Package release provides release name parsing via PCRE-compatible regex patterns sourced from TRaSH Guides and Sonarr/Radarr QualityParser.
Package release provides release name parsing via PCRE-compatible regex patterns sourced from TRaSH Guides and Sonarr/Radarr QualityParser.
search/scoring
Package scoring provides pure subtitle scoring and identity filtering.
Package scoring provides pure subtitle scoring and identity filtering.
search/syncing
Package syncing provides subtitle timing synchronization for the search engine.
Package syncing provides subtitle timing synchronization for the search engine.
search/timeout
Package timeout provides provider health tracking with sliding-window failure detection and cooldown-based timeout.
Package timeout provides provider health tracking with sliding-window failure detection and cooldown-based timeout.
server
Package server — Content-Security-Policy construction.
Package server — Content-Security-Policy construction.
server/activity
Package activity provides concurrent-safe activity and alert tracking for the subflux UI status indicator.
Package activity provides concurrent-safe activity and alert tracking for the subflux UI status indicator.
server/authhandlers
Package authhandlers provides shared types and utilities for the server's authentication handler cluster: login, WebAuthn, OIDC, admin user management, and security management (password change, API keys, passkeys).
Package authhandlers provides shared types and utilities for the server's authentication handler cluster: login, WebAuthn, OIDC, admin user management, and security management (password change, API keys, passkeys).
server/confighandlers
Package confighandlers provides HTTP handlers for configuration CRUD operations: get, save, reset, schema, and path validation.
Package confighandlers provides HTTP handlers for configuration CRUD operations: get, save, reset, schema, and path validation.
server/coverage
Package coverage provides pure computation functions for subtitle coverage analysis.
Package coverage provides pure computation functions for subtitle coverage analysis.
server/coveragehandlers
Package coveragehandlers provides HTTP handlers for the /api/coverage/* endpoints.
Package coveragehandlers provides HTTP handlers for the /api/coverage/* endpoints.
server/events
Package events is subflux's typed server-sent-events layer: the sealed Event/EventData types the app publishes, marshaled onto the shared webhttp/sse broadcast hub.
Package events is subflux's typed server-sent-events layer: the sealed Event/EventData types the app publishes, marshaled onto the shared webhttp/sse broadcast hub.
server/filehandlers
Package filehandlers provides HTTP handlers for the /api/files/* endpoints.
Package filehandlers provides HTTP handlers for the /api/files/* endpoints.
server/httphelpers
Package httphelpers provides shared HTTP handler prelude helpers used across server sub-packages.
Package httphelpers provides shared HTTP handler prelude helpers used across server sub-packages.
server/manualops
Package manualops implements the business logic for manual subtitle search and download operations.
Package manualops implements the business logic for manual subtitle search and download operations.
server/mediahandlers
Package mediahandlers provides HTTP handlers for the /api/media/* endpoints.
Package mediahandlers provides HTTP handlers for the /api/media/* endpoints.
server/polling
Package polling provides the history-polling subsystem for Sonarr/Radarr import events and the write-through poll timestamp cache.
Package polling provides the history-polling subsystem for Sonarr/Radarr import events and the write-through poll timestamp cache.
server/previewhandlers
Package previewhandlers provides HTTP handlers for the video/subtitle preview and poster proxy endpoints.
Package previewhandlers provides HTTP handlers for the video/subtitle preview and poster proxy endpoints.
server/queryhandlers
Package queryhandlers provides read-only HTTP query handlers: subtitle state, backoff, manual locks, providers, parsed config, score simulation, and dashboard stats.
Package queryhandlers provides read-only HTTP query handlers: subtitle state, backoff, manual locks, providers, parsed config, score simulation, and dashboard stats.
server/scanning
Package scanning implements the full-scan orchestration engine.
Package scanning implements the full-scan orchestration engine.
server/scheduler
Package scheduler provides the periodic full-scan pipeline, DB maintenance, and auth cleanup scheduling for the subflux server.
Package scheduler provides the periodic full-scan pipeline, DB maintenance, and auth cleanup scheduling for the subflux server.
server/serveradapter
Package serveradapter provides adapter types that bridge the server's activity, alert, and event subsystems to the interfaces consumed by the scanning and manualops packages.
Package serveradapter provides adapter types that bridge the server's activity, alert, and event subsystems to the interfaces consumed by the scanning and manualops packages.
server/sessionbatch
Package sessionbatch implements batched session activity updates to reduce SQLite write-lock acquisitions.
Package sessionbatch implements batched session activity updates to reduce SQLite write-lock acquisitions.
server/showskip
Package showskip provides a TTL-based cache for show-level subtitle pre-check results.
Package showskip provides a TTL-based cache for show-level subtitle pre-check results.
server/synchandlers
Package synchandlers provides HTTP handlers for subtitle sync operations (audio-based sync and manual offset adjustment).
Package synchandlers provides HTTP handlers for subtitle sync operations (audio-based sync and manual offset adjustment).
store/kv
Package kv holds the shared bbolt store primitives used by both the core subflux store (internal/store) and the auth store (internal/authstore).
Package kv holds the shared bbolt store primitives used by both the core subflux store (internal/store) and the auth store (internal/authstore).
store/storetest
Package storetest provides a shared, engine-agnostic behavioral contract suite for api.Store implementations.
Package storetest provides a shared, engine-agnostic behavioral contract suite for api.Store implementations.
subsync
Package subsync provides subtitle timing synchronization.
Package subsync provides subtitle timing synchronization.
subsync/crosslang
Package crosslang implements cross-language subtitle alignment using anchor-based matching and dynamic programming.
Package crosslang implements cross-language subtitle alignment using anchor-based matching and dynamic programming.
subsync/ffmpeg
Package ffmpeg provides low-level ffmpeg/ffprobe subprocess wrappers for stream probing, subtitle extraction, and PCM audio extraction.
Package ffmpeg provides low-level ffmpeg/ffprobe subprocess wrappers for stream probing, subtitle extraction, and PCM audio extraction.
subsync/fft
Package fft provides a radix-2 Cooley-Tukey FFT implementation for cross-correlation in the subsync package.
Package fft provides a radix-2 Cooley-Tukey FFT implementation for cross-correlation in the subsync package.
subsync/framerate
Package framerate provides framerate drift detection for subtitle alignment.
Package framerate provides framerate drift detection for subtitle alignment.
testsupport
Package testsupport provides shared test helpers used across multiple packages.
Package testsupport provides shared test helpers used across multiple packages.
wiring
Package wiring holds composition-root types that connect concrete implementations across the api, metrics, search, and provider packages.
Package wiring holds composition-root types that connect concrete implementations across the api, metrics, search, and provider packages.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL