Files
Varaverk/Media/README-Media.md
T
Gmer4Lfe 649a3b7b62 Docs pass: update headers and READMEs for all new scripts
Script headers (6 files):
- Add OPERATIONAL SAFEGUARDS section to all discovery + emby sync scripts
- Add STATE FILES section to all 3 discovery scripts
- Add CONFIGURATION section to emby_to_*_sync.sh
- Rename USAGE → RUNTIME MODES in emby_to_*_sync.sh (doc standard)
- Fix SONARR_DISCOVERY_MONITOR_MODE default doc: "future" → "all"

Media/README-Media.md:
- Remove "WIP prototype" label; describe all 3 discovery scripts
- Add discovery scripts to scripts table and HOW THE SCRIPTS RELATE diagram
- Add weekly discovery schedule block to EXECUTION ORDER section
- Update intro to mention behavior-driven discovery

Media/Manual-Media.md:
- Add configuration reference sections for all 3 discovery scripts
- Add flag reference sections for all 3 discovery scripts

Tools/README-Tools.md:
- Add "Library Sync Bootstrap" category and emby_to_*_sync.sh to scripts table
- Add emby sync scripts to HOW THE SCRIPTS RELATE diagram

Tools/Manual-Tools.md:
- Update emby sync notes: search commands now fire immediately after add

README.md:
- Update Media folder description to include discovery
- Update weekly schedule to show discovery scripts
- Update README table entry for Media
2026-05-20 17:48:30 -04:00

239 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ━━━━━ MEDIA ━━━━━
Library health, consistency, sync, and behavior-driven discovery for a multi-server arr
stack. Correct permissions so arrs can manage files. Junk removal so orphan detection
isn't confused by scene debris. Library sync so every node tracks the same content.
Orphan cleanup against live arr APIs so deleted content actually leaves disk. Emby
notified automatically after every deletion. Weekly discovery adds new music, movies,
and TV shows based on what you actually play — no manual browsing required.
> **These scripts permanently delete files.** The arr cleanup scripts are protected by
> multiple safety layers that must all pass before anything is touched — but dry runs and
> log review are still the right first step on any new system or after any configuration
> change. The testing procedure in Manual-Media.md exists for a reason.
---
## ━━━ THE PROBLEM THAT BUILT THIS ━━━
**Files Owned by Root That Arrs Can't Touch**
Download clients without explicit PUID/PGID write files owned by root. Arrs running as
`nobody:users` cannot rename, move, or delete them. Import fails. Upgrade attempts fail.
The failure is subtle — arr shows the file as managed but can't touch it. You only
discover this when an upgrade is requested and the old version refuses to delete.
The fix: `media_shares_permissions.sh` applies correct ownership daily. Even if a
container is misconfigured, the window is at most 24 hours.
**Scene Junk Confusing Orphan Detection**
Scene releases include `.sfv`, `.nfo`, `.rar`, `.sample` files alongside the actual media.
After extraction and import these are worthless — but they're not tracked by any arr.
They look like orphans. Processing them as orphans means the cleanup output is full of
noise, making it hard to spot actual orphaned media.
The fix: `media_cleaner.sh` runs before any arr cleanup and removes all known junk
patterns first. By the time arr cleanup runs, every untracked file is actual media.
**Deleted Shows and Removed Albums Still on Disk**
When you remove a series from Sonarr and the delete command fails — permission issue,
container wasn't running, path mismatch — the files stay permanently. Over years on an
active library this accumulates significantly.
The fix: arr cleanup scripts query the live API for every tracked file path, walk the
disk, and delete anything absent from the API response that's old enough to be past the
import window.
**Emby Showing Ghost Entries After Cleanup**
After arr cleanup deletes files, Emby still shows them until its next scheduled scan —
potentially hours later. Users see broken entries that produce "file not found" errors.
The fix: `notify_emby_scan()` is called automatically after every deletion. Triggers
Emby's "Clean Missing Files" task immediately.
**No Safety Net on Deletion Size**
A misconfigured root path — pointing cleanup at the wrong directory — means the API
returns zero tracked files for a root that actually contains thousands. Every file walks
as an orphan. Everything gets deleted. This is the catastrophic failure mode.
The fix: `LIDARR/SONARR/RADARR_MAX_DELETE_GB` — if total deletion size exceeds the
limit, the script stops and requires `--i-know-what-im-doing` to proceed. The flag name
is long and annoying by design. It cannot be added by accident.
---
## ━━━ WHAT THIS FOLDER DOES ━━━
Scripts divide into five functional areas:
**Library Foundation**
`media_shares_permissions.sh` — normalize ownership and permissions daily. Runs first in
every maintenance window because arr cleanup depends on correct ownership to delete files.
**Junk Removal**
`media_cleaner.sh` — remove scene debris and tool artifacts before orphan scan. Runs
second, before any arr cleanup, so orphan detection only encounters actual media files.
**Library Sync**
`arr_sync.sh` — full-mesh arr library sync across all nodes. Every node syncs with every
other — union model, no hierarchy. Once arrs agree on what to track, rsync spreads the
actual files. The architectural shift from file-first sync to arr-first sync.
**Orphan Cleanup**
`lidarr_cleanup.sh`, `sonarr_cleanup.sh`, `radarr_cleanup.sh` — API-verified orphan
removal. Five classification categories (TRACKED/PROTECTED/ORPHAN/JUNK/RECENT), seven
safety layers, automatic Emby notification after deletion.
**Database Hygiene**
`radarr_tmdb_removed.sh`, `sonarr_tvdb_removed.sh` — remove entries that upstream
databases have dropped (TMDb/TVDB status="deleted"). These generate health warnings in
arrs and can never be monitored or downloaded. Most are announced-but-never-released
entries. Files are kept by default — most have none.
**Library Enrichment**
`arrs_failed_stalled_recovery.sh` — detect and recover failed imports and stalled
downloads across all arrs. Blocklists the bad release and triggers a re-search — hands-
free overnight recovery.
`lidarr_missing_art.sh` — fetch missing album and artist artwork from fanart.tv and
fallback sources. Never overwrites existing files.
**Discovery**
`playback_aware_lidarr_discovery.sh` — behavior-driven music discovery. Scores your
Emby play history, runs Last.fm getSimilar on top artists, adds the best matches to
Lidarr. 05 meaningful adds per week.
`playback_aware_radarr_discovery.sh` — behavior-driven movie discovery. Scores recently
watched movies, runs TMDB recommendations on seeds, adds top candidates to Radarr.
`playback_aware_sonarr_discovery.sh` — behavior-driven TV discovery. Scores recently
watched series weighted by user diversity, runs TMDB TV recommendations on seeds, adds
top shows to Sonarr. Multi-user design: one person binge-watching does not dominate seeds.
---
## ━━━ EXECUTION ORDER ━━━
Scripts run in multiple contexts — not all are part of the daily maintenance window:
**Daily via `media_management.sh` (MEDIA_MAINTENANCE_JOBS):**
```
1. media_shares_permissions.sh — permissions first — arr cleanup depends on this
2. media_cleaner.sh anime — junk before orphan scan
3. media_cleaner.sh media
4. lidarr_cleanup.sh — after permissions + clean
5. sonarr_cleanup.sh
6. radarr_cleanup.sh
```
**Weekly arr sync (before rsync in weekly_sync_maintenance.sh):**
```
arr_sync.sh — arrs agree on library → rsync then spreads the files
```
**Daily recovery (separate schedule — 5am or every 6hr):**
```
arrs_failed_stalled_recovery.sh
```
**Weekly discovery (WEEKLY_MAINTENANCE_SCRIPTS in master.conf):**
```
playback_aware_lidarr_discovery.sh — score play history → Last.fm similar → add to Lidarr
playback_aware_radarr_discovery.sh — score watch history → TMDB recommendations → add to Radarr
playback_aware_sonarr_discovery.sh — score episode history → TMDB TV recommendations → add to Sonarr
```
**Ad-hoc or separate schedule:**
```
lidarr_missing_art.sh — fetch missing artwork
radarr_tmdb_removed.sh — weekly cleanup of TMDb-dropped entries
sonarr_tvdb_removed.sh — weekly cleanup of TVDB-dropped entries
```
**Why permissions before everything else:** arr cleanup needs `nobody:users` ownership to
delete files. If a file is `root:root`, deletion fails silently — the file looks processed
but stays on disk.
**Why junk before arr cleanup:** junk files are not tracked by any arr — they look like
orphans. Removing them first means orphan detection only finds actual media. Cleaner
output, more accurate detection.
**Why arr_sync before rsync:** once arrs agree on what to track, rsync spreads the actual
files. An upgrade on one node — new tracked path, old path no longer in API — gets
cleaned by arr_cleanup on all nodes after the next sync cycle.
---
## ━━━ HOST AWARENESS ━━━
Scripts run on both servers but only operate on the shares and arrs that belong to that
server. `detect_hosts()` aliases all `HOST*_` prefixed vars to their unprefixed names.
```
HOST1 — source of truth for: HOST2 — source of truth for:
Movies (Radarr) Anime_Movies (his Radarr)
Tv_Shows (Sonarr) Anime_Shows (his Sonarr)
Music (Lidarr)
```
Permissions and cleaner scripts run on both servers against their own shares. Arr cleanup
scripts check the aliased URL — if empty (arr not configured on this host), they exit
cleanly with no action. No manual `HOST1`/`HOST2` comparisons exist in any script.
---
## ━━━ SCRIPTS IN THIS FOLDER ━━━
| Script | Role | When It Runs |
|--------|------|--------------|
| `media_shares_permissions.sh` | Apply `nobody:users` ownership + correct permissions to all media shares | Daily via `media_management.sh` |
| `media_cleaner.sh` | Remove junk files (two profiles: `anime` + `media`) | Daily via `media_management.sh` |
| `arr_sync.sh` | Full-mesh arr library sync — all nodes track the same content | Weekly before rsync |
| `lidarr_cleanup.sh` | Delete orphaned music files not tracked by Lidarr | Daily via `media_management.sh` |
| `sonarr_cleanup.sh` | Delete orphaned TV files not tracked by Sonarr | Daily via `media_management.sh` |
| `radarr_cleanup.sh` | Delete orphaned movie files not tracked by Radarr | Daily via `media_management.sh` |
| `arrs_failed_stalled_recovery.sh` | Auto-recover failed imports and stalled downloads | Daily (5am or every 6hr) |
| `lidarr_missing_art.sh` | Fetch missing album and artist artwork | Ad-hoc or separate schedule |
| `radarr_tmdb_removed.sh` | Remove movies dropped from TMDb | Ad-hoc or weekly |
| `sonarr_tvdb_removed.sh` | Remove series dropped from TVDB | Ad-hoc or weekly |
| `playback_aware_lidarr_discovery.sh` | Behavior-driven music discovery — Emby plays → Last.fm similar → Lidarr | Weekly via `weekly_sync_maintenance.sh` |
| `playback_aware_radarr_discovery.sh` | Behavior-driven movie discovery — Emby watches → TMDB recommendations → Radarr | Weekly via `weekly_sync_maintenance.sh` |
| `playback_aware_sonarr_discovery.sh` | Behavior-driven TV discovery — Emby episodes → TMDB TV recommendations → Sonarr | Weekly via `weekly_sync_maintenance.sh` |
---
## ━━━ HOW THE SCRIPTS RELATE ━━━
```
Weekly arr sync (before rsync):
arr_sync.sh ──────────────── syncs tracked IDs across all nodes
│ union model: any node adds → all nodes get it
└── then rsync spreads the actual files to all nodes
└── then arr_cleanup removes orphans on all nodes (old paths, removed content)
Daily maintenance window (media_management.sh):
media_shares_permissions.sh
│ (permissions correct — arr can now delete files)
media_cleaner.sh (anime + media)
│ (junk removed — orphan scan finds only actual media)
lidarr_cleanup.sh ──────────── queries Lidarr API → walks /Music → deletes orphans
sonarr_cleanup.sh ──────────── queries Sonarr API → walks /Tv_Shows → deletes orphans
radarr_cleanup.sh ──────────── queries Radarr API → walks /Movies → deletes orphans
└── each cleanup → notify_emby_scan() → Emby removes ghost entries
Daily recovery:
arrs_failed_stalled_recovery.sh ── importFailed/stalled → blocklist → re-search
Weekly discovery (WEEKLY_MAINTENANCE_SCRIPTS):
playback_aware_lidarr_discovery.sh ─ Emby plays → Last.fm similar → top candidates → Lidarr
playback_aware_radarr_discovery.sh ─ Emby watches → TMDB recommendations → top candidates → Radarr
playback_aware_sonarr_discovery.sh ─ Emby episodes → TMDB TV recommendations → top candidates → Sonarr
└── each discovery script fires arr search immediately after successful add
Ad-hoc enrichment:
lidarr_missing_art.sh ─────── discovers missing artwork → fetches from fanart.tv
radarr_tmdb_removed.sh ────── status="deleted" → remove from Radarr + add exclusion
sonarr_tvdb_removed.sh ────── status="deleted" → remove from Sonarr + add exclusion
```