Conductarr
🎼 Priority-based download queue orchestrator for Radarr, Sonarr & SABnzbd — automatically manages and reorders your download queue across multiple priority tiers.
What it does
Conductarr sits between Radarr/Sonarr and SABnzbd and does two things:
- Queue ordering — every SABnzbd job is mapped to a virtual queue (e.g. user requests, other, upgrades) via tag matchers. The SABnzbd queue is kept in priority order, and by default only the top job downloads while the rest are paused. Conductarr-initiated upgrades always sort below everything else.
- Gradual library upgrades — for queues with an
upgradesection, conductarr walks your whole library, finds movies/episodes whose file does not yet satisfy youraccept_conditions(e.g. the German DL custom format), runs an interactive search through the Arr, and grabs the best release that is a real improvement — one item at a time, rate-limited, and only when nothing else is downloading.
Radarr/Sonarr themselves never need to upgrade anything; conductarr decides what to grab, the Arr does the actual import.
Safety model
Conductarr is built so that it cannot damage your library or disturb normal downloads:
| Guarantee | How |
|---|---|
| Never deletes anything | The clients have no delete/remove methods at all (enforced by a unit test). The only writes are: SABnzbd switch/pause/resume of single jobs, and Arr POST /release for one selected release. |
| Never touches torrents | Only releases with protocol: usenet are considered; unknown protocols are rejected. |
| Never grabs the wrong item | Releases the Arr mapped to a different movie/episode are dropped. Season packs are used when they are the best option, but only if the Arr confirmed they contain the searched episode; the other episodes of a grabbed pack are not searched meanwhile (allow_season_packs: false disables packs). |
| Respects Arr rejections | Only the "not an upgrade / cutoff met" family of rejections (which exist because the Arr itself may not upgrade) is overridden. Anything else — wrong quality, language, size, restricted terms, "already in queue" — blocks the release. |
| No downgrades | The release must beat the current custom-format score (min_score_increase) and may not have a lower resolution than the current file. |
| Never retries a bad release | Every grabbed release title is remembered per item and never grabbed again (e.g. after a failed import). |
| User downloads first | Upgrades wait while any other job is in SABnzbd (defer_to_other_downloads), sort last (upgrades_last) and are limited by max_active. Jobs you paused yourself are never resumed; everything conductarr paused is resumed on shutdown (SIGTERM). |
| Indexer friendly | At most one search per search_interval and max_searches_per_day per upgrade queue, persisted across restarts. |
| Disk safety | No new upgrades below min_free_space_gb (as reported by SABnzbd) or while the SABnzbd queue is paused. |
| Stable | Every HTTP call has a timeout, every cycle has a watchdog, the queue loop never waits for an indexer search, and a heartbeat file backs the Docker HEALTHCHECK. |
| Observe first | dry_run: true runs everything but never reorders, pauses, resumes or grabs. |
Quick start (Docker Compose)
services:
conductarr:
image: ghcr.io/eulemitkeule/conductarr:latest
container_name: conductarr
restart: unless-stopped
environment:
PUID: 1000
PGID: 1000
UMASK: "022"
TZ: Europe/Berlin
LOG_LEVEL: info
volumes:
- /path/to/conductarr/config:/config
command: conductarr watch
Conductarr only needs its own config directory — it never needs access to your media or download folders.
Place a conductarr.yml inside the config directory; config.example.yml documents every option. Unknown keys are rejected at startup, so typos cannot silently disable a safety setting.
Recommended rollout
- Start with
dry_run: trueand watch the log for a day: every "would grab", "would pause" and "would move" line shows exactly what conductarr would do. - Try a single item:
conductarr debug-upgrades --source radarr --id 42(performs one real indexer search, grabs nothing). - Switch to
dry_run: falsewithmax_active: 1.
Commands
| Command | Purpose |
|---|---|
conductarr watch |
Run the queue and upgrade loops. |
conductarr status |
Read-only overview: candidates per queue, grabs in flight, upgrades done, search budget used. |
conductarr debug-upgrades [--source radarr] [--id 42] |
Evaluate the next candidate (or one item) and print every filter step. No writes, no grab. |
conductarr healthcheck |
Exit 0 if the queue loop completed a cycle recently (used by the Docker HEALTHCHECK). |
How it works
Two independent loops run every poll_interval:
Queue loop
- Read the SABnzbd queue.
- Map new
nzo_ids to Radarr/Sonarr items via their queues (one fetch per cycle). Jobs that could not be mapped are retried every minute. - Finalise jobs that left the queue once SABnzbd history reports
Completed/Failed(post-processing is waited for). Jobs deleted without history are released after 30 minutes. - Reorder by virtual-queue rank and keep one job active.
Upgrade loop (per upgrade queue)
- Rescan the libraries every
rescan_interval: every movie/episode with a file becomes a candidate, including items that were first downloaded as a user request. - Skip if
max_activegrabs are in flight, SABnzbd is paused/low on space, other downloads are waiting, or the search budget is used. - Walk candidates from a persisted cursor; items that already satisfy the conditions, have no file or are downloading are skipped without searching.
- Search the first real candidate, run the selection pipeline (usenet → conditions → mapping → rejections → downloadable → blocklist → resolution → score) and grab the best remaining release.
The grab is recorded in the database before the Arr is asked to grab it, so a download can never go untracked.
Development
uv sync --all-groups
uv run pytest tests/unit # fast, no services needed
uv run pytest tests/integration # starts mock SABnzbd/Radarr/Sonarr via docker compose
uv run ruff check . && uv run ty check
Metadata
Release files for conductarr 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| conductarr-3.0.0.tar.gz | 131.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| conductarr-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 186.7 kB
Release files / conductarr-3.0.0.tar.gz
| Download URL | conductarr-3.0.0.tar.gz |
|---|---|
| Size | 131.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
55c493b32d182b49fc04b3082a5409945e6f9d06800080c529a210e1e0a15278
|
|
BLAKE2b-256 checksum How to use checksums |
8bff6fdd7805473cd11fabb78b507c2f8d8fb651014df0aaeac7cd0a2bdea28a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / conductarr-3.0.0-py3-none-any.whl
| Download URL | conductarr-3.0.0-py3-none-any.whl |
|---|---|
| Size | 55.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9ba5f5bebbf7472712feb6a188d598e70982a1b97677119ec199a0ee5e49e495
|
|
BLAKE2b-256 checksum How to use checksums |
5fb8909b58536c48d467c7dd80fb606a595934dcf1f1414e63728bb5679d57cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency log