110 lines
6.3 KiB
Markdown
110 lines
6.3 KiB
Markdown
# compact-tv
|
|
|
|
Sonarr and Radarr each have exactly four quality profiles. Two of them (**Standard** and
|
|
**Mini**) also have their files shrunk by `compact-tv.py`, which runs every night.
|
|
|
|
| Profile | Used for | What gets kept |
|
|
|---|---|---|
|
|
| **Mini** | shows I demote hard | TV: 720p at 800 kbps (~0.42 GB per hour). Movies: 720p at 1500 kbps |
|
|
| **Standard** | **every request from other users (Seerr default)**, and anything I demote | TV: 1080p at 1200 kbps (~0.6 GB per hour). Movies: 1080p at 2500 kbps (~1.2 GB per hour) |
|
|
| **HD** | my own library, unchanged | whatever downloads, up to 1080p |
|
|
| **UHD** | only me, through Seerr's 4K servers | 4K |
|
|
|
|
SD sources keep their resolution, at 600 kbps for TV and 900 kbps for movies. Movies get
|
|
higher rates because dark scenes, grain and action break down first at low bitrates. Nothing is ever
|
|
upscaled. Audio becomes Opus: 96 kbps for stereo, 160 for 5.1, 192 for 7.1. Subtitles,
|
|
chapters and attachments are copied as they are. I picked the rates from sample encodes of
|
|
Shameless S05E06 (VMAF ~91 for Standard, ~86 for Mini).
|
|
|
|
## How the profiles work
|
|
|
|
- **Standard and Mini** are copies of HD that upgrade only toward small releases (see below),
|
|
so Sonarr and Radarr never replace a file with a bigger one.
|
|
- **Small downloads first.** Both profiles prefer small releases when they grab:
|
|
- The "Small Encode" format (MeGusta, PSA, ELiTE, GalaxyTV/RG, YTS/YIFY) scores +3000.
|
|
- LQ scores 0. In HD and UHD it scores -10000, which keeps those groups out of HD.
|
|
- Standard merges HDTV/WEB/Bluray at each resolution, so the format score decides between
|
|
sources rather than the source type.
|
|
- Mini merges 720p and 1080p into one group and adds +1000 for 720p.
|
|
- Many files therefore arrive already small, and the job skips them.
|
|
- **Minimum sizes are shared by every profile.** For 720p I lowered them to 5 MB per minute
|
|
and for 1080p to 6 MB per minute (8 for Bluray-1080p), so small encodes are not rejected.
|
|
HD still never takes the LQ groups.
|
|
- **Seerr:**
|
|
- The "Sonarr - HD" and "Radarr" servers default to Standard. Only my admin account sees
|
|
the profile picker, so other users always get Standard. I pick HD or UHD per request.
|
|
- Other users no longer have "Request 4K".
|
|
- **Configarr is opt-in** (`docker compose --profile configarr up configarr`). Its templates
|
|
would recreate the old TRaSH profiles and size limits.
|
|
- Backups from before all this: `backups/pre-four-profiles-20261028/`. That covers both
|
|
apps' profiles, custom formats and size definitions, which title was on which profile,
|
|
and Seerr's settings. `setup-small-profiles.py` reapplies the Standard and Mini settings.
|
|
|
|
## How the job works
|
|
|
|
The nightly run (00:30 to 08:00) has two phases.
|
|
|
|
**1. Search first.** It asks Sonarr and Radarr to search titles on Standard or Mini that it
|
|
hasn't searched before, biggest first: 5 shows and 20 movies a night, so downloads and
|
|
indexer use stay spread out. Standard and Mini upgrade only toward small releases:
|
|
- Every allowed quality sits in one group, so no resolution or source upgrade can happen.
|
|
- An upgrade needs a custom-format score jump of at least 2500, and upgrades stop at 3000.
|
|
Only gaining "Small Encode" clears that bar.
|
|
|
|
So a search replaces a big file only with a small encode, never with a big HEVC release.
|
|
After that, the apps' regular RSS checks keep picking up small releases on their own.
|
|
|
|
**2. Encode what's left.** A title becomes eligible for encoding 7 days after its search,
|
|
and only its files that are still big get encoded. Files that arrive after the title's
|
|
search work differently:
|
|
- This covers new episodes of ongoing shows, and fresh requests.
|
|
- The first night they're seen, they get their own search (one episode or movie at a time).
|
|
- They're encoded only if they're still big 3 days later.
|
|
|
|
Downloads never wait for any of this, so new episodes and requests land in the library as
|
|
soon as they download. Only encoding waits. For each file, the job encodes to
|
|
`~/.cache/compact-tv/work.mkv` on the NVMe and then checks the result:
|
|
- the duration matches the original
|
|
- the audio and subtitle track counts match
|
|
- the file is smaller
|
|
|
|
It then renames the new file over the original, keeping the same name with `.mkv`, and asks
|
|
Sonarr or Radarr to rescan. Plex picks the change up through its folder watcher, and the
|
|
episode keeps its Plex ID, so watch history stays. If a small release shows up later, the
|
|
app may still replace an encoded file with it, which costs bandwidth, not GPU.
|
|
|
|
Other details:
|
|
- Each output carries a `COMPACT_TIER` tag. A file is skipped if re-encoding would save less
|
|
than 30%. Ruled-out files are cached by size in `state.json`, so they aren't probed every
|
|
night. Search dates and failures are kept there too.
|
|
- A downloaded HEVC file no bigger than twice the target size counts as done. That is the
|
|
small release a search brought in (a MeGusta 1080p episode runs about 2.3 Mbps against
|
|
Standard's 1.2), and it is neither re-encoded nor searched again. The job's own tagged
|
|
outputs are exempt, so Standard to Mini still works.
|
|
- **Skipped:** HDR and Dolby Vision sources (they need tone mapping), subtitle tracks ffprobe
|
|
can't identify, and files that have failed twice.
|
|
- Speed is about 0.19 hours of GPU per hour of 1080p video (NVENC is the bottleneck). The job
|
|
stops starting new encodes at 08:00.
|
|
- `cost-report.py` estimates, per title, the GPU hours the job would need against the size of
|
|
a fresh small download.
|
|
- A `PAUSE` file in this folder makes the nightly run do nothing.
|
|
|
|
## Going back up
|
|
|
|
Moving down works: Standard to Mini re-encodes the Standard copy. Moving up does not
|
|
restore quality. To get quality back, I set the title to HD and search, which redownloads
|
|
it at full size.
|
|
|
|
## Running it
|
|
|
|
./compact-tv.py --dry-run # what tonight would do
|
|
./compact-tv.py --search-all --limit 0 # search every unsearched title now, encode nothing
|
|
touch PAUSE # skip nightly runs until PAUSE is deleted
|
|
./compact-tv.py --series "Shameless (US)" --limit 1
|
|
systemctl status compact-tv.timer compact-tv.service
|
|
tail -f compact-tv.log
|
|
|
|
The installed units are copies of `compact-tv.service` and `compact-tv.timer` from this
|
|
folder. `ExecStart` goes through `/usr/bin/python3` because SELinux won't let systemd
|
|
execute a file labeled as home content.
|