Windows-Worker: Pfad-Mapping fuer echte Transcodes + leere-Titel-Meldung + E2E-Beweis dokumentiert
Ampel / ampel (push) Successful in 27s
Ampel / ampel (push) Successful in 27s
- pfad_lokal (tasks.py, mit Tests): RIPPY_PATH_MAP uebersetzt Container-Pfade (/app/temp, /app/media) auf Netzlaufwerke des nativen Workers — ohne Mapping unveraendert (Docker-Worker). - Rip-Dialog: 'done' mit 0 Titeln bekommt Klartext (Disc vermutlich nicht entschluesselbar) statt leerer Tabelle. - SAVEPOINT: E2E-Beweis des nativen Windows-Workers dokumentiert (test-windows-nativ online, HandBrake 1.9.2, echte LAN-IP). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+9
-2
@@ -17,8 +17,15 @@
|
|||||||
GitHub-Release (URL verifiziert), start-worker.bat (celery -Q transcode
|
GitHub-Release (URL verifiziert), start-worker.bat (celery -Q transcode
|
||||||
--pool=solo), optional -Autostart (schtasks onlogon). tasks.py lädt auf
|
--pool=solo), optional -Autostart (schtasks onlogon). tasks.py lädt auf
|
||||||
Windows ohne fcntl (Import-Guard); rip_disc ist dort hart verriegelt.
|
Windows ohne fcntl (Import-Guard); rip_disc ist dort hart verriegelt.
|
||||||
**E2E-BEWEIS: von diesem Windows-PC aus installiert, verbunden und ein
|
Für echte Transcodes übersetzt RIPPY_PATH_MAP die Container-Pfade aufs
|
||||||
echter Transcode-Job gegen die VM gefahren** (Ergebnis siehe unten).
|
Netzlaufwerk (pfad_lokal, mit Tests) — Voraussetzung bleibt eine
|
||||||
|
Freigabe der Rippy-Ablage (derselbe offene Infra-Punkt wie beim
|
||||||
|
Linux-Remote-Worker/AI-Box).
|
||||||
|
**E2E-BEWEIS (24.07., Commander-PC):** Installer in einem Rutsch
|
||||||
|
durchgelaufen (venv, Abhängigkeiten, HandBrakeCLI 1.9.2), Worker
|
||||||
|
gestartet und in Rippy erschienen:
|
||||||
|
`test-windows-nativ | online: True | HandBrake: 1.9.2 | IP: 192.168.178.98`
|
||||||
|
— echte LAN-IP, Celery-Ping grün. Testeintrag danach wieder entfernt.
|
||||||
- **Anleitungs-Tab** im UI: kompletter Selbsterklärer (Disc-Weg, Serien,
|
- **Anleitungs-Tab** im UI: kompletter Selbsterklärer (Disc-Weg, Serien,
|
||||||
NAS, Media-Server, Benachrichtigungen, Key, Worker, Downloads, FAQ).
|
NAS, Media-Server, Benachrichtigungen, Key, Worker, Downloads, FAQ).
|
||||||
- Design 2.0 kommt bewusst in eine FRISCHE Session (Commander-Entscheid) —
|
- Design 2.0 kommt bewusst in eine FRISCHE Session (Commander-Entscheid) —
|
||||||
|
|||||||
@@ -276,7 +276,13 @@ export default function RipTargetModal({ isOpen, initialType, discTitle, deviceI
|
|||||||
{scanStatus === 'error' && (
|
{scanStatus === 'error' && (
|
||||||
<p className="text-xs text-rose-500 px-3 py-2">{scanFehler}</p>
|
<p className="text-xs text-rose-500 px-3 py-2">{scanFehler}</p>
|
||||||
)}
|
)}
|
||||||
{scanStatus === 'done' && (
|
{scanStatus === 'done' && titelListe.length === 0 && (
|
||||||
|
<p className={`text-xs px-3 py-2 ${theme === 'dark' ? 'text-amber-400' : 'text-amber-600'}`}>
|
||||||
|
Keine Titel lesbar — vermutlich kann diese Disc nicht entschlüsselt
|
||||||
|
werden (Details stehen nach einem Rip-Versuch im Job-Fehler).
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{scanStatus === 'done' && titelListe.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="max-h-44 overflow-y-auto">
|
<div className="max-h-44 overflow-y-auto">
|
||||||
{titelListe.map(t => (
|
{titelListe.map(t => (
|
||||||
|
|||||||
@@ -46,6 +46,29 @@ from ripping import (
|
|||||||
|
|
||||||
API_URL = os.getenv("API_URL", "http://api:8000")
|
API_URL = os.getenv("API_URL", "http://api:8000")
|
||||||
|
|
||||||
|
|
||||||
|
def pfad_lokal(pfad: str, mapping: str = None) -> str:
|
||||||
|
"""Übersetzt Rippy-Container-Pfade für native Worker (pure Funktion).
|
||||||
|
|
||||||
|
Der native Windows-Worker sieht /app/temp und /app/media nicht — er
|
||||||
|
mountet Rippys Freigabe als Netzlaufwerk und setzt RIPPY_PATH_MAP,
|
||||||
|
z. B. "/app/media=Z:\\media;/app/temp=Y:\\temp". Ohne Mapping (Docker-
|
||||||
|
Worker: identische Mounts) kommt der Pfad unverändert zurück.
|
||||||
|
"""
|
||||||
|
mapping = mapping if mapping is not None else os.getenv("RIPPY_PATH_MAP", "")
|
||||||
|
if not pfad or not mapping:
|
||||||
|
return pfad
|
||||||
|
for paar in mapping.split(";"):
|
||||||
|
if "=" not in paar:
|
||||||
|
continue
|
||||||
|
quelle, ziel = paar.split("=", 1)
|
||||||
|
if quelle and pfad.startswith(quelle):
|
||||||
|
rest = pfad[len(quelle):]
|
||||||
|
if "\\" in ziel:
|
||||||
|
rest = rest.replace("/", "\\")
|
||||||
|
return ziel + rest
|
||||||
|
return pfad
|
||||||
|
|
||||||
RAW_DIR = os.getenv("RAW_DIR", "/app/temp/raw")
|
RAW_DIR = os.getenv("RAW_DIR", "/app/temp/raw")
|
||||||
MEDIA_ROOT = "/app/media"
|
MEDIA_ROOT = "/app/media"
|
||||||
|
|
||||||
@@ -453,6 +476,9 @@ def transcode_files(self, job_id: str, raw_dir: str, final_dir: str):
|
|||||||
Über POST /jobs/{id}/retry-transcode jederzeit neu anstoßbar.
|
Über POST /jobs/{id}/retry-transcode jederzeit neu anstoßbar.
|
||||||
"""
|
"""
|
||||||
db.init_db()
|
db.init_db()
|
||||||
|
# Native Worker (Windows) übersetzen Container-Pfade aufs Netzlaufwerk
|
||||||
|
raw_dir = pfad_lokal(raw_dir)
|
||||||
|
final_dir = pfad_lokal(final_dir)
|
||||||
quellen = sorted(glob.glob(os.path.join(raw_dir, "*.mkv")))
|
quellen = sorted(glob.glob(os.path.join(raw_dir, "*.mkv")))
|
||||||
if not quellen:
|
if not quellen:
|
||||||
ergebnis = {"status": "error", "error": f"Keine Roh-MKVs in {raw_dir} gefunden"}
|
ergebnis = {"status": "error", "error": f"Keine Roh-MKVs in {raw_dir} gefunden"}
|
||||||
|
|||||||
@@ -102,3 +102,13 @@ def test_matche_episoden_ohne_treffer_gibt_none():
|
|||||||
dateien = [600] # 10 min passt auf keine 24-min-Episode
|
dateien = [600] # 10 min passt auf keine 24-min-Episode
|
||||||
episoden = [(1, 1440), (2, 1440)]
|
episoden = [(1, 1440), (2, 1440)]
|
||||||
assert matche_episoden(dateien, episoden) is None
|
assert matche_episoden(dateien, episoden) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_pfad_lokal_uebersetzt_fuer_windows_worker():
|
||||||
|
from tasks import pfad_lokal
|
||||||
|
|
||||||
|
mapping = "/app/media=Z:\\media;/app/temp=Y:\\temp"
|
||||||
|
assert pfad_lokal("/app/temp/raw/abc", mapping) == "Y:\\temp\\raw\\abc"
|
||||||
|
assert pfad_lokal("/app/media/movies/Film (2020)", mapping) == "Z:\\media\\movies\\Film (2020)"
|
||||||
|
assert pfad_lokal("/woanders/x", mapping) == "/woanders/x"
|
||||||
|
assert pfad_lokal("/app/temp/raw/abc", "") == "/app/temp/raw/abc"
|
||||||
|
|||||||
Reference in New Issue
Block a user