sabnzbd/usenet-scripts/test_movetdarr.py
Trulla 5b4dd2384e Allow serienen/videoen categories in movetdarr
Die Kategorie-Whitelist kannte nur serien4k|serien|video4k|video. Die
englischen Arr-Instanzen sonarrEN (Port 8991, Root /data/FHD/serienen) und
radarrEN (Port 7880, Root /data/FHD/videoen) nutzen aber serienen/videoen.

Folge: Downloads dieser Kategorien wurden mit "nicht unterstuetzte Kategorie"
abgewiesen und blieben in /usenet/complete liegen, ohne je in den
FileFlows/Arr-Flow zu gelangen. Gefunden: Mutiny.2026 x2 (08./09.09.2026,
zusammen ~21 GB), beide Filme in radarrEN weiter monitored/hasFile=false.

/tdarr/complete/{serienen,videoen} existieren bereits; die FileFlows-Library
scannt /tdarr/complete rekursiv, es braucht also nur die Whitelist.

2 neue Testfaelle (serienen + videoen), test_movetdarr.py und
test_movetdarr_parallel.py gruen.
2026-09-16 11:55:01 +02:00

124 lines
4.9 KiB
Python

import json
import os
import subprocess
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
SCRIPT = Path(__file__).with_name("movetdarr.sh")
actions = []
status_polls = 0
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
global status_polls
data = json.loads(self.rfile.read(int(self.headers.get("Content-Length", "0"))))
actions.append(data)
action = data.get("action")
if action == "start":
result = {"ok": True, "jobId": "test-job-1234", "state": "registered"}
elif action == "moved":
result = {"ok": True, "jobId": "test-job-1234", "state": "processing"}
elif action == "status":
status_polls += 1
result = {"ok": True, "jobId": "test-job-1234", "state": "success" if status_polls >= 2 else "processing"}
else:
result = {"ok": True, "jobId": "test-job-1234", "state": "failed"}
body = json.dumps(result).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *_args):
pass
def run_case(root, category="serien4k", status="0", name="Show.S01E01", target_video=False):
src_root = root / "source"
dst_root = root / "dest"
source = src_root / category / name
source.mkdir(parents=True)
(source / "episode.mkv").write_bytes(b"video")
target = dst_root / category / name
if target_video:
target.mkdir(parents=True)
(target / "existing.mkv").write_bytes(b"existing")
env = os.environ | {
"SOURCE_ROOT": str(src_root),
"DEST_ROOT": str(dst_root),
"LOGFILE": str(root / "postprocess.log"),
"HANDOFF_URL": f"http://127.0.0.1:{server.server_port}/media/handoff",
"LEASE_LOCK": str(root / "lease.lock"),
"POLL_SECONDS": "0",
"MAX_POLLS": "3",
}
result = subprocess.run(
["bash", str(SCRIPT), str(source), name + ".nzb", name, "", category, "", status],
env=env,
text=True,
capture_output=True,
timeout=20,
)
return result, source, target
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, target = run_case(Path(td))
assert result.returncode == 0, (result.stdout, result.stderr)
assert not source.exists() and (target / "episode.mkv").exists()
assert [a["action"] for a in actions] == ["start", "moved", "status", "status"]
assert actions[0]["expectedFiles"] == "1"
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, _ = run_case(Path(td), category="other")
assert result.returncode == 1 and source.exists() and actions == []
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, target = run_case(Path(td), target_video=True)
assert result.returncode == 2 and source.exists() and (target / "existing.mkv").exists()
assert [a["action"] for a in actions] == ["start", "fail"]
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
root = Path(td)
stale = root / "dest" / "video" / "Movie.2026"
stale.mkdir(parents=True)
(stale / "leftover.nfo").write_text("stale")
result, source, target = run_case(root, category="video", name="Movie.2026")
assert result.returncode == 0 and not source.exists()
assert not (target / "leftover.nfo").exists() and (target / "episode.mkv").exists()
# 16.09.2026: serienen/videoen (englische Arr-Instanzen sonarrEN:8991 /
# radarrEN:7880 mit Root-Foldern /data/FHD/serienen bzw. /data/FHD/videoen)
# fehlten in der Kategorie-Whitelist. Folge: Mutiny.2026 x2 blieben am
# 08./09.09.2026 mit "nicht unterstuetzte Kategorie 'videoen'" in
# /usenet/complete liegen und wurden nie importiert.
for extra_category in ("serienen", "videoen"):
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, target = run_case(Path(td), category=extra_category)
assert result.returncode == 0, (extra_category, result.stdout, result.stderr)
assert not source.exists(), extra_category
assert (target / "episode.mkv").exists(), extra_category
assert [a["action"] for a in actions] == ["start", "moved", "status", "status"], extra_category
print("PASS: success lease, category fail-closed, no-overwrite, stale-directory cleanup, EN-Kategorien")
finally:
server.shutdown()
server.server_close()