diff --git a/arrs-4k/README.md b/arrs-4k/README.md index 9b68b9d..547aceb 100644 --- a/arrs-4k/README.md +++ b/arrs-4k/README.md @@ -21,6 +21,11 @@ idempotent, changed=0). 1080p radarr was mislabeled `is4k=true` and was flipped to `is4k=false` (non-4k default). - **Plex**: SEPARATE libraries `Movies - 4K` (/data/media/movies-4k) + `TV - 4K` (/data/media/tv-4k), not merged into the 1080p libraries. +- **Download clients** (added post-Step-1, packaged in the playbook via `ensure-arrs-4k-dl.py`): + SABnzbd + Transmission on both instances, DISTINCT categories to avoid cross-import with 1080p — + sabnzbd `movies-4k`/`tv-4k`, transmission `radarr-uhd`/`sonarr-uhd` (Transmission category forbids digits). + Completed paths under /data (hardlinks intact). +- **Homepage**: separate `Radarr 4K`/`Sonarr 4K` tiles in Media Automation (keys via HOMEPAGE_VAR, siteMonitor). - **Traefik**: file-provider routers (see traefik-router snippet), NOT docker labels. - **Tdarr**: does NOT watch movies-4k/tv-4k (its libraries are the specific 1080p /media/movies, /media/tv) — quality-first keeps the grabbed file. diff --git a/arrs-4k/deploy-arrs-4k.yml b/arrs-4k/deploy-arrs-4k.yml index 87193c0..a144837 100644 --- a/arrs-4k/deploy-arrs-4k.yml +++ b/arrs-4k/deploy-arrs-4k.yml @@ -84,3 +84,76 @@ - name: Result ansible.builtin.debug: msg: "arrs-4k {{ 'running (changed)' if r.changed else 'already up (no-op)' }}" + + - name: Wait for 4K arr APIs (ports) + ansible.builtin.wait_for: + host: 192.168.99.183 + port: "{{ item }}" + timeout: 90 + loop: [7879, 8990] + + - name: Install 4K download-client config script + ansible.builtin.copy: + dest: "{{ app_dir }}/ensure-arrs-4k-dl.py" + owner: tommy + group: tommy + mode: "0755" + content: | + #!/usr/bin/env python3 + # Idempotent: ensure the 4K arr download clients + sabnzbd categories exist. + # Reads RADARR4K_KEY / SONARR4K_KEY / SAB_KEY from env. Prints CHANGED or NOCHANGE. + import os, json, urllib.request + H = "http://192.168.99.183" + SAB = os.environ["SAB_KEY"] + changed = False + + def sab_cats(): + return json.load(urllib.request.urlopen(f"{H}:8081/api?mode=get_cats&output=json&apikey={SAB}"))["categories"] + def sab_add(cat): + urllib.request.urlopen(f"{H}:8081/api?mode=set_config§ion=categories&keyword={cat}&dir={cat}&apikey={SAB}").read() + def dc_get(port, key): + return json.load(urllib.request.urlopen(urllib.request.Request(f"{H}:{port}/api/v3/downloadclient", headers={"X-Api-Key": key}))) + def dc_post(port, key, body): + urllib.request.urlopen(urllib.request.Request(f"{H}:{port}/api/v3/downloadclient", + data=json.dumps(body).encode(), method="POST", headers={"X-Api-Key": key, "Content-Type": "application/json"})) + + def sab_body(catf, catv, recent, older): + return {"enable": True, "protocol": "usenet", "priority": 1, "name": "SABnzbd", + "implementation": "Sabnzbd", "configContract": "SabnzbdSettings", + "fields": [{"name": "host", "value": "gluetun"}, {"name": "port", "value": 8080}, + {"name": "apiKey", "value": SAB}, {"name": catf, "value": catv}, + {"name": recent, "value": -100}, {"name": older, "value": -100}]} + def tr_body(catf, catv): + return {"enable": True, "protocol": "torrent", "priority": 1, "name": "Transmission", + "implementation": "Transmission", "configContract": "TransmissionSettings", + "fields": [{"name": "host", "value": "gluetun"}, {"name": "port", "value": 9091}, + {"name": "urlBase", "value": "/transmission/"}, {"name": catf, "value": catv}]} + + cats = sab_cats() + for c in ("movies-4k", "tv-4k"): + if c not in cats: + sab_add(c); changed = True + + plan = [ + (7879, os.environ["RADARR4K_KEY"], [sab_body("movieCategory", "movies-4k", "recentMoviePriority", "olderMoviePriority"), + tr_body("movieCategory", "radarr-uhd")]), + (8990, os.environ["SONARR4K_KEY"], [sab_body("tvCategory", "tv-4k", "recentTvPriority", "olderTvPriority"), + tr_body("tvCategory", "sonarr-uhd")]), + ] + for port, key, bodies in plan: + have = {c["name"] for c in dc_get(port, key)} + for b in bodies: + if b["name"] not in have: + dc_post(port, key, b); changed = True + + print("CHANGED" if changed else "NOCHANGE") + + - name: Ensure 4K download clients + sabnzbd categories (idempotent, cats movies-4k/tv-4k) + ansible.builtin.command: "python3 {{ app_dir }}/ensure-arrs-4k-dl.py" + environment: + RADARR4K_KEY: "{{ vault_radarr4k_apikey }}" + SONARR4K_KEY: "{{ vault_sonarr4k_apikey }}" + SAB_KEY: "{{ vault_sabnzbd_apikey }}" + register: dl_result + changed_when: "'CHANGED' in dl_result.stdout" + no_log: true diff --git a/homepage/services.yaml b/homepage/services.yaml index 4e94875..b960b30 100644 --- a/homepage/services.yaml +++ b/homepage/services.yaml @@ -55,6 +55,25 @@ type: radarr url: http://radarr:7878 key: {{HOMEPAGE_VAR_RADARR_KEY}} + - Radarr 4K: + icon: radarr.png + href: https://radarr-4k.goattw.net + description: 4K Movies (quality-first) + siteMonitor: https://radarr-4k.goattw.net + widget: + type: radarr + url: http://radarr-4k:7878 + key: {{HOMEPAGE_VAR_RADARR4K_KEY}} + + - Sonarr 4K: + icon: sonarr.png + href: https://sonarr-4k.goattw.net + description: 4K TV (quality-first) + siteMonitor: https://sonarr-4k.goattw.net + widget: + type: sonarr + url: http://sonarr-4k:8989 + key: {{HOMEPAGE_VAR_SONARR4K_KEY}} - Prowlarr: icon: prowlarr.png