- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Series counterpart of radarr-recode: a Custom Script queues every 1080p import and every 720p import not matching the x265 custom format, and a host-side worker recodes them, rescans, re-monitors and tells Jellyfin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| README.md | ||
| smart-recode-enqueue | ||
| sonarr-recode | ||
sonarr-recode
Post-processing for Sonarr imports. Every imported 1080p
episode, and every 720p episode that doesn't match the x265 custom format, is:
- re-encoded with
smart-recode(720p HEVC on the GPU), replacing the original, - rescanned in Sonarr, so it tracks the new file,
- set back to monitored,
- announced to Jellyfin.
720p x265 files are left as they are. This is the series counterpart of radarr-recode.
Two scripts share the work, because Sonarr runs in a container that has none of the tools the encode needs (fish, gum, the VAAPI ffmpeg, the GPU):
| Script | Runs | Job |
|---|---|---|
smart-recode-enqueue |
inside the Sonarr container, as a Custom Script connection | appends eligible imports to a queue file in Sonarr's /config |
sonarr-recode |
on the host, started by a systemd path unit when the queue exists | works through the queue: steps 1–4 above |
smart-recode-enqueue
smart-recode-enqueue # called by Sonarr, no arguments
Reads Sonarr's environment variables:
sonarr_eventtype=Test(Sonarr's "Test" button): succeeds only if/config/smart-recodeexists.sonarr_eventtype=Download(new imports and upgrades): appends<series id>\t<episode ids>\t<path>to/config/smart-recode/queue, unless the file is one of these:File Why it is skipped *.yify.mp4already recoded HDR or Dolby Vision recode-video-yify-gpudoes no tonemapping, so colours would come out wrong720p matching the x265custom formatalready the wanted result neither 720p nor 1080p outside what this recodes
The format is matched by name, so renaming x265 in Sonarr means changing this
script too.
Install it somewhere under the container's /config and add it in Sonarr under
Settings → Connect → Custom Script, with On File Import and
On File Upgrade enabled.
sonarr-recode
sonarr-recode --queue-dir DIR --series-dir DIR
--config CONFIG_XML --port PORT
[--jellyfin-url URL --jellyfin-farm-dir DIR]
| Option | Meaning |
|---|---|
--queue-dir |
host path of the container's /config/smart-recode |
--series-dir |
host path of the container's /tv |
--config |
Sonarr's config.xml, for its API key |
--port |
Sonarr's port on localhost |
--jellyfin-url |
Jellyfin's base URL (optional) |
--jellyfin-farm-dir |
the series farm as Jellyfin sees it (optional) |
Environment:
JELLYFIN_API_KEY: the key for Jellyfin's API. Kept out of the command line, wherepswould show it. Without it, or without the two Jellyfin options, step 4 is skipped with a log message.
sonarr-recode loops until the queue is empty, then exits.
What happens to each queued file
The /tv/ prefix is mapped to --series-dir. A file that no longer exists is
skipped, and so is a *.yify.mp4.
- Recode.
smart-recode --yesruns in a.sonarr-recodefolder next to the file, through a symlink to it. The encoder writes its output in stages, and Sonarr's scheduled refresh skips dot-folders, so it can't import a half-written file. The original is deleted only when smart-recode succeeds and exactly one.yify.mp4came out and was moved next to it. - Rescan. Sonarr is asked to
RescanSeries, so it swaps the deleted original for the.yify.mp4, and sonarr-recode waits for that to finish (up to 10 minutes). - Monitor. With Sonarr's "unmonitor deleted episodes" setting on, the rescan
unmonitors episodes whose file went missing.
PUT episode/monitorsets them back. - Jellyfin.
POST <jellyfin-url>/Library/Media/Updatedwith<jellyfin-farm-dir>/<language>/<series>.
A failure in steps 2, 3 or 4 is logged and doesn't stop the rest.
Queue files
All in --queue-dir:
| File | Purpose |
|---|---|
queue |
written by smart-recode-enqueue |
queue.incoming |
queue, moved aside while sonarr-recode takes it over |
queue.work |
the lines being processed; removed once all are handled |
failed |
lines whose recode failed; their originals were kept |
Retrying a failed recode
Lines in failed are not retried automatically. Once the cause is fixed, put the
line back in the queue and remove it from failed:
cat <queue-dir>/failed >> <queue-dir>/queue
rm <queue-dir>/failed
Running it under systemd
Use a path unit with PathExists, not PathModified: systemd checks again when
the service exits, so a line queued during a run starts another one. Watch both
queue and queue.work, so an interrupted run is retried. Logs go to the
journal: journalctl -u sonarr-recode.
On sulaco, the units, the env file and the Sonarr side (profile, delay profile,
custom formats, the Custom Script connection) are set up by the sonarr.yml
playbook in sulaco-playbook,
which also checks out this repo.
Requirements
On the host:
- fish 3.5 or later (for
path) smart-recodeand what it needs (recode-video-yify-gpu, the VAAPI ffmpeg,gum)curl,jq- a series share that supports symlinks and dot-folders
In the container: bash.