SKYHOUSE.dev Journal

Maintaining the Cloud Fortress

Four Problems Wearing One Costume

Why: With the alert noise fixed, Damien looked at what the monitors were actually pointing at and found the arrs had dumped roughly 36 duplicate films onto the server over a couple of weeks — and separately suspected Sonarr had a year-suffix problem nobody could measure, because Plex offers duplicate detection for movie libraries and none at all for TV.

This entry covers clearing the last of the rdt-client orphans, a genuine config divergence between the two rdt-client instances, and then the archaeology: what the duplicates actually were, which turned out to be four unrelated mechanisms that look identical from the outside.

1. Clearing the orphans, and proving the loop

Seven errored records remained in the movies rdt-client — six DIFF_ISSUE: Download already queued from 2026-08-04 that the previous sweep skipped because they were under its 24h age gate, plus one All files excluded. Cleared with the flags verified on 2026-08-04 (deleteData only): 44 records → 37, 0 errored, with /media/plex1/Movies byte-identical at 1,599 directories before and after.

The immediate re-run of radarr-queue-cleanup.py is the part worth recording, because it closed the loop that has been open since 2026-08-04: no DID NOT STICK lines. Removals that had bounced back every time for two days now held. The arr-level cleanup was never broken; it was cleaning a layer that rdt-client immediately overwrote.

2. A third variant the tooling did not cover

Two Freeway entries then refused to clear. Radarr was right to refuse them: it already holds Freeway.1996.2160p.UHD.BluRay, and both queue items were 1080p, declined with "Not an upgrade for existing movie file." But rdtclient-cleanup.py only deletes records carrying an error, and these had none — they were completed, healthy, and permanently unimportable. So the queue cleanup de-queued them without blocklisting (correct: the releases are fine, we simply do not want them), rdt-client kept the records, and Radarr rebuilt the queue entries from them. Forever, with nothing wrong anywhere.

Cleared by hand. The tooling gap is real and remains open: completed but unimportable is a third category alongside errored and in-flight, and nothing automates it yet.

3. The two rdt-client instances were not configured alike

The TV instance had 0 errored records against Movies' 7, which was worth explaining rather than assuming. Reading both settings APIs:

settingTV (healthy)Movies
PreferZippedDownloadsFalseTrue
OnlyDownloadAvailableFilesTrueFalse
TorrentRetryAttempts13
DownloadRetryAttempts35

The first two were aligned to TV's values; the retry counts were deliberately left alone so this stays a two-variable test. PreferZippedDownloads is the prime suspect for All files excluded: TorBox offered a 2.8 GB .mkv with Selected: true and rdt-client created no download rows at all, with every DownloadLink null.

A false lead worth recording. Reading the SQLite settings table directly showed HostDownloadAction as "DownloadAll" on TV and "0" on Movies, which looked like a third divergence. The API showed both resolve to Download all files to host — the same value with two serialisations. The database is not the API. Two instances of the same app can persist identical settings differently depending on the version that wrote them.

4. The duplicates: four mechanisms, not one

Plex reports 36 duplicated titles in the Movies library. They have four unrelated causes:

A wrong inference, corrected the same session. The first read of the pairs was that the tracked copy always carried "Radarr's rename format" and the untracked one a raw release name. Then config/naming showed renameMovies = False — Radarr does not rename anything on this box. Both filenames in every pair are the release's own; the two conventions come from the same source group publishing under two names. The folder format is what is templated, not the file.

5. The TV audit — the tool Plex does not have

Plex's duplicate view exists only for movie libraries, so a duplicated episode is invisible. ~/bin/tv-duplicate-audit.py (read-only) finds three distinct conditions across all three TV roots:

scanned 386 series folders across 3 roots
SPLIT SERIES        5
DUPLICATE EPISODES  400   (604.4 GB in those files, 210.0 GB recoverable)
UNTRACKED FOLDERS   91    (TV_Old 78, Limited Run 12, live TV root 1)
SONARR ENTRIES WITH NO FOLDER ON DISK   14

