--- name: sonarr description: Use Sonarr to add TV shows and manually import existing raw-named folders into the Show Name (Year)/Season NN/... layout Jellyfin and Plex expect. Use when migrating the TV library, importing a newly-downloaded show, or otherwise interacting with this repo's Sonarr instance. --- # Sonarr library migration/import This repo's Sonarr manages TV show organization for both Jellyfin and Plex (they share the same `nas_media` library at `/data/video/tv`). The `claude-homelab` plugin's Sonarr skill (if installed) only covers search/add/remove — it has no manual-import support. This skill's `sonarr.sh` covers that gap: matching an existing raw-named folder to the right show/episodes and importing it into place. Always use `./sonarr.sh` (repo root) for this — never hand-roll the Sonarr API calls, the folder-naming token gotcha below has already bitten this workflow once. ## Workflow: migrate one existing show 1. **Find the TVDB match**: `./sonarr.sh lookup ""` — prints candidates as `tvdb: Title (Year) status`. Pick the right one (check year/status against what's actually in the folder). 2. **Add it**: `./sonarr.sh add tvdb:` — adds unmonitored, no search, root folder defaults to `/data/video/tv`. Skip if already in `./sonarr.sh list`. 3. **Preview the import**: `./sonarr.sh scan ""` — shows how each file will map to season/episode, and flags anything Sonarr couldn't parse (`!! `). **Read this before importing** — don't skip straight to step 4. 4. **Apply it**: `./sonarr.sh import ""` — re-scans and imports only if every file matched cleanly (refuses and tells you to check `scan` output otherwise, rather than partially importing). Files are hardlinked (not copied) into `/data/video/tv/Show Name (Year)/Season NN/Show Name - SxxExx - Title Quality.ext` — no extra disk usage, original folder is left behind but empty. 5. **Clean up the empty source folder**: `docker exec jellyfin rmdir ""` (any container with the `nas_media` mount works). 6. **Verify Plex picks it up correctly** — this is not automatic, see below. ## Verifying the Plex side (do this every time, not just once) Moving a file changes its path. Plex's database still points at the *old* path until it rescans — it does not watch the filesystem live here. After every import: ```bash TOKEN=$(docker exec plex grep -o 'PlexOnlineToken="[^"]*"' \ "/config/Library/Application Support/Plex Media Server/Preferences.xml" | cut -d'"' -f2) curl -s "http://localhost:32400/library/sections/2/refresh?X-Plex-Token=$TOKEN" ``` (Section `2` is "TV Shows" in this Plex instance — confirm with `curl -s "http://localhost:32400/library/sections?X-Plex-Token=$TOKEN"` if unsure.) Then spot-check that the show's episodes still show the correct `viewCount`/watch state pointing at the new file path — Plex re-matches by its own metadata agent (title/GUID), not by the old path, so watched status normally survives a move, but confirm it rather than assuming, especially for messier shows (duplicate releases, specials, date-based naming). ## Known gotcha: `{Year}` is not a valid Sonarr token The series folder format must use the combined token `{Series TitleYear}` (produces `Title (Year)` as one unit) — `{Series Title} ({Year})` silently produces `Title ()` with no year, because Sonarr has no standalone `{Year}` token for series-level naming. This bit the first two shows migrated (`Andor`, `3 Body Problem`) before being caught — check `docker exec sonarr curl -s http://localhost:8989/api/v3/config/naming/examples -H "X-Api-Key: $SONARR_API_KEY"` (or `curl "$SONARR_URL/api/v3/config/naming/examples" -H "X-Api-Key: $SONARR_API_KEY"` from the host) any time naming config changes — `seriesFolderExample` should show a real year, not `()`. ## Edge cases that will need manual handling (not automatable via scan/import) - **Date-based shows** (e.g. Jeopardy) — no season/episode numbers in filenames. Set the show's Series Type to "Daily" in the Sonarr UI before scanning; `scan` will otherwise reject every file. - **Specials mixed into a season folder** (e.g. Lower Decks `S00E501`-style files) — Sonarr should route these to `Season 00` correctly; verify rather than assume. - **Duplicate episodes from two releases** (e.g. Poker Face) — decide which release to keep before importing; `scan` will show both, `import` will refuse until resolved since it won't guess. - **Combined/ambiguous episode files** (e.g. `e01-02.` with no season number) — `scan` will likely reject these; use the Sonarr web UI's Manual Import screen instead, which allows manually assigning an episode range per file (the CLI script only handles clean auto-matches by design, so it can't silently mis-import something ambiguous). ## Other commands - `./sonarr.sh list` — series currently in Sonarr - `./sonarr.sh rootfolders` — root folders + how many unmapped (not-yet-added) folders each has - `./sonarr.sh queue` — current download queue - `./sonarr.sh test-client` — tests configured download client connections (currently just qBittorrent_vpn, category `tv-sonarr`) ## Credentials `SONARR_API_KEY` / `SONARR_URL` live in this repo's `.credentials` (source it before running `sonarr.sh` manually outside the script — the script sources it itself). Get a fresh key from Sonarr UI → Settings → General → Security, or `docker exec sonarr grep -o 'ApiKey>[^<]*' /config/config.xml`.