Files
felhom.eu/documentation/audits/media-all-drives-2026-10-14/tools/jfsetup.py
T

51 lines
2.7 KiB
Python

#!/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"))