diff --git a/docs/refactoring_plan.md b/docs/refactoring_plan.md index 47e5962..ec47a7f 100644 --- a/docs/refactoring_plan.md +++ b/docs/refactoring_plan.md @@ -204,3 +204,134 @@ Testvorgaben: - Track-Matching bei unterschiedlicher Track-Anzahl - disc_id korrekt pro Disc zugeordnet - Fehlende Quellen (None) robust behandelt + +--- + +## Parallelisierung: Informationsbeschaffung vor dem Rippen + +### Kernidee + +Die CD muss für zwei Operationen physisch im Laufwerk liegen, aber diese +brauchen sich nicht zu überlappen: + +``` +Operation A: TOC lesen (cd-discid) ~2 Sekunden +Operation B: Audio rippen (cdparanoia) 20–60 Minuten pro Disc +``` + +Heute sind beide in einem monolithischen Durchlauf verschmolzen. Das +Umordnen kostet nichts — die CD darf ruhig zweimal im Laufwerk liegen. + +### Neuer Ablauf (Drei Phasen A / B / C) + +``` +┌─────────────────────────────────────────────────────────┐ +│ Phase A: Informationsbeschaffung (~30s – 5 min) │ +│ │ +│ 1. CD einlegen (einmalig oder pro Disc bei Multi-Disc) │ +│ 2. cd-discid → TOC: disc_id, Offsets, Laufzeiten │ +│ 3. CDDB-Lookup → artist, album, year, genre, tracks │ +│ 4. EAN-Scan → MusicBrainz → Album + MBID │ +│ 5. CAA-Download → front.jpg, back.jpg │ +│ 6. back.jpg → Vision-LLM (läuft parallel, ~1–3 min) │ +│ 7. merge_album() → album.json (preliminary) │ +│ 8. "album.json bereit — bitte prüfen." │ +│ 9. User editiert album.json (optional, zweites │ +│ Terminal oder nach Prompt) │ +│ 10. "Rippen starten? [Enter]" │ +└─────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Phase B: Audio-Ripping (~20–60 min pro Disc) │ +│ │ +│ abcde läuft im Vordergrund (Terminal zeigt Progress) │ +│ Während des Rippens im zweiten Terminal: │ +│ - album.json editieren (jederzeit möglich) │ +│ - Vision-LLM-Ergebnis (falls noch ausstehend) │ +│ wird nach Abschluss nachgemergt │ +└─────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Phase C: Apply (~30s) │ +│ │ +│ Liest finales album.json (evtl. editiert während B) │ +│ organize → tag → embed → playlist │ +└─────────────────────────────────────────────────────────┘ +``` + +### Was der Hauptgewinn ist + +``` +Heute: Metadaten ── Rippen (60 min) ── album.json sehen ── editieren +Neu: Metadaten ── album.json sehen ── [editieren] ── Rippen (60 min) + └── Vision-LLM läuft parallel zum Rippen +``` + +Der User sieht die Metadaten **vor** dem langen Warten. Editing erfolgt +mit vollständiger Information, nicht blind im Nachhinein. + +### Warum kein Hintergrund-Ripping + +Hintergrund-Ripping (subprocess mit PID-Datei) ist technisch machbar, +erhöht aber die Komplexität (PID-Management, Fehlerbehandlung, Status- +Tracking) ohne entscheidenden Mehrwert: Der User kann während des +Vordergrund-Rippings jederzeit ein zweites Terminal öffnen und album.json +dort editieren. `$EDITOR album.json` reicht. + +### Abcde: keine Änderung nötig + +abcde hat Aktions-Flags (`-a cddb,read,encode,tag`), die eine Trennung +erlauben würden. Da wir CDDB und TOC aber bereits **vor** abcde über +eigene Module abfragen (`lookup_by_discid`, `cd-discid`), kann abcde +unverändert für Phase B genutzt werden — nur mit dem Wissen, dass seine +CDDB-Ausgabe bereits in album.json konsolidiert ist. + +### Multi-Disc-Verhalten + +``` +Disc 1: Phase A (~2 min) → "Rippen starten?" → Phase B Disc 1 (~45 min) +Disc 2: Phase A (~2 min) → "Rippen starten?" → Phase B Disc 2 (~45 min) +... +``` + +Jede Disc wird sequenziell behandelt (ein Laufwerk). Phase A aller Discs +könnte theoretisch vorab gebündelt werden (alle Discs kurz einlegen, TOC +lesen), ist aber als optionale Optimierung einzustufen. + +### Änderungsbedarf in ripper.py + +Die heutige `interactive_rip`-Funktion wird aufgeteilt in: + +```python +def gather_metadata(disc_num, config, scanner) -> DiscMetadata: + """Phase A: TOC, CDDB, MB, Vision-LLM — kein Rippen.""" + ... + +def rip_disc(disc_num, config) -> Path: + """Phase B: abcde — kein Netzwerk, kein LLM.""" + ... + +def interactive_rip(config): + """Orchestriert: für jede Disc gather_metadata → confirm → rip_disc.""" + ... +``` + +`DiscMetadata` ist ein internes Dataclass/NamedTuple das alle Rohdaten +einer Disc bis zum Merge trägt: + +```python +@dataclass +class DiscMetadata: + disc_number: int + discid_line: str # Rohstring aus cd-discid + cddb_result: CddbResult | None + mb_album: Album | None # nur bei erster Disc sinnvoll + mb_mbid: str | None + vision_album: Album | None # Ergebnis des Background-Threads + uploaded_photo: Path | None +``` + +Nach dem letzten `rip_disc`-Aufruf ruft `interactive_rip` einmalig +`merge_album()` auf und schreibt album.json.