Media all drives: Jellyfin set up on Tester 1 (both drives, read-only); S5/S6 predictions written before the pull
gates / gates (push) Successful in 6m47s

This commit is contained in:
2026-10-14 19:39:40 +02:00
parent e3b785f33a
commit 56f613807d
5 changed files with 148 additions and 0 deletions
@@ -0,0 +1,16 @@
19:23:07 jellyfin: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "media", "HDD_PATH": "/mnt/felhom-drives/media_a"} -> 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
19:33:11 jellyfin: never became deployed (state=not_deployed)
2026/10/14 17:23:44 [WARN] [stacks] install jellyfin was INTERRUPTED by a controller restart — removing what it started (volumes kept) and reporting it (R-681)
2026/10/14 17:23:45 [WARN] [stacks] 1 install(s) interrupted by the restart were resolved and reported: [jellyfin]
2026/10/14 17:23:45 [INFO] Event pushed: app_deploy_failed (warning) [hu-only] — Alkalmazás telepítése nem sikerült: Jellyfin — interrupted by a controller restart before it finished — install it again
19:33:32 jellyfin: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "media", "HDD_PATH": "/mnt/felhom-drives/media_a"} -> 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
19:34:17 jellyfin: deployed, state=running, pinned={'jellyfin': 'jellyfin/jellyfin:10.11.11'}
running /var/lib/docker/volumes/jellyfin_jellyfin_cache/_data->/cache:true /var/lib/docker/volumes/jellyfin_jellyfin_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:false /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:false
2026/10/14 17:33:31 [INFO] [api] Deploy requested for stack: jellyfin
2026/10/14 17:33:32 [INFO] [stacks] Deploying stack jellyfin with 3 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH]
2026/10/14 17:33:32 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Jellyfin
2026/10/14 17:34:00 [INFO] [stacks] Stack jellyfin deployed successfully (took 28.8s)
2026/10/14 17:34:01 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Jellyfin
@@ -0,0 +1,8 @@
auth 200
Authorization+Token /Library/VirtualFolders 200 2
Authorization+Token /Items 200 22
X-Emby-Token /Library/VirtualFolders 200 2
X-Emby-Token /Items 200 22
X-MediaBrowser-Token /Library/VirtualFolders 200 2
X-MediaBrowser-Token /Items 200 22
@@ -0,0 +1,53 @@
POST /Startup/Configuration -> 204
GET /Startup/User -> 200
POST /Startup/User -> 204
POST /Startup/RemoteAccess -> 204
POST /Startup/Complete -> 204
POST /Users/AuthenticateByName -> 200
POST /Library/VirtualFolders -> 204
POST /Library/VirtualFolders -> 204
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
GET /Items -> 401 b''
Series []
Movie []
Episodes: 0
GET /Library/VirtualFolders -> 401 b''
@@ -0,0 +1,21 @@
# Predictions — written before S5/S6 (2026-10-14 ~19:45 CEST, controller 0.311.0, agent 0.156.1)
S5 — Media B's virtual disk is detached while everything runs (`qm set 341 --delete scsi3`; the volume moves to `unused0`)
- The agent sees the drive go; the controller's drive gate marks /mnt/felhom-drives/media_b DISCONNECTED and stops
NOTHING: no app has HDD_PATH on media_b (all six media apps carry HDD_PATH=media_a). A `storage_disconnected` event
goes to the hub → a mail. FileBrowser is re-synced without media_b.
- The media containers keep their old bind of media_b: reads of B's files fail (I/O error) or the folder looks empty;
they do not crash. Sonarr/Radarr: root folder B „missing" in health; American Horror Story / Matchbox files missing.
- Plex: its automatic scan is off (R-1037), so NO scan runs and NO item is removed. Plex's „empty trash after every scan"
is on by default — it would remove B's items only if a scan ran.
- Jellyfin: real-time monitoring is on by default; if it scans, it may REMOVE B's items from its library.
- Nothing is written onto the guest's own disk under /mnt/felhom-drives/media_b (the gate exists to prevent that), unless
an app keeps writing into its stale bind — qBittorrent/Transmission are seeding from B and may log errors.
- The whole-guest backup is not affected (mp8 is excluded); tonight's app backups skip nothing on B (no app keeps data
there by HDD_PATH).
S6 — the same volume is re-attached to the same slot (`qm set 341 --scsi3 nvme-scratch:341/vm-341-disk-4.raw,backup=0`)
- The agent re-mounts it and binds it under the parent again; the controller clears DISCONNECTED; it restarts only the
apps IT stopped — none.
- So the running media containers still do NOT see B until they are recreated (their bind was made at start).
Expected: the restart notice of decision 238 must also cover „a drive came back".
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""jfsetup.py — S5 prep: Jellyfin's first-run wizard and two libraries, by its own API, INSIDE the guest.
The admin password is generated on DooPlex (kept ONLY in ~/.felhom-tester1/secrets.json, 0600) and substituted into
this file's copy on the box for the run; the copy is deleted afterwards. Nothing secret is printed."""
import json, subprocess, time, urllib.parse, urllib.request
PW = "__JF_PW__"
ip = subprocess.run("docker inspect jellyfin -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", shell=True,
capture_output=True, text=True).stdout.strip()
B = f"http://{ip}:8096"
AUTH = 'MediaBrowser Client="felhom-test", Device="wire", DeviceId="felhom-wire-1", Version="1"'
def call(m, path, body=None, tok=None):
h = {"Content-Type": "application/json", "Authorization": AUTH + (f', Token="{tok}"' if tok else "")}
r = urllib.request.Request(B + path, data=json.dumps(body).encode() if body is not None else None, headers=h, method=m)
try:
with urllib.request.urlopen(r, timeout=60) as x:
t = x.read().decode(); print(f" {m} {path.split('?')[0]} -> {x.status}"); return json.loads(t) if t else None
except urllib.error.HTTPError as e:
print(f" {m} {path.split('?')[0]} -> {e.code} {e.read()[:150]}"); return None
for _ in range(30):
try:
urllib.request.urlopen(B + "/health", timeout=5); break
except Exception:
time.sleep(3)
call("POST", "/Startup/Configuration", {"UICulture": "en-US", "MetadataCountryCode": "HU", "PreferredMetadataLanguage": "hu"})
call("GET", "/Startup/User")
call("POST", "/Startup/User", {"Name": "admin", "Password": PW})
call("POST", "/Startup/RemoteAccess", {"EnableRemoteAccess": True, "EnableAutomaticPortMapping": False})
call("POST", "/Startup/Complete")
a = call("POST", "/Users/AuthenticateByName", {"Username": "admin", "Pw": PW})
tok = a["AccessToken"]
for name, ct, sub in (("TV Shows", "tvshows", "tv"), ("Movies", "movies", "movies")):
q = urllib.parse.urlencode({"name": name, "collectionType": ct, "refreshLibrary": "true"})
call("POST", f"/Library/VirtualFolders?{q}", {"LibraryOptions": {"PathInfos": [
{"Path": f"/mnt/felhom-drives/{d}/userdata/media/{sub}"} for d in ("media_a", "media_b")]}}, tok)
for _ in range(40):
time.sleep(6)
s = call("GET", "/Items?Recursive=true&IncludeItemTypes=Series,Movie,Episode&Fields=Path", tok) or {}
items = s.get("Items", [])
eps = [i for i in items if i["Type"] == "Episode"]
if len(eps) >= 16:
break
for t in ("Series", "Movie"):
print(t, sorted(i["Name"] for i in items if i["Type"] == t))
print("Episodes:", len(eps))
for v in call("GET", "/Library/VirtualFolders", tok) or []:
print("library", v["Name"], v.get("Locations"))