Jikan/MAL-Quelle + Job-Titel + Abbrechen-Knopf + Dark-Hover + Ordner-Verwaltung
Ampel / ampel (push) Failing after 38s
Ampel / ampel (push) Failing after 38s
Commander-Befunde (Screenshots 23.07. abends):
- Jikan (MyAnimeList, kostenlos OHNE Key) als Anime-Quelle vor OMDb —
waehlt per Titel-Aehnlichkeit (SequenceMatcher, Schwelle 0.55) statt
blind Treffer 1; damit trifft "Evangelion 2.22" den exakten Film
- Job-Titel: POST /jobs uebernimmt den erkannten Disc-Titel aus dem
DISC_CACHE — Schluss mit "Unbekannt" in der Queue
- Kooperativer Job-Abbruch: POST /jobs/{id}/cancel setzt canceling,
der Worker prueft das Flag bei jedem Fortschritts-Update und killt
den Encoder-Prozess sauber (RipAbbruch); UI-Knopf "Abbrechen" fuer
laufende, Status-Badge "Wird abgebrochen"
- Dark-Mode: Job-Zeilen-Hover war hart hell (hover:bg-slate-50)
- Speicherziele: lokaler Ordner-Browser mit "Ordner anlegen"
(GET /browse + POST /browse/mkdir, nur unter /app/media)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ from detection import detect_disc_type
|
||||
from ripping import (
|
||||
DEFAULT_HB_PRESET,
|
||||
RIP_OUTPUT_DIR,
|
||||
RipAbbruch,
|
||||
rip_cd,
|
||||
rip_video,
|
||||
run_handbrake,
|
||||
@@ -40,8 +41,20 @@ def _zielbasis(target_dir, disc_type: str) -> str:
|
||||
return os.path.join(RIP_OUTPUT_DIR, disc_type)
|
||||
|
||||
|
||||
def _abbruch_angefordert(job_id: str) -> bool:
|
||||
"""Kooperativer Abbruch: hat der Nutzer über die API abgebrochen?"""
|
||||
return db.get_job_status(job_id) == "canceling"
|
||||
|
||||
|
||||
def _job_abschliessen(job_id: str, ergebnis: dict) -> None:
|
||||
"""Schreibt den Endzustand eines Jobs (completed/failed) nach Postgres."""
|
||||
if ergebnis.get("status") == "cancelled":
|
||||
db.update_job(
|
||||
job_id, status="failed", error="Abgebrochen durch Nutzer",
|
||||
finished_at=db.utcnow(),
|
||||
)
|
||||
db.add_log("warning", "worker", f"Job {job_id}: abgebrochen — Rohdaten bleiben erhalten")
|
||||
return
|
||||
if ergebnis.get("status") == "success":
|
||||
db.update_job(
|
||||
job_id,
|
||||
@@ -69,6 +82,11 @@ def rip_disc(self, device_path: str, job_id: str, target_dir: str = None):
|
||||
dort eingehängte Shares (NFS/SMB) sind damit direkt wählbar.
|
||||
"""
|
||||
db.init_db()
|
||||
|
||||
if _abbruch_angefordert(job_id):
|
||||
_job_abschliessen(job_id, {"status": "cancelled"})
|
||||
return {"status": "cancelled"}
|
||||
|
||||
disc_type = detect_disc_type(device_path)
|
||||
|
||||
if disc_type in ("no_disc", "unknown"):
|
||||
@@ -90,6 +108,8 @@ def rip_disc(self, device_path: str, job_id: str, target_dir: str = None):
|
||||
if progress == letzter[0]:
|
||||
return
|
||||
letzter[0] = progress
|
||||
if _abbruch_angefordert(job_id):
|
||||
raise RipAbbruch()
|
||||
self.update_state(
|
||||
state="PROGRESS",
|
||||
meta={"progress": progress, "status": "ripping", "message": message},
|
||||
@@ -162,14 +182,23 @@ def transcode_files(self, job_id: str, raw_dir: str, final_dir: str):
|
||||
)
|
||||
|
||||
anzahl = len(quellen)
|
||||
letzter = [-1]
|
||||
for index, quelle in enumerate(quellen):
|
||||
ziel = os.path.join(final_dir, os.path.basename(quelle))
|
||||
|
||||
def datei_fortschritt(p, _index=index):
|
||||
gesamt = int((_index * 100 + p) / anzahl)
|
||||
if gesamt == letzter[0]:
|
||||
return
|
||||
letzter[0] = gesamt
|
||||
if _abbruch_angefordert(job_id):
|
||||
raise RipAbbruch()
|
||||
db.update_job(job_id, progress=min(99, gesamt))
|
||||
|
||||
hb = run_handbrake(quelle, ziel, preset=preset, progress_cb=datei_fortschritt)
|
||||
if hb.get("status") == "cancelled":
|
||||
_job_abschliessen(job_id, hb)
|
||||
return hb
|
||||
if hb.get("status") != "success":
|
||||
ergebnis = {
|
||||
"status": "error",
|
||||
|
||||
Reference in New Issue
Block a user