Bad Sisters is the clearest case and the most expensive: it exists as both Bad Sisters and Bad Sisters (2022), 18 duplicated episodes, 41.1 GB. President Curtis/President Curtis (2026) repeats it; Kids In The Hall (2022) vs Kids in the Hall adds capitalisation on top — though that pair is a false positive: the TV_Old copy is the 1988 original and the other is the 2022 revival. Genuinely different shows. Malcolm in the Middle (2000) vs (2025) is the same kind of false positive. The normaliser strips years in order to catch Bad Sisters, so it cannot distinguish a revival from a split; that is a judgement the report hands to a human rather than a bug to fix.

The audit then asks Sonarr which copy it tracks, because that is what makes a deletion safe: removing the tracked copy leaves the episode missing and a monitored series simply grabs it again, which is how the library reached this state. 36 of 41 affected series have a tracked copy of every duplicated episode — 228.9 GB of untracked duplicates are safe to delete. The remaining 5 are series Sonarr does not manage at all, so there is no re-download risk either; the choice of which copy to keep is simply the operator's.

The rule is "delete untracked", never "delete the smaller one". The obvious automation — drop the lower-quality copy of each pair — is actively unsafe here. Across the 387 duplicated episodes that have a tracked copy, the tracked copy is not the largest in 104 of them. A size-based rule would therefore delete the file Sonarr depends on 27% of the time, and a monitored series re-grabs every one. Size is not quality either: a 480p DVD x265 rip routinely outweighs a 720p WEBRip. Keeping the tracked copy everywhere forgoes only 11.9 GB of larger untracked files — a cheap price for a rule that cannot bite. Where the bigger copy is genuinely wanted, Manual Import it first so it becomes tracked, then delete the other.

The audit therefore emits a reviewable manifest rather than acting: ~/docs/tv-safe-delete-20260805.sh, 391 rm lines, 216.6 GB. Verified before being handed over — 0 targets tracked by Sonarr, 0 non-existent, 0 duplicate lines, and 0 episodes that would lose every copy. (A first spot-check appeared to show Mr Show losing its last copy; that check was directory-scoped and Mr Show's duplicate lives in a different root. Re-verified group-scoped, it is fine — the plex2 copy survives and Plex's TV library spans both roots.)

Tuned against real data, twice. The first run reported 737 duplicate episodes. It was counting Featurettes/Deleted Scenes files, which are named with the episode code they belong to — The Office alone contributed 174 phantoms. Filtering extras directories (2,232 files) brought it to 400. A second defect: the recoverable-space figure came out negative, because the running total accumulated only the sampled episodes that got printed while the subtraction used a whole-library sum. Both are the same lesson the pipeline probe taught on 2026-08-04: a checker validated only against tidy data is validated wrong.

6. The finding that answers the workflow question

Damien's manual routine has been: move a finished show or a mis-categorised film to its proper library by hand, then delete the entry from Sonarr/Radarr. That routine is forced by configuration, and the configuration is one field wide.

Sonarr root folders : /data/TV                 (only)
Radarr root folders : /data/Movies             (only)

but the containers can already SEE:
  sonarr  ->  /data/TV, /data/Limited Run          (/media/plex2 is mounted)
  radarr  ->  /data/Movies, /data/Live, /data/Standup,
              /data/Documentaries, /data/Rare, /data/Arthouse

Radarr can already reach every destination Damien has been moving films to by hand. They are simply not registered as root folders. Adding them means a film can be moved inside Radarr (Edit → Root Folder), which relocates the file and keeps the entry — so the app never loses track of a film it already has, which is mechanism #1 and #2 above at the source. Sonarr can do the same for Limited Run today; TV_Old needs /media/plex3 mounted into the container first, which is a compose change.

This reframes "delete the entry after moving" as the thing to stop doing. An entry deleted while the file stays on disk is exactly the state that produces a duplicate the next time the title is added.

The duplicates were never one bug. They were a bulk import that could not parse legacy filenames, a folder template keyed on a year that moves, a release published twice under two names, and a pile of the operator's own Blu-ray rips — all of which look identical in a folder listing.

← Back to Admin Hub