diff --git a/src/musiksammlung/cli.py b/src/musiksammlung/cli.py index 6b2cc32..afab349 100644 --- a/src/musiksammlung/cli.py +++ b/src/musiksammlung/cli.py @@ -13,7 +13,7 @@ from musiksammlung.cover import copy_covers from musiksammlung.llm_parser import parse_tracklist from musiksammlung.models import Album from musiksammlung.ocr import ocr_images -from musiksammlung.organizer import apply_mapping, build_mapping, check_disc_counts +from musiksammlung.organizer import apply_mapping, build_mapping from musiksammlung.playlist import generate_playlist from musiksammlung.ripper import RipperConfig, interactive_rip from musiksammlung.tagger import tag_album @@ -90,7 +90,7 @@ def scan( ), languages: str = typer.Option("deu+eng", "--lang", "-l", help="OCR-Sprachen"), backend: str = typer.Option("ollama", "--backend", "-b", help="LLM-Backend"), - model: str = typer.Option("gemma3:12b", "--model", "-m", help="Text-LLM-Modell"), + model: str = typer.Option("llama3", "--model", "-m", help="Text-LLM-Modell"), base_url: str = typer.Option( "http://localhost:11434", "--url", help="LLM-API-URL" ), @@ -146,36 +146,6 @@ def apply( raw = json.loads(album_json.read_text(encoding="utf-8")) album = Album.model_validate(raw) - # Prüfe Track-Anzahl pro Disc - checks = check_disc_counts(album, input_dir) - problems = [c for c in checks if not c.ok] - if problems: - typer.echo( - "\nFEHLER: Track-Diskrepanz zwischen gerippten Dateien und album.json:\n", - err=True, - ) - for c in checks: - status = "OK" if c.ok else "!!" - typer.echo( - f" [{status}] Disc {c.disc_number}: " - f"{c.audio_file_count} Datei(en), {c.json_track_count} JSON-Track(s)", - err=True, - ) - if c.surplus_files: - typer.echo( - f" → {c.surplus_files} Track(s) fehlen im JSON " - f"(Tracks {c.json_track_count + 1}–{c.audio_file_count} eintragen)", - err=True, - ) - elif c.surplus_json: - typer.echo( - f" → {c.surplus_json} JSON-Eintrag/Einträge ohne Audiodatei " - f"(Tracks {c.audio_file_count + 1}–{c.json_track_count} prüfen)", - err=True, - ) - typer.echo(f"\nBitte {album_json} korrigieren und erneut aufrufen.", err=True) - raise typer.Exit(1) - mapping = build_mapping(album, input_dir, output_dir) typer.echo(f"Mapping: {len(mapping)} Dateien") for src, dst in mapping.items(): @@ -292,7 +262,7 @@ def process( ), languages: str = typer.Option("deu+eng", "--lang", "-l"), backend: str = typer.Option("ollama", "--backend", "-b"), - model: str = typer.Option("gemma3:12b", "--model", "-m"), + model: str = typer.Option("llama3", "--model", "-m"), base_url: str = typer.Option("http://localhost:11434", "--url"), dry_run: bool = typer.Option(False, "--dry-run"), ) -> None: diff --git a/src/musiksammlung/llm_parser.py b/src/musiksammlung/llm_parser.py index c69118c..1b0a98e 100644 --- a/src/musiksammlung/llm_parser.py +++ b/src/musiksammlung/llm_parser.py @@ -47,7 +47,7 @@ def _call_ollama(text: str, model: str, base_url: str) -> str: ], "stream": False, }, - timeout=300.0, + timeout=120.0, ) response.raise_for_status() return response.json()["message"]["content"] @@ -71,7 +71,7 @@ def _call_openai_compatible( {"role": "user", "content": text}, ], }, - timeout=300.0, + timeout=120.0, ) response.raise_for_status() return response.json()["choices"][0]["message"]["content"] diff --git a/src/musiksammlung/organizer.py b/src/musiksammlung/organizer.py index 1c00419..22a6a3e 100644 --- a/src/musiksammlung/organizer.py +++ b/src/musiksammlung/organizer.py @@ -5,7 +5,6 @@ from __future__ import annotations import logging import re import shutil -from dataclasses import dataclass from pathlib import Path from musiksammlung.config import AUDIO_EXTENSIONS @@ -14,29 +13,6 @@ from musiksammlung.models import Album logger = logging.getLogger(__name__) -@dataclass -class DiscCheck: - """Ergebnis der Track-Zählung für eine einzelne Disc.""" - - disc_number: int - audio_file_count: int - json_track_count: int - - @property - def ok(self) -> bool: - return self.audio_file_count == self.json_track_count - - @property - def surplus_files(self) -> int: - """Dateien ohne JSON-Eintrag (Tracks fehlen im JSON).""" - return max(0, self.audio_file_count - self.json_track_count) - - @property - def surplus_json(self) -> int: - """JSON-Einträge ohne Datei (Dateien fehlen im Verzeichnis).""" - return max(0, self.json_track_count - self.audio_file_count) - - def _sanitize_filename(name: str) -> str: """Entfernt problematische Zeichen aus Dateinamen.""" return re.sub(r'[<>:"/\\|?*]', "_", name).strip() @@ -52,31 +28,6 @@ def discover_audio_files(directory: Path) -> list[Path]: return sorted(files, key=extract_number) -def check_disc_counts(album: Album, input_dir: Path) -> list[DiscCheck]: - """Vergleicht Dateianzahl und JSON-Track-Anzahl pro Disc. - - Args: - album: Validiertes Album-Modell - input_dir: Verzeichnis mit gerippten Dateien (enthält CD1/, CD2/, ... bei Multi-Disc) - - Returns: - Liste von DiscCheck-Objekten — auch für korrekte Discs (ok=True). - """ - multi_disc = len(album.discs) > 1 - results: list[DiscCheck] = [] - - for disc in album.discs: - source_dir = input_dir / f"CD{disc.disc_number}" if multi_disc else input_dir - file_count = len(discover_audio_files(source_dir)) if source_dir.exists() else 0 - results.append(DiscCheck( - disc_number=disc.disc_number, - audio_file_count=file_count, - json_track_count=len(disc.tracks), - )) - - return results - - def build_mapping( album: Album, input_dir: Path, diff --git a/src/musiksammlung/vision_llm.py b/src/musiksammlung/vision_llm.py index 1ac84a4..171d23e 100644 --- a/src/musiksammlung/vision_llm.py +++ b/src/musiksammlung/vision_llm.py @@ -28,9 +28,6 @@ WICHTIG: - Wenn "CD 1", "CD 2", "Disc 1" etc. sichtbar sind, erstelle mehrere Einträge in "discs". - Ohne Disc-Angabe: eine Disc mit disc_number=1. - Lasse Zeitangaben (z.B. "3:12") und Interpretenangaben pro Track weg. -- MEHRSPALTIGE LAYOUTS: CD-Rückseiten haben oft 2, 3 oder 4 Spalten nebeneinander. - Lies ALLE Spalten vollständig von oben nach unten, bevor du zur nächsten Spalte gehst. - Überspringen oder Auslassen von Spalten ist ein häufiger Fehler — lies jede Spalte komplett. Antworte NUR mit dem JSON, ohne Erklärung. Beispiel: