The missing *arr for your media server. Convertarr plugs into Sonarr and Radarr, ffprobes every file, and pre-encodes anything Jellyfin or Plex would otherwise have to transcode at playback — so streaming stays direct-play. Subtitles, attachments, chapters, and matching audio tracks are stream-copied bit-for-bit; only the offending streams get re-encoded.
- Sonarr + Radarr integration — point at your existing instances, browse the library with codec/format filters and global search, click rescan, watch the queue fill.
- Visual workflow editor — build conversion rules on a drag-and-drop node canvas. Conditions on source codec, container, resolution, or audio select which files match; the workflow's video + audio targets drive the encode. No workflows defined = rescan is a no-op until you create one.
- Triggers — feed a workflow manually (the Rescan button) or on a schedule with a Cron trigger that walks your library paths and re-ingests matched files automatically.
- Stream actions — beyond codec conversion, a workflow can rename subtitle tracks to clean, canonical titles (SDH / Forced / Signs & Songs detection) and enforce a per-branch Time Limit circuit-breaker.
- Hardware acceleration — auto-detects NVENC (NVIDIA), VAAPI (Intel/AMD), QSV (Intel), AMF (AMD), or falls back to libx265 (CPU). Full-GPU pipeline keeps frames in VRAM end-to-end where supported.
- Multi-node — pair a second Convertarr instance as a worker with a short, confirm-on-screen handshake. The host owns the queue; workers pull jobs over HTTP and run them on their own hardware. Each worker reports its own concurrency cap and encoder.
- Live dashboard — running jobs show progress, ETA, fps, speed, and an
AV1 → HEVCcodec chip so you can see exactly what's being transformed. - File indexer — a background ffprobe cache (
Settings → System → Indexer) keeps library filters and cron triggers fast without re-probing on every request. - History + statistics —
/system/statisticscharts codec, container, and resolution distribution across your library;/system/aboutshows version and uptime. - Backup / restore — export every user-editable setting (instances, path mappings, workflows, saved filters) as JSON and import it on a fresh install. Instance-identity secrets (app API key, session secret, password hash, pairing tokens) are redacted and never overwritten on import.
- Secure by default — a fresh install forces a
/setupadmin account before it serves anything; the machine-to-machine API is always API-key gated regardless of UI auth mode.
Grab the sample docker-compose.yml (NVIDIA-by-default; VAAPI / CPU notes inline at the bottom of the file), drop it next to a config/ folder, and:
docker compose up -dAdjust:
- Volumes — bind your media at the same path Sonarr/Radarr uses internally. Convertarr receives the *arr-relative path and opens it directly; mismatched paths force you to set up Path Mappings on every *arr instance.
PUID/PGID— set to the user that owns your media bind-mounts.0/0runs the container as root if you can't or won't chown.- GPU — see "Hardware acceleration" below.
Open http://localhost:6565. On first run you'll be sent to /setup to create an admin username + password — Convertarr won't serve protected pages until you do. Then add your Sonarr/Radarr instances under Settings → Sonarr / Radarr, define a workflow under Settings → Workflows, and click Rescan on a series or movie.
Prebuilt images are published to https://github.com/OPvault/convertarr/pkgs/container/convertarr on every push to main and every v* tag. :latest follows the newest build on main; pin a specific release with :1.1.0 (or the sliding :1 / :1.1 tags) for reproducible deploys.
For development or environments where Docker isn't available. Requires Python 3.12+, ffmpeg, and ffprobe.
git clone https://github.com/OPvault/convertarr
cd convertarr
./run.shrun.sh creates the venv on first run, installs the package in editable mode, and launches Convertarr on 0.0.0.0:6565. Pass --no-reload for production-style runs. As with Docker, first run lands on /setup.
State (DB, logs, runtime settings) lives in data/ next to the source tree. Override with CONVERTARR_DATA_DIR=/some/path.
A workflow is a "when the source matches these conditions, target these codecs (and run these actions)" rule. Workflows are the only path to a conversion — Convertarr runs nothing until at least one is defined. Build them on the node canvas under Settings → Workflows.
You assemble a workflow from nodes on the editor canvas:
- Trigger — Manual (the Rescan button, the implicit default) and/or Cron (a recurring filesystem walk of configured library paths). A workflow with no cron trigger only runs when you rescan.
- Conditions (the Match node) — zero or more clauses ANDed/ORed together. An empty list is a catch-all.
- Codec targets — Video Codec (
copy,h264,hevc,av1) and Audio Codec (copy,aac,ac3,eac3,opus,flac). - Actions — optional extra steps: Rename Subtitles (rewrite messy subtitle track titles to canonical names) and Time Limit (a per-branch wall-clock circuit-breaker).
- Priority — lower number wins. The first enabled workflow whose conditions match is chosen and the rest are skipped. Within a workflow, branches are evaluated top to bottom and the first matching branch wins.
A workflow can hold multiple branches (each its own condition set + targets + actions), so one workflow can, say, send AV1 to HEVC on one branch and FLAC to AAC on another.
| Field | Type | Source | Notes |
|---|---|---|---|
video_codec |
string | primary video stream's codec_name |
attached_pic (cover art) is ignored |
container |
string | ffprobe format_name, normalised |
matroska → mkv, mov,mp4,m4a → mp4 |
resolution |
number | primary video stream height | e.g. 1080, 2160 |
audio_codec |
string | set of every audio stream's codec | is matches when any audio track is that codec |
audio_channels |
number | max channels across audio streams | a 5.1 + stereo file evaluates to 6 |
is/is not— exact match. Multi-value:ismatches if any value matches;is notrequires none of them match.contains/does not contain— substring (strings only). Useful for catching variants likempeg2video.is greater than/is less than— numeric (height, channels).
Clauses are joined with and (default) or or. Standard precedence — A and B or C and D becomes (A and B) or (C and D). A workflow branch matches when any AND-group passes.
The matched branch's video / audio targets become the per-file plan. Convertarr then walks every stream and decides:
- Video (non-cover-art): re-encode if the source codec doesn't equal the target, copy otherwise.
h264/avc/x264are treated as the same codec — same forhevc/h265/x265— so a "target hevc" rule on an already-hevc file is a no-op even if the source codec is namedx265. - Audio: re-encode the streams that aren't the target codec; the rest are stream-copied.
- Subtitles: copied bit-for-bit — unless the branch has a Rename Subtitles action, which rewrites their titles in place.
- Attachments (fonts), chapters, data: always copied.
- Cover art / poster jpegs (dispositioned
attached_pic): always copied.
If every stream ends up copy (and there's no subtitle rename to do), no Job is queued — the rescan is treated as "nothing to do" rather than firing a remux.
A real-world catch-all that normalises everything into HEVC video + AAC audio. This is the branches shape you'd see in a /system/backup export:
{
"name": "Normalize to HEVC + AAC",
"enabled": true,
"priority": 100,
"branches": [
{
"conditions": [
{ "field": "video_codec", "op": "equal",
"value": ["h264", "av1", "vp9", "mpeg4", "mpeg2video", "vc1"],
"connector": "and" },
{ "field": "audio_codec", "op": "equal",
"value": ["flac"],
"connector": "or" }
],
"target_video_codec": "hevc",
"target_audio_codec": "aac",
"actions": []
}
],
"triggers": []
}How the branch reads: the or connector on the second clause splits the conditions into two groups, so it matches when either is true:
- Video group — primary video codec is one of
h264,av1,vp9,mpeg4,mpeg2video, orvc1(i.e. anything that's not already HEVC). - Audio group — any audio track is FLAC.
Anything matching either group gets re-encoded to HEVC video + AAC audio; tracks that already match the target are stream-copied (so a file with HEVC video but a FLAC track only re-encodes the FLAC). Matching files that are already HEVC + AAC across the board produce no work.
You normally build this clause-by-clause on the editor canvas; the JSON above is what's stored in the DB and exported. (Older flat exports — conditions / target_video_codec / target_audio_codec at the top level — still import; they're wrapped into a single branch.)
Add a Cron trigger node to run a workflow on a schedule instead of (or in addition to) the Rescan button. You give it a cron expression and one or more library paths; on each fire Convertarr reads its file index for those paths and re-ingests matched files through the workflow's branches. Trigger paths are contained to your configured library roots — a workflow can't point the scheduler at arbitrary parts of the filesystem.
Files discovered by a cron trigger that don't map to any known Sonarr/Radarr entry land in Activity → Unlinked (operator-facing, never auto-queued) rather than the encode queue.
The Rescan button on a series/movie page lets you pick a workflow from a dropdown. The chosen workflow is still evaluated against the file's conditions — picking "Convert AV1 to HEVC" on an HEVC file won't fire it. This way you can re-trigger a specific rule without rearranging priorities.
The image bundles ffmpeg with libx265 (CPU) plus VAAPI / NVENC / QSV support. The host needs to expose its GPU into the container:
| GPU | Compose |
|---|---|
| NVIDIA | deploy.resources.reservations.devices block with capabilities: [gpu, compute, utility, video]. Requires nvidia-container-toolkit on the host. |
| Intel / AMD VAAPI | devices: ["/dev/dri:/dev/dri"] + group_add: ["video", "render"] |
| CPU only | nothing — libx265 always works. |
Convertarr auto-detects the best available encoder at startup. Override per-node from Settings → Nodes if it picks the wrong one.
Note: AV1 NVDEC requires Ada Lovelace (RTX 4060+); on older NVIDIA cards Convertarr automatically SW-decodes AV1 sources. 10-bit H.264 (Hi10P) is also SW-decoded on VAAPI/QSV because most implementations don't support it.
Run Convertarr on a second machine the same way (Docker or bare-metal) and complete its /setup. Then pair it with a short, Wii-U-style confirmation handshake:
- On the host's Settings → Nodes page, enter the worker's address and the worker's API key (both under that worker's Settings → General). Click Begin pairing.
- The worker shows four coloured shapes on its own Settings → Nodes screen.
- Tap that same sequence into the host's confirm page.
On a match, the host issues the worker a scoped, revocable per-node token (it never ships its own master API key), the worker switches into worker-mode within a few seconds, and it starts pulling jobs. The four-shape code is the out-of-band human check — it never crosses the API, so knowing the worker's API key alone isn't enough to pair.
Each worker decides its own Max concurrent jobs (Settings → General on that machine; 0 = host-only / paused). Workers configure their own Sonarr/Radarr Path Mappings, so the host can dispatch *arr-relative paths and the worker resolves them locally — useful when host and worker have different mount layouts. delete-originals is a worker-local policy: the host can't make a worker delete files its own settings wouldn't.
Disconnect with the Disconnect from host button on the worker, or Unpair from the host's Nodes page.
All runtime config is editable from the web UI under Settings:
- General — auth (forms is the default;
nonerequires an explicit opt-in env var, see below), API key, max concurrent jobs (this node), delete-originals toggle, encoder override. - Sonarr / Radarr — instances + path mappings.
- Workflows — the node-canvas editor for conversion rules + triggers.
- Nodes — pair / unpair workers, view their encoder + version.
- System — Logs, Statistics, Indexer (file-index status + manual sweep/reindex), Backup, About.
A handful of immutable settings live in env vars (prefix CONVERTARR_), useful for headless setups:
CONVERTARR_DATA_DIR— where the DB / logs / settings live (/configin Docker).CONVERTARR_DB_URL— SQLAlchemy DB URL override.CONVERTARR_HOST_URL_HINT— overrides the host URL sent to workers when pairing.CONVERTARR_ALLOW_NO_AUTH=1— the only way to honour a storedauth_method = none. Without it, a storednoneis treated as forms auth, so a default install on0.0.0.0is never anonymous. Set this only on a trusted, isolated network.
Convertarr is in active development (current version 1.1.0). The default flow is non-destructive — failed/cancelled encodes never touch the original — but always confirm with a test library first. Originals are moved to a sibling .convertarr-backup/ folder when delete-originals is off.
Source: https://github.com/OPvault/convertarr · Issues: https://github.com/OPvault/convertarr/issues · Security: see SECURITY.md