Allow serienen/videoen in media handoff #58

Merged
sascha merged 1 commit from fix/handoff-en-categories-20260916 into main 2026-09-16 12:12:16 +02:00
2 changed files with 98 additions and 1 deletions

6
app.py
View file

@ -4121,7 +4121,11 @@ async def media_handoff(payload: MediaHandoffPayload, request: Request):
_audit("/media/handoff", "POST", 403, f"caller={caller} action={payload.action}")
raise HTTPException(403, "Media handoff caller is not allowed")
allowed_categories = {"serien4k", "serien", "video4k", "video"}
# 16.09.2026: serienen/videoen ergaenzt. Die englischen Arr-Instanzen
# sonarrEN (Port 8991, Root /data/FHD/serienen) und radarrEN (Port 7880,
# Root /data/FHD/videoen) nutzen eigene SAB-Kategorien. Ohne sie brach der
# Handoff mit 422 ab und Releases blieben in /usenet/complete liegen.
allowed_categories = {"serien4k", "serien", "serienen", "video4k", "video", "videoen"}
if payload.action == "start":
if payload.category not in allowed_categories:
raise HTTPException(422, "Unsupported media category")

View file

@ -117,6 +117,99 @@ def test_media_handoff_rejects_wrong_category_path(monkeypatch):
assert response.status_code == 422
def test_media_handoff_accepts_english_arr_categories(monkeypatch):
"""serienen/videoen muessen durchgehen (sonarrEN 8991 / radarrEN 7880).
16.09.2026: fehlten in allowed_categories -> HTTP 422 -> movetdarr.sh
brach mit "n8n-Handoff konnte nicht registriert werden" ab und liess
Mutiny.2026 x2 tagelang in /usenet/complete/videoen liegen.
"""
seen = []
class FakeResponse:
status_code = 200
def json(self):
return {"ok": True, "jobId": "test-job-5678", "state": "registered"}
class FakeClient:
def __init__(self, **_kwargs):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *_args):
return None
async def post(self, url, json, headers):
seen.append(json)
return FakeResponse()
monkeypatch.setattr(app.httpx, "AsyncClient", FakeClient)
with TestClient(app.app) as client:
monkeypatch.setattr(app, "SERVICES", {"n8n": {"url": "http://n8n:5678", "auth": "n8n"}})
for category in ("serienen", "videoen"):
response = client.post(
"/media/handoff",
headers={"Authorization": "Bearer test-token"},
json={
"action": "start",
"category": category,
"directory": f"/usenet/complete/{category}/Release.2026",
"release": "Release.2026-GRP",
"cleanName": "Release 2026",
"expectedFiles": 1,
},
)
assert response.status_code == 200, (category, response.text)
assert response.json()["state"] == "registered", category
assert [item["category"] for item in seen] == ["serienen", "videoen"]
def test_media_handoff_still_rejects_unknown_category(monkeypatch):
"""Fail-closed bleibt: eine frei erfundene Kategorie wird nicht geproxyt."""
monkeypatch.setattr(
app.httpx,
"AsyncClient",
lambda **_kwargs: (_ for _ in ()).throw(AssertionError("must not proxy")),
)
with TestClient(app.app) as client:
response = client.post(
"/media/handoff",
headers={"Authorization": "Bearer test-token"},
json={
"action": "start",
"category": "hoerbuecher",
"directory": "/usenet/complete/hoerbuecher/Buch",
"release": "Buch",
},
)
assert response.status_code == 422
def test_media_handoff_english_category_path_must_match(monkeypatch):
"""Pfadpruefung gilt auch fuer die neuen Kategorien."""
monkeypatch.setattr(
app.httpx,
"AsyncClient",
lambda **_kwargs: (_ for _ in ()).throw(AssertionError("must not proxy")),
)
with TestClient(app.app) as client:
response = client.post(
"/media/handoff",
headers={"Authorization": "Bearer test-token"},
json={
"action": "start",
"category": "videoen",
"directory": "/usenet/complete/video4k/Wrong",
"release": "Wrong",
},
)
assert response.status_code == 422
def test_paperless_import_queues_pdf_through_butler(monkeypatch):
captured = {}