Trackstarr tidies audio and subtitle tracks in your movie and TV library. It processes Radarr and Sonarr imports, removes unwanted tracks, creates missing audio downmixes and puts the remaining tracks in your preferred order. Video is always copied without re-encoding.
- Audio and language rules: keep original-language audio and selected dubs, add stereo or surround mixes, choose codecs and bitrates, and remove unwanted layouts.
- Track cleanup: remove commentary, redundant SDH subtitles, embedded artwork and release tags; optionally convert MP4/M4V to MKV.
- Controlled processing: preview plans, schedule library sweeps, pause individual titles, pause processing and defer hard-linked files.
- Live web UI: browse and search your library, inspect planned changes, edit MKV track tags, follow progress and review event history.
- Media server integration: refresh Plex, Jellyfin or Emby after a rewrite.
Try the demo — a sample library with no backend. Any username and password work.
services:
trackstarr:
image: ghcr.io/marcedforlife/trackstarr:latest
container_name: trackstarr
restart: unless-stopped
user: "${PUID:-1000}:${PGID:-1000}"
ports:
- "5120:5120"
volumes:
- /srv/media:/data
- /srv/config/trackstarr:/config
environment:
MEDIA_DIRS: /data/movies:/data/tv
TZ: Pacific/Auckland- Create
/srv/config/trackstarrwith ownership matching the container user, for examplesudo install -d -o 1000 -g 1000 /srv/config/trackstarr. That user also needs write access to the media and staging directory (/data/trackstarr-workby default). - Save the example as
compose.yaml, adjust the paths and timezone, and rundocker compose up -d. - Open port 5120 in your browser. Sign in as
adminusing the password printed in the container log, then change it when prompted. - Add Radarr and Sonarr under Settings → Connections. Trackstarr registers their webhooks automatically.
MEDIA_DIRS and paths received from the *arrs must refer to files inside the
container. The example maps /srv/media/movies to /data/movies;
change the mount or MEDIA_DIRS to match your library. Missing sweep directories
produce warnings, so check the startup log. Set WEBHOOK_URL if the *arrs cannot
reach http://trackstarr:5120.
The default REWRITE_MODE=imports rewrites new imports but only reports changes
for library sweeps. Use report to preview all processing first; use all to
allow scheduled sweeps to rewrite the existing library. Review
/config/pending.tsv before enabling all.
Each RULE_<NAME> accepts always, alongside or never:
always: apply whenever needed.alongside: apply only when another rule already requires a rewrite.never: disable the rule.
| Rule | Default | Action |
|---|---|---|
languages |
always |
Remove audio and subtitles in languages absent from LANGUAGES. Untagged tracks stay. |
tag_original |
never |
Tag an untagged audio track with the title's original language, where nothing in the file contradicts it. Usually no rewrite. |
commentary |
never |
Remove commentary, described audio and isolated scores. These are never downmix sources. |
sdh |
alongside |
Remove an SDH subtitle if a full subtitle survives in the same language. This rule preserves forced subtitles. |
regenerate |
never |
Rebuild outdated downmixes or replace tracks under the configured bitrate rules. MKV only. |
cover_art |
always |
Remove embedded artwork. |
release_tags |
alongside |
Clear release tags from track and container titles. |
stray_streams |
alongside |
Remove data and timecode streams. |
order |
always |
Order video, audio by AUDIO_LAYOUTS (then other sizes by channel count), subtitles and attachments. |
remux |
never |
Convert MP4/M4V to MKV, converting text subtitles to SRT. Video is copied. |
Rules are idempotent: an unchanged file does not need another rewrite.
alongside rules cannot trigger a rewrite indirectly. Trackstarr never upmixes
or applies a language filter that would remove every audio track. Forced
subtitles are protected from SDH cleanup, but still follow the language filter.
AUDIO_LAYOUTS controls which audio sizes to create, retain or remove, and their
output order. The default 2.0,5.1 creates missing stereo AAC at 320k and 5.1 AC3
at 640k.
AUDIO_LAYOUTS: 2.0:libopus:192k,5.1:eac3:448k,7.1:remove| Entry | Meaning |
|---|---|
5.1 |
Create a missing mix using the defaults for this layout. |
5.1:eac3:448k |
Create a missing mix with this encoder and bitrate. |
7.1:keep |
Keep this size without creating it; include it in the output order. |
7.1:remove |
Remove this size after creating any replacement downmixes. |
Bare-layout defaults are 1.0:aac:160k, 2.0:aac:320k, 5.1:ac3:640k,
6.1:aac:704k and 7.1:aac:768k. Other sizes need an explicit encoder and rate.
Each channel count may appear only once, including equivalent counts such as
4.2 and 5.1.
A missing mix uses the best eligible larger track in the same language. Without
a larger source, it cannot be created. Downmix and removal entries need no
separate RULE_ switch: either can trigger a rewrite. Removal can use the outgoing
track as a downmix source, but never removes the last audio track. Removed tracks
are not backed up and cannot be used for future regeneration.
LANGUAGES controls which languages survive and which receive downmixes.
The default is original,eng.
LANGUAGES: original,eng,fre:keep| Entry | Meaning |
|---|---|
eng |
Keep English and create the requested downmix layouts where sources exist. |
fre:keep |
Keep French without creating mixes. |
original |
Use the title's original language reported by Radarr or Sonarr; no TMDB key needed. |
Explicit language entries take precedence over original. If the *arrs cannot
identify the original language, that entry contributes no language. Names
normalise: en, eng and English are equivalent.
RULE_TAG_ORIGINAL writes the original language onto an untagged audio track,
but only where the file has one untagged track, nothing in that language already,
and that track is neither commentary nor titled as another language. A code
LANGUAGES would then drop is never written.
Where the tag is a file's only change it goes into the MKV header with
mkvpropedit, so nothing is re-encoded and no alongside rule rides along. MP4, a
hardlink the download client still holds, and an image without mkvtoolnix fall
back to a rewrite, which writes the same tag.
Unlisted languages follow RULE_LANGUAGES; untagged tracks always stay. If none
of the requested downmix languages can fill a layout, Trackstarr can use another
surviving language, preferring LANGUAGES order. A generated fallback is removed
once a requested language can supply that layout.
Enable RULE_REGENERATE to update existing mixes in MKV files:
REGENERATE_SCOPE=generatedrebuilds Trackstarr's own tracks when their codec or bitrate settings change.REGENERATE_SCOPE=allalso replaces tracks belowREGENERATE_BELOW_PERCENTof the target bitrate, when a larger surviving source can improve them. The default80means below 80% of the target, not 80% below it.REGENERATE_ABOVE_PERCENTre-encodes oversized lossy tracks from themselves.0disables this;150means above 150% of the target bitrate. Lossless tracks are excluded. This option trades quality for space.
Imports and sweeps. Webhooks queue imported files. SWEEP_AT schedules a walk
of MEDIA_DIRS using a five-field cron expression in the configured timezone.
Unchanged files use cached verdicts; policy or version changes invalidate them.
Sweeps write actionable results to /config/pending.tsv. Imports get priority
for their initial check on the separate probe pool, so a sweep rewrite cannot
hold that check up. Imports needing a rewrite move to the front of the waiting
rewrite queue unless a manual reorder is active. Running rewrites finish first;
undoing the manual reorder restores automatic import promotion.
Hard links. With SKIP_HARDLINKS=true, files with multiple hard links are
deferred. They are rechecked every HARDLINK_RECHECK seconds (900 by default).
Processing resumes once the extra links are removed; stopping seeding alone does
not remove a hard link. Set the interval to 0 to leave retries to sweeps.
Holds and pause. Hold a title for a set time or indefinitely. Held files are still planned and reported, but rewrites wait. Holds and the global processing pause survive restarts. The UI also supports stopping after the current file, stopping all work and skipping an individual file.
Verified replacement. Rewrites are staged in WORK_DIR. Trackstarr checks
duration and stream count before replacing the original and defers files that
change during processing. Staging needs space for the output; a different
filesystem adds a copy. Track removal is permanent; Trackstarr does not keep a
backup of the original.
Connection failures. If a configured *arr library cannot be listed and the
policy uses original, an applying sweep falls back to report-only to avoid
removing audio based on incomplete language information.
Media servers. Configure Plex or Jellyfin (also used for Emby) to refresh
rewritten files. If the server uses different paths, set a mapping such as
PLEX_PATH_MAP=/data/media=/srv/media.
- Overview: live progress, estimated time remaining, queued work and processing controls.
- Library: search movies and series, filter verdicts, sort titles and inspect per-file plans. Missing downloads and unsupported containers have distinct verdicts, and an Untagged filter finds the titles whose audio carries no language tag. Admins can pause titles or select them for immediate planning or rewriting.
- Track editing: admins can edit MKV track languages and commentary, forced
and SDH flags, including matching tracks across episodes.
mkvpropeditupdates headers in place, then Trackstarr reassesses the file. - Events: rewrite, failure, deferral, sweep and settings history from
events.jsonl, with the policy used for each decision. - Settings: edit runtime settings and test connections. Non-empty environment overrides pin the corresponding fields.
- Appearance: light, dark or system theme, six colour palettes or one from a hue of your own, poster effects, cover-art visibility and default library filters and ordering, saved per browser.
- Account: change your password. Accounts have admin or viewer roles; administrators manage accounts through the CLI.
First start creates admin using ADMIN_PASSWORD, or a generated password logged
once. To reset it, run docker exec -it trackstarr trackstarr user passwd admin.
The UI and its data API require sign-in; /health is public and webhooks use
separate secrets. Reverse proxies must allow streaming on /api/stream without
response buffering for live updates.
trackstarr serve # web UI, API, webhooks and scheduled sweeps
trackstarr sweep # report library changes in /config/pending.tsv
trackstarr sweep --apply # apply library changes, unless mode is report
trackstarr fix FILE... # plan and rewrite selected files
trackstarr plan FILE... # explain changes and print the ffmpeg command
trackstarr secret NAME # create a caller's webhook secret
trackstarr user add NAME # create an account (--role admin|viewer)
trackstarr user passwd NAME # reset a password and invalidate sessions
trackstarr user rm NAME # delete an account; preserves the last admin
trackstarr user list # list accounts and rolesplan and fix accept --original LANG for files outside an *arr library:
trackstarr plan --original eng "Tears of Steel (2012).mkv"REWRITE_MODE=report prevents rewrites even with fix or sweep --apply.
Title holds also apply to these commands.
Runtime settings can be saved in /config/settings.json through the UI.
Non-empty environment variables take precedence. Unknown settings-file keys
produce startup warnings. The service settings in the last table are configured
through the environment and require a restart.
| Variable | Default | Purpose |
|---|---|---|
LANGUAGES |
original,eng |
Languages to keep and downmix; see above. |
AUDIO_LAYOUTS |
2.0,5.1 |
Layout actions, encoders, bitrates and order. |
RULE_<NAME> |
See Rules | never, alongside or always. |
ALLOWED_EXTS |
.mkv,.mp4,.m4v |
Containers eligible for rewriting. |
REGENERATE_SCOPE |
generated |
generated or all. |
REGENERATE_BELOW_PERCENT |
80 |
Low-bitrate threshold as a percentage of target; 10–90. |
REGENERATE_ABOVE_PERCENT |
0 |
Oversized-track threshold; 0 disables, otherwise 110–400. |
COMMENTARY_PATTERN, SDH_PATTERN, FORCED_PATTERN, RELEASE_TAG_PATTERN |
See config.py | Track-title detection regexes. |
| Variable | Default | Purpose |
|---|---|---|
RADARR_URL / RADARR_API_KEY |
Unset | Radarr connection; both required to enable. |
SONARR_URL / SONARR_API_KEY |
Unset | Sonarr connection; both required to enable. |
PLEX_URL / PLEX_TOKEN |
Unset | Plex refreshes and title links. |
JELLYFIN_URL / JELLYFIN_API_KEY |
Unset | Jellyfin or Emby refreshes and title links. |
PLEX_PATH_MAP / JELLYFIN_PATH_MAP |
Unset | Comma-separated LOCAL=REMOTE path prefixes. |
RADARR_PUBLIC_URL, SONARR_PUBLIC_URL, PLEX_PUBLIC_URL, JELLYFIN_PUBLIC_URL |
Service URL | Browser-accessible addresses for title links. |
WEBHOOK_URL |
http://trackstarr:5120 |
Address the *arrs use to reach Trackstarr; default follows LISTEN_PORT. |
| Variable | Default | Purpose |
|---|---|---|
MEDIA_DIRS |
/data/media/movies:/data/media/tv |
Colon-separated sweep roots inside the container. |
REWRITE_MODE |
imports |
report, imports or all. |
SWEEP_AT |
Unset | Local-time cron schedule, e.g. 0 4 * * *; empty disables. |
TZ |
Unset | IANA timezone; container defaults to UTC. |
SKIP_HARDLINKS |
true |
Defer files with multiple hard links. |
HARDLINK_RECHECK |
900 |
Retry interval in seconds; 0 leaves retries to sweeps. |
IMDB_RATINGS |
true |
Fetch IMDb ratings daily for title scores; dataset is for personal, non-commercial use. |
MAX_CONCURRENT_REWRITES |
1 |
Shared rewrite limit across imports, sweeps and processes using the same state directory. |
PROBE_WORKERS |
4 |
Concurrent import and sweep probes. |
FFMPEG_TIMEOUT / PROBE_TIMEOUT |
7200 / 180 |
Timeouts in seconds. |
| Variable | Default | Purpose |
|---|---|---|
WORK_DIR |
/data/trackstarr-work |
Rewrite staging directory. |
STATE_DIR |
/config |
Settings, cache, events, accounts, pauses, secrets and locks. |
TRACKSTARR_KEY_FILE |
$STATE_DIR/key |
Encryption key for saved credentials. |
LISTEN_ADDR / LISTEN_PORT |
0.0.0.0 / 5120 |
Listener address and port. |
WEB_DIR |
/web in the image |
Static UI directory; empty disables pages. |
ADMIN_PASSWORD |
Generated | Initial admin password, ignored once the user store exists. Supports secret-file forms. |
LOG_LEVEL |
INFO |
DEBUG, INFO, WARNING, ERROR or CRITICAL. |
Supply connection credentials in one of three ways:
| Method | Storage | Editable in UI |
|---|---|---|
| Connections page | Encrypted in settings.json with mode 0600 |
Yes |
RADARR_API_KEY (or equivalent) |
Environment; visible in docker inspect |
No |
RADARR_API_KEY_FILE or FILE__RADARR_API_KEY |
Mounted secret file | No |
Using multiple environment forms for the same credential is a startup error. Environment credentials override saved values.
By default, Trackstarr creates /config/key when first saving a credential.
Anyone with the entire config directory therefore has both the encrypted values
and their key. To keep the key separately, generate one and mount it at the path
set by TRACKSTARR_KEY_FILE:
(umask 077; openssl rand -hex 32 > trackstarr.key)Ensure the container user can read the mounted key, and back it up. An explicitly configured key file is never generated automatically. If it is missing or invalid, the service starts with affected connections disabled. Restore the key, or provide a valid replacement and re-enter the connection credentials.
Webhook secrets are stored as SHA-256 digests in /config/webhook-secrets and
*arr webhooks are verified and provisioned at startup. Account passwords are
stored as scrypt hashes in /config/users.json.
Use Python 3.14+, uv, Node.js/npm (CI uses Node 26), ffmpeg/ffprobe 8.1+ and mkvtoolnix.
uv sync --locked
cp .env.example .env # local state and staging under dev/
uv run dev.py # starts API and UI with hot reload
uv run pytest
uv run pytest --cov # enforces 100% coverage
uv run ruff check
uv run ruff format --check
uv run mypydev.py installs missing web dependencies on first run. Pure planner tests use
stream dictionaries; integration tests generate real media with ffmpeg. Tag-editing
tests skip if mkvpropedit is unavailable. cryptography is the only Python runtime
dependency. After dependency changes, update and commit uv.lock.
See web development, browser probes and UI review tools for frontend commands and conventions.
MIT. Trackstarr is independent of Radarr, Sonarr, Plex, Jellyfin, Emby and IMDb.
The container image ships ffmpeg (GPL) and MKVToolNix (GPL), run as separate programs. Service logos come from Dashboard Icons (Apache 2.0) and Simple Icons (CC0). Each mark stays its owner's trademark.