Recyclarr Config Generator
Generates a recyclarr configuration from live Sonarr and Radarr instances, instead of hand-writing one and syncing it the other way.
Point it at each *arr, and it reads what is already configured — quality profiles,
custom format scores, quality definition sizes, naming formats — and renders the
equivalent recyclarr.yml. Custom formats are matched back to their TRaSH Guides
trash_id by name, so the output is guide-backed and safe to recyclarr sync.
This is a one-way snapshot tool. Once the config exists, recyclarr becomes the source of truth and you edit the YAML, not the UI.
Requirements
Section titled “Requirements”- Python 3.9+
pip install -r requirements.txt(requests,PyYAML)- Network access to each *arr, and to GitHub once (the TRaSH Guides tarball is cached
under
~/.cache/recyclarr-config-generator/and refreshed daily)
Only Sonarr and Radarr are supported. recyclarr does not manage Prowlarr, Lidarr, Readarr or Bazarr, so those stacks have no config to generate.
cd utils/recyclarr-config-generatorpip install -r requirements.txt
cp instances.example.yml instances.yml # then edit: one entry per *arrpython generate_recyclarr_config.py # writes recyclarr.yml + secrets.ymlPreview without writing anything:
python generate_recyclarr_config.py --stdoutSupplying API keys
Section titled “Supplying API keys”Each *arr’s key is under Settings → General → API Key.
Keys should not live in instances.yml. Leave api_key out and the generator reads an
environment variable — api_key_env if set, otherwise <KEY>_API_KEY derived from the
instance key. That fits the repo’s BWS convention:
bws run --access-token "$BWS_ACCESS_TOKEN" -- python generate_recyclarr_config.py--api-key-mode controls how the key is referenced in the output:
| Mode | Output | Notes |
|---|---|---|
secret (default) | api_key: !secret sonarr_... | also writes secrets.yml (mode 600) |
env | api_key: !env_var SONARR_.. | recyclarr reads it from its own environment |
inline | api_key: "abc123..." | plain text in the config — avoid committing |
instances.yml, secrets.yml, recyclarr.yml and configs/ are all gitignored here.
Useful flags
Section titled “Useful flags”| Flag | Effect |
|---|---|
--split | one file per instance in --output dir, for recyclarr’s configs |
--reset-unmatched-scores | add reset_unmatched_scores.enabled to every profile |
--include-zero-scores | also emit custom formats currently scored 0 |
--no-modernize | keep retired formats as comments instead of migrating them |
--no-quality-definition | skip the quality_definition block |
--no-media-naming | skip the media_naming block |
--insecure | skip TLS verification (internal Step CA certs) |
--refresh-guides | force a re-download of the TRaSH Guides |
--guides-tarball PATH | use a local Guides tarball; no network needed |
Per-instance, verify_ssl: false does the same as --insecure for one entry.
What it reads, and what it emits
Section titled “What it reads, and what it emits”| From the API | Into the config |
|---|---|
qualityprofile | quality_profiles — upgrade/cutoff, min scores, enabled qualities and groups (top-first) |
qualityprofile | custom_formats — trash_ids grouped by identical per-profile score assignments |
qualitydefinition | quality_definition — closest guide size set, plus per-quality overrides where the instance differs |
config/naming | media_naming — the guide key whose format string matches exactly |
Every emitted trash_id carries the custom format’s name as a trailing comment,
matching the convention in the recyclarr docs.
What it will not do
Section titled “What it will not do”Custom formats you invented yourself are skipped. recyclarr only syncs formats that exist in the TRaSH Guides, so anything with no counterpart is listed as a comment at the end of that instance’s block and stays hand-managed in the UI. The same applies to naming formats you customised — the guide key cannot be inferred, so the raw format is left as a comment.
Profiles are emitted with the qualities that are currently enabled, highest-first. Anything not listed gets disabled by recyclarr on sync, which reproduces the profile as it stands.
How custom formats are matched
Section titled “How custom formats are matched”Instances keep whatever name a format was imported under, while the guide renames things over time, so matching runs in three stages and stops at the first hit:
- Exact name, case- and punctuation-insensitive —
DTS-Xfinds the guide’sDTS X. - Identical specifications — the matching rules themselves fingerprint the format,
so it is found however far the name has drifted. This is what recognises
DV (WEBDL)as the guide’sDV (w/o HDR fallback). - Normalized name — a trailing parenthetical is dropped and
v2reads as2, which recoversAnime BD Tier 01 (Top SeaDex Muxers)→Anime BD Tier 01andRepack v2→Repack2.
Stage 3 alone is not trustworthy: the guide retires names that unrelated formats later
sit close to. So a stage-3 hit is only accepted when both formats are built from the
same kinds of rules. HDR (undefined) matches release groups while the guide’s HDR
matches title tags — near-identical names, different formats — so that one is rejected
and reported rather than silently mis-scored.
Anything matched by stage 2 or 3 is printed as it happens, so a rename is never applied without telling you.
Retired formats are migrated, not dropped
Section titled “Retired formats are migrated, not dropped”TRaSH replaced the per-flavour HDR formats (DV, DV HDR10, DV HLG, DV SDR,
HDR10, HDR10+, PQ, HDR (undefined)) with a single HDR format that matches any
HDR release, plus additive boost formats for the flavours worth paying extra for.
Rather than emitting those as dead comments, the generator rewrites them into the
current model. A boost is scored at the difference between the old flavour’s score and
the score the profile gives plain HDR, so the totals a profile was tuned around
survive the migration:
DV, DV HDR10, DV HDR10+, DV HLG, DV SDR at 1500 -> HDR 500 + DV Boost 1000HDR10+ at 600 -> HDR 500 + HDR10+ Boost 100HDR10, PQ, HDR (undefined) at 500 -> dropped, already covered by HDR 500An existing HDR score always wins as the base, since that is what the profile already
asks for. If a flavour scored no higher than HDR did, no boost applies and it is
simply dropped. Every substitution is printed while it runs and recorded as a comment
block in the config, so the change is auditable after the fact. --no-modernize turns
the whole pass off and reverts to listing them as unmatched.
Formats with no clean equivalent are never guessed at. Sonarr’s retired
UHD Streaming Cut looks like the current UHD Streaming Boost, but it covers a
different set of services (AMZN/HMAX/Stan vs DSNP/HMAX/NF) and the guide scores it
positively where the old one was usually negative — so it is reported for you to decide,
not migrated.
After generating
Section titled “After generating”Always preview before the first sync — it shows exactly what recyclarr would change:
recyclarr sync --previewExpect small diffs on the first run: scores the guide has but your instance never set, and qualities whose sizes were left at *arr defaults. Review them, then sync for real.