feat(ui): SoC Refactoring & Config Validation

- Theme Context ausgelagert (ThemeContext.tsx + useDarkMode.ts)
- Config Validation mit TMDB-API-Key Pflicht (config_validation.py)
- Cache Key Centralization (cache/keys.py)
- CD-Ripping mit abcde implementiert (Worker)
- Docker Compose mit Healthchecks & LOG_LEVEL
- ROADMAP.md & SAVEPOINT.md aktualisiert
This commit is contained in:
Hitonabi
2026-07-22 16:38:19 +02:00
parent e710151c91
commit 95c1f9b105
18 changed files with 406 additions and 104 deletions
+90
View File
@@ -103,6 +103,16 @@ def detect_disc_type(device_path: str) -> str:
return "unknown"
def check_abcde_installed() -> bool:
"""Prüft, ob abcde installiert ist."""
return shutil.which("abcde") is not None
def check_cdparanoia_installed() -> bool:
"""Prüft, ob cdparanoia installiert ist."""
return shutil.which("cdparanoia") is not None
@celery_app.task(bind=True, name="worker.ripping.rip_dvd")
def rip_dvd(self, device_path: str, disc_id: str) -> dict:
"""Rippt eine DVD mit HandBrake."""
@@ -133,3 +143,83 @@ def rip_bluray(self, device_path: str, disc_id: str) -> dict:
output_path = f"{output_dir}/bluray_{disc_id}.mkv"
return run_handbrake(device_path, output_path)
@celery_app.task(bind=True, name="worker.ripping.rip_cd")
def rip_cd(self, device_path: str, disc_id: str) -> dict:
"""Rippt eine CD mit abcde."""
if not check_abcde_installed():
return {
"status": "error",
"error": "abcde ist nicht installiert. Installiere abcde und cdparanoia für CD-Ripping."
}
if not check_cdparanoia_installed():
return {
"status": "error",
"error": "cdparanoia ist nicht installiert. Installiere abcde und cdparanoia für CD-Ripping."
}
output_dir = f"/output/cd/{disc_id}"
os.makedirs(output_dir, exist_ok=True)
try:
cmd = [
"abcde",
"-d", device_path,
"-o", "flac",
"-b",
"-t", "1",
"-a", "default",
"-x",
"-o", output_dir
]
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1
)
for line in process.stdout:
self.send_task(
"worker.ripping.update_progress",
kwargs={
"progress": 50,
"status": "ripping",
"message": f"Rippe CD: {line.strip()}"
}
)
process.wait()
if process.returncode == 0:
# MusikBrainz Lookup (via abcde's post-read)
self.send_task(
"worker.ripping.update_progress",
kwargs={
"progress": 90,
"status": "ripping",
"message": "MusikBrainz Lookup läuft..."
}
)
return {
"status": "success",
"output_dir": output_dir,
"return_code": process.returncode
}
else:
return {
"status": "error",
"error": f"abcde failed with code {process.returncode}",
"return_code": process.returncode
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
+2 -6
View File
@@ -1,6 +1,6 @@
import os
from celery_app import celery_app
from ripping import rip_dvd, rip_bluray, detect_disc_type
from ripping import rip_dvd, rip_bluray, rip_cd, detect_disc_type
@celery_app.task(bind=True, name="worker.tasks.rip_disc")
@@ -12,11 +12,7 @@ def rip_disc(self, device_path: str, disc_id: str = None):
disc_id = device_path.replace("/", "_")
if disc_type == "cd":
return {
"status": "not_implemented",
"message": "CD-Ripping mit abcde noch nicht implementiert",
"disc_type": disc_type
}
return rip_cd(device_path, disc_id)
elif disc_type == "dvd":
return rip_dvd(device_path, disc_id)
elif disc_type == "bluray":