Media all drives: Prowlarr on Tester 1 (drill only) wired to Sonarr/Radarr by wire.py; sign-in check (outsiders are asked; home network and containers are not); R-1028..R-1032
gates / gates (push) Successful in 6m31s

This commit is contained in:
2026-10-14 18:30:08 +02:00
parent e6dbc7304b
commit 75a3323921
11 changed files with 308 additions and 6 deletions
@@ -0,0 +1,13 @@
# 2026-10-14T18:23:01+02:00 — from DooPlex over the PUBLIC names (DNS → Cloudflare 2606:4700:…, the same tunnel path as any outside visitor); no cookie, no key, no login
sonarr/ -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
sonarr/api/v3/system/status -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
sonarr/api/v3/series -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
sonarr/api/v3/movie -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
sonarr/initialize.json -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
sonarr/login -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/ -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/api/v3/system/status -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/api/v3/series -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/api/v3/movie -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/initialize.json -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
radarr/login -> 401 redirect= bytes=51 | {"error":"this app is waiting for its first setup"}
@@ -0,0 +1,78 @@
sonarr via docker network (source 172.18.x = traefik-like), header [none]: /api/v3/system/status -> 401
/ -> 200
sonarr via docker network (source 172.18.x = traefik-like), header [X-Forwarded-For: 203.0.113.7]: /api/v3/system/status -> 401
/ -> 302 http://sonarr:8989/login?returnUrl=%2F
sonarr via docker network (source 172.18.x = traefik-like), header [X-Real-Ip: 203.0.113.7]: /api/v3/system/status -> 401
/ -> 200
sonarr via docker network (source 172.18.x = traefik-like), header [X-Forwarded-For: 203.0.113.7, 172.18.0.2]: /api/v3/system/status -> 401
/ -> 302 http://sonarr:8989/login?returnUrl=%2F
radarr via docker network (source 172.18.x = traefik-like), header [none]: /api/v3/system/status -> 401
/ -> 200
radarr via docker network (source 172.18.x = traefik-like), header [X-Forwarded-For: 203.0.113.7]: /api/v3/system/status -> 401
/ -> 302 http://radarr:7878/login?returnUrl=%2F
radarr via docker network (source 172.18.x = traefik-like), header [X-Real-Ip: 203.0.113.7]: /api/v3/system/status -> 401
/ -> 200
radarr via docker network (source 172.18.x = traefik-like), header [X-Forwarded-For: 203.0.113.7, 172.18.0.2]: /api/v3/system/status -> 401
/ -> 302 http://radarr:7878/login?returnUrl=%2F
entryPoints:
web:
address: ":80"
http:
redirections:
--
forwardedHeaders:
trustedIPs:
- "172.16.253.2/32"
http:
middlewares:
- felhom-forwarded@file
/etc/traefik:
acme.json
certs
dynamic
traefik.yml
/etc/traefik/dynamic:
controller.yml
forwarded.yml
serverstransports.yml
setup-gate-radarr.yml
setup-gate-sonarr.yml
/etc/traefik/dynamic/forwarded.yml-5-http:
/etc/traefik/dynamic/forwarded.yml-6- middlewares:
/etc/traefik/dynamic/forwarded.yml:7: felhom-forwarded:
/etc/traefik/dynamic/forwarded.yml-8- headers:
/etc/traefik/dynamic/forwarded.yml-9- customRequestHeaders:
/etc/traefik/dynamic/forwarded.yml-10- X-Forwarded-Port: "443"
/etc/traefik/dynamic/forwarded.yml-11- X-Forwarded-Host: ""
/etc/traefik/dynamic/forwarded.yml-12- X-Forwarded-Uri: ""
/etc/traefik/dynamic/forwarded.yml-13- X-Forwarded-Method: ""
/etc/traefik/dynamic/forwarded.yml-14- X-Forwarded-Prefix: ""
/etc/traefik/dynamic/forwarded.yml-15- X-Forwarded-Tls-Client-Cert: ""
/etc/traefik/dynamic/forwarded.yml-16- X-Forwarded-Tls-Client-Cert-Info: ""
/etc/traefik/dynamic/forwarded.yml-17- Forwarded: ""
/etc/traefik/dynamic/forwarded.yml-18- True-Client-Ip: ""
/etc/traefik/dynamic/forwarded.yml-19- X-Client-Ip: ""
--
/etc/traefik/traefik.yml-22- http:
/etc/traefik/traefik.yml-23- middlewares:
/etc/traefik/traefik.yml:24: - felhom-forwarded@file
/etc/traefik/traefik.yml-25- tls:
/etc/traefik/traefik.yml-26- certResolver: letsencrypt
/etc/traefik/traefik.yml-27-
/etc/traefik/traefik.yml-28-providers:
/etc/traefik/traefik.yml-29- docker:
/etc/traefik/traefik.yml-30- endpoint: "unix:///var/run/docker.sock"
/etc/traefik/traefik.yml-31- exposedByDefault: false
/etc/traefik/traefik.yml-32- network: traefik-public
/etc/traefik/traefik.yml-33- file:
/etc/traefik/traefik.yml-34- directory: /etc/traefik/dynamic
/etc/traefik/traefik.yml-35- watch: true
/etc/traefik/traefik.yml-36-
sonarr /initialize.json [none] -> 200, contains apiKey field: 1
sonarr /initialize.json [X-Forwarded-For: 203.0.113.7] -> 302, contains apiKey field: 0
radarr /initialize.json [none] -> 200, contains apiKey field: 1
radarr /initialize.json [X-Forwarded-For: 203.0.113.7] -> 302, contains apiKey field: 0
WebAPI login failure. Reason: invalid credentials, attempt count: 1, IP: 37.191.56.193, username: nobody
@@ -0,0 +1,19 @@
created 2026-10-14T16:04:53.056354746Z started 2026-10-14T16:04:53.476264772Z
Attempting to obtain server token from claim token
Token obtained successfully
Plex Media Server first run setup complete
Starting Plex Media Server.
2026/10/14 15:41:23 [INFO] [api] Remove requested for stack: plex
2026/10/14 15:41:23 [INFO] Removing deployed stack: plex (removeHDDData=true, hddDeclared=true, backupPaths=2)
2026/10/14 15:41:50 [INFO] [stacks] RemoveStack plex: removed volume(s) [plex_plex_config plex_plex_transcode]
2026/10/14 15:41:50 [INFO] Stack plex removed successfully (took 26.7s)
2026/10/14 15:41:50 [INFO] Event pushed: app_removed (info) [hu-only] — Alkalmazás eltávolítva: plex
2026/10/14 15:41:50 [INFO] [stacks] image retention (remove of plex): deleted [plexinc/pms-docker:1.41.4.9463-630c9f557] (0186be6d1305, 351MB) — no container, installed app or undo names it (decision 53)
2026/10/14 15:41:50 [INFO] [stacks] image retention (remove of plex): pass over 21 image(s) of [plexinc/pms-docker] — 1 candidate(s), 1 deleted, the rest kept
2026/10/14 16:04:44 [INFO] [api] Deploy requested for stack: plex
2026/10/14 16:04:45 [INFO] [stacks] Deploying stack plex with 4 env vars: [DOMAIN, SUBDOMAIN, PLEX_CLAIM, HDD_PATH]
2026/10/14 16:04:45 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Plex
2026/10/14 16:04:53 [INFO] [stacks] Stack plex deployed successfully (took 8.5s)
2026/10/14 16:04:53 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Plex
351174951
@@ -0,0 +1,9 @@
18:24:50 prowlarr: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "prowlarr"} -> 202 {"ok": true, "data": {"warning": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. Normál használat mellett ez nem okoz problémát."}, "message": "Telepítés elindítva – az ál
18:25:10 prowlarr: deployed, state=running, pinned={'prowlarr': 'lscr.io/linuxserver/prowlarr:version-2.6.5.5623'}
running /var/lib/docker/volumes/prowlarr_prowlarr_config/_data->/config:true
2026/10/14 16:24:50 [INFO] [api] Deploy requested for stack: prowlarr
2026/10/14 16:24:50 [INFO] [stacks] Deploying stack prowlarr with 2 env vars: [DOMAIN, SUBDOMAIN]
2026/10/14 16:24:50 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Prowlarr
2026/10/14 16:24:58 [INFO] [stacks] Stack prowlarr deployed successfully (took 7.5s)
2026/10/14 16:24:58 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Prowlarr
@@ -0,0 +1,96 @@
== qBittorrent http://172.18.0.2:8080 (password: the deploy secret, read from the container's environment)
POST http://172.18.0.2:8080/api/v2/auth/login -> 204 ''
session cookie name: QBT_SID_8080
GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac}
before: auto_tmm_enabled=True save_path=/mnt/felhom-drives/media_a/userdata/media/torrents temp_path_enabled=True temp_path=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp
POST http://172.18.0.2:8080/api/v2/app/setPreferences -> 200 ''
GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac}
after : auto_tmm_enabled=True (a category's save path applies only in automatic mode)
GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b}
POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 ''
POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 ''
POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 ''
POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 ''
GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b}
category movies-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp
category movies-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp
category tv-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp
category tv-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp
== Transmission http://172.18.0.3:9091/transmission/rpc (password: the deploy secret, read from the container's environment)
POST http://172.18.0.3:9091/transmission/rpc -> 409 '<h1>409: Conflict</h1><p>Your request had an invalid session_id header.</p><p>To fix this, follow these steps:<ol><li>
POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result}
before: {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/incomplete", "incomplete-dir-enabled": false, "rename-partial-files": true}
POST http://172.18.0.3:9091/transmission/rpc -> 409 '<h1>409: Conflict</h1><p>Your request had an invalid session_id header.</p><p>To fix this, follow these steps:<ol><li>
POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result}
POST http://172.18.0.3:9091/transmission/rpc -> 409 '<h1>409: Conflict</h1><p>Your request had an invalid session_id header.</p><p>To fix this, follow these steps:<ol><li>
POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result}
after : {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir-enabled": false, "rename-partial-files": true} (one incomplete folder for all torrents would be on drive A)
POST http://172.18.0.3:9091/transmission/rpc -> 409 '<h1>409: Conflict</h1><p>Your request had an invalid session_id header.</p><p>To fix this, follow these steps:<ol><li>
POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result}
probe Host: transmission:9091 -> HTTP 200 {'arguments': {'version': '4.1.3 (838877323f)'}, 'result': 'success'}
== sonarr http://172.18.0.5:8989/api/v3 (key: <ApiKey> in /config/config.xml, read from the config volume)
GET http://172.18.0.5:8989/api/v3/tag -> 200 [list of 2]
tags: {'drive-a': 1, 'drive-b': 2}
GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 2]
GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 2]
root folder: /mnt/felhom-drives/media_a/userdata/media/tv accessible=True freeSpace=99701403648
root folder: /mnt/felhom-drives/media_b/userdata/media/tv accessible=True freeSpace=99701551104
GET http://172.18.0.5:8989/api/v3/downloadclient/schema -> 200 [list of 18]
GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 4]
client qBittorrent drive-a: exists — left as it is
client Transmission drive-a: exists — left as it is
client qBittorrent drive-b: exists — left as it is
client Transmission drive-b: exists — left as it is
GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 4]
client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=tv-drive-a directory=None
client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=tv-drive-b directory=None
client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/tv
client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/tv
GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy}
before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True
PUT http://172.18.0.5:8989/api/v3/config/mediamanagement/1 -> 202 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy}
GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy}
after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept)
== radarr http://172.18.0.7:7878/api/v3 (key: <ApiKey> in /config/config.xml, read from the config volume)
GET http://172.18.0.7:7878/api/v3/tag -> 200 [list of 2]
tags: {'drive-a': 1, 'drive-b': 2}
GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 2]
GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 2]
root folder: /mnt/felhom-drives/media_a/userdata/media/movies accessible=True freeSpace=99701403648
root folder: /mnt/felhom-drives/media_b/userdata/media/movies accessible=True freeSpace=99701551104
GET http://172.18.0.7:7878/api/v3/downloadclient/schema -> 200 [list of 19]
GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 4]
client qBittorrent drive-a: exists — left as it is
client Transmission drive-a: exists — left as it is
client qBittorrent drive-b: exists — left as it is
client Transmission drive-b: exists — left as it is
GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 4]
client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=movies-drive-a directory=None
client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=movies-drive-b directory=None
client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/movies
client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/movies
GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re}
before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True
PUT http://172.18.0.7:7878/api/v3/config/mediamanagement/1 -> 202 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re}
GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re}
after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept)
== done; Transmission reachable by its container name: True
== prowlarr http://172.18.0.13:9696/api/v1 (key: <ApiKey> in /config/config.xml, read from the config volume)
GET http://172.18.0.13:9696/api/v1/applications/schema -> 200 [list of 7]
GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 0]
POST http://172.18.0.13:9696/api/v1/applications -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, syncLevel, tags}
POST http://172.18.0.13:9696/api/v1/applications -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, syncLevel, tags}
GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 2]
application Radarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://radarr:7878 syncCategories=[2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090]
application Sonarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://sonarr:8989 syncCategories=[5000, 5010, 5020, 5030, 5040, 5045, 5050, 5090]
GET http://172.18.0.13:9696/api/v1/indexer -> 200 [list of 0]
indexers in Prowlarr: []
POST http://172.18.0.13:9696/api/v1/command -> 201 {body, clientUserAgent, commandName, id, name, priority, queued, sendUpdatesToClient, started, stateChangeTime, status, trigger, updateScheduledTask}
GET http://172.18.0.13:9696/api/v1/command/1 -> 200 {body, clientUserAgent, commandName, duration, ended, id, message, name, priority, queued, sendUpdatesToClient, started, stateChangeTime, status, trigger, updateScheduledTask}
ApplicationIndexerSync -> status=completed result=None message=Completed
GET http://172.18.0.5:8989/api/v3/indexer -> 200 [list of 0]
sonarr now holds 0 indexer(s): []
GET http://172.18.0.7:7878/api/v3/indexer -> 200 [list of 0]
radarr now holds 0 indexer(s): []
== prowlarr part done
@@ -20,3 +20,5 @@ Predictions were written before each measurement: `A-drives/PREDICTIONS-A4-A6.md
| S2 | One public-domain film (archive.org, CC PD Mark) on drive B: through Transmission and through qBittorrent → **hardlink** in `media_b/…/movies`. archive.org's web seed failed the last piece for qBittorrent; a local peer (the same torrent in Transmission) finished it — recorded | `S2-*.txt` |
| S3 | Tag drive-a, no client forced → „Transmission drive-a", hardlink on A. Mismatch (root B, tag A) → download on A, **copy** on B (link count 1 each) | `S3-*.txt` |
| — | **17:25 CEST: demo-hp was power-cut by another session's Immich drill** (`plugcut.py --plug hp`) — Tester 1 went down with it and came back by itself; the boot gate recreated the four media apps onto drive A; all test films then removed (0 files left) | `AFTER-power-cut-1725.txt` |
| H | Prowlarr (drill-only template `b0fd3a0`, `version-2.6.5.5623`, config volume only) deployed through the dashboard; `wire.py` adds Sonarr and Radarr as Prowlarr apps with „Full Sync” (both pass Prowlarr's test; the sync completes; 0 indexers so far). Sonarr now refuses a bare IP (the operator set „Allowed hosts”) — `wire.py` sends `Host: <app>` | `H-*.txt` |
| G | Sign-in with „Disabled for Local Addresses”: outsiders ARE asked (the public address arrives in `X-Forwarded-For`; Sonarr/Radarr redirect to `/login`); the home network and other containers are not (200 + API key) — R-1030. The box's setup gate also blocks outsiders until setup is marked done | `G-auth/` |
@@ -12,6 +12,7 @@ About one hour. From any place with a browser. Use only content you have the rig
| qBittorrent | https://torrent.enkicsifelhom.hu |
| Transmission | https://transmission.enkicsifelhom.hu |
| Plex (after step 2) | https://plex.enkicsifelhom.hu/web |
| Prowlarr (added 18:25) | https://prowlarr.enkicsifelhom.hu |
The two test drives are **Media A** and **Media B**. Their folders:
@@ -33,7 +34,8 @@ If the code expired: remove Plex in the dashboard **without** „delete data" (s
1. Open Sonarr. The box asks for your dashboard sign-in first. Then Sonarr asks you to make its admin user. Make it.
2. Do the same in Radarr.
3. In each: **Settings → Indexers → +**. Add your own download sources.
3. **Prowlarr:** make its admin user. Add your own download sources in **Indexers** — once, in Prowlarr only.
Prowlarr sends them to Sonarr and Radarr by itself („Full Sync”). If a source asks for FlareSolverr, tell CC.
The download apps are already connected. Do not change **Settings → Download Clients**, except as step 4c says.
@@ -12,6 +12,8 @@ For each drive d in DRIVES, M = /mnt/felhom-drives/<d>/userdata/media (the same
„Transmission <d>" (directory M/transmission/tv, tag <d>)
Radarr : the same with movies
Sonarr/Radarr: minimum free space when importing = MIN_FREE_MB; „use hardlinks instead of copy" quoted
Prowlarr : (when installed) one Application entry each for Sonarr and Radarr, sync level „Full Sync", reached by
container name; then an ApplicationIndexerSync command, and the indexers each app holds read back
Box-wide, recorded with the reason in the output:
qBittorrent : auto_tmm_enabled = true (a category's save path applies ONLY in automatic torrent management;
qBittorrent's default is manual, which saves every torrent to the default
@@ -82,7 +84,7 @@ def ip(c):
def apikey(app):
x = open(f"/var/lib/docker/volumes/{app}_{app}_config/_data/config.xml").read()
x = open(f"/var/lib/docker/volumes/{app}_{app}_config/_data/config.xml").read() # sonarr, radarr, prowlarr
k = re.search(r"<ApiKey>([^<]+)</ApiKey>", x).group(1)
SECRETS.append(k)
return k
@@ -158,7 +160,9 @@ TR_BY_NAME = c == 200
# ---------------------------------------------------------------- Sonarr / Radarr
def arr(app, port, kind, root_kind):
base = f"http://{ip(app)}:{port}/api/v3"
H = {"X-Api-Key": apikey(app)}
# Host = the container name: Sonarr/Radarr refuse a bare IP once the household sets „Allowed hosts"
# (measured 2026-10-14 18:30: 400 „Invalid Hostname" for Host=<ip>, 200 for Host=sonarr)
H = {"X-Api-Key": apikey(app), "Host": app}
say(f"== {app} {base} (key: <ApiKey> in /config/config.xml, read from the config volume)")
c, tags, _ = call("GET", base + "/tag", H)
have = {t["label"]: t["id"] for t in tags or []}
@@ -220,3 +224,53 @@ def arr(app, port, kind, root_kind):
arr("sonarr", 8989, "tv", "tv")
arr("radarr", 7878, "movie", "movies")
say("== done; Transmission reachable by its container name:", TR_BY_NAME)
# ---------------------------------------------------------------- Prowlarr -> Sonarr / Radarr (Settings -> Apps)
def prowlarr():
if not sh("docker ps -q -f name=^prowlarr$"):
say("== prowlarr: not installed — skipped")
return
base = f"http://{ip('prowlarr')}:9696/api/v1"
H = {"X-Api-Key": apikey("prowlarr"), "Host": "prowlarr"}
say(f"== prowlarr {base} (key: <ApiKey> in /config/config.xml, read from the config volume)")
c, schema, _ = call("GET", base + "/applications/schema", H)
sch = {x["implementation"]: x for x in schema}
c, apps, _ = call("GET", base + "/applications", H)
have = {a["name"] for a in apps or []}
for impl, port in (("Sonarr", 8989), ("Radarr", 7878)):
if impl in have:
say(f" application {impl}: exists — left as it is")
continue
body = json.loads(json.dumps(sch[impl]))
vals = {"prowlarrUrl": "http://prowlarr:9696", "baseUrl": f"http://{impl.lower()}:{port}",
"apiKey": apikey(impl.lower())}
for f in body["fields"]:
if f["name"] in vals:
f["value"] = vals[f["name"]]
body.update({"name": impl, "syncLevel": "fullSync", "tags": []})
c, r, _ = call("POST", base + "/applications", H, body, expect=(200, 201, 400))
if c == 400:
say(f" application {impl}: REFUSED by Prowlarr's own test: {redact(json.dumps(r))[:400]}")
c, apps, _ = call("GET", base + "/applications", H)
for a in apps or []:
f = {k["name"]: k.get("value") for k in a["fields"]}
say(f" application {a['name']}: syncLevel={a.get('syncLevel')} prowlarrUrl={f.get('prowlarrUrl')} baseUrl={f.get('baseUrl')}"
f" syncCategories={f.get('syncCategories')}")
c, ix, _ = call("GET", base + "/indexer", H)
say(f" indexers in Prowlarr: {[i['name'] for i in ix or []]}")
c, cmd, _ = call("POST", base + "/command", H, {"name": "ApplicationIndexerSync"})
cid = (cmd or {}).get("id")
import time
for _ in range(20):
time.sleep(3)
c, st, _ = call("GET", base + f"/command/{cid}", H)
if (st or {}).get("status") in ("completed", "failed", "aborted"):
break
say(f" ApplicationIndexerSync -> status={(st or {}).get('status')} result={(st or {}).get('result')} message={(st or {}).get('message')}")
for app, port in (("sonarr", 8989), ("radarr", 7878)):
c, ai, _ = call("GET", f"http://{ip(app)}:{port}/api/v3/indexer", {"X-Api-Key": apikey(app), "Host": app})
say(f" {app} now holds {len(ai or [])} indexer(s): {[(i['name'], i.get('implementation')) for i in ai or []]}")
prowlarr()
say("== prowlarr part done")