LLM-Parser-Tests, check-Befehl und Cover-Doku
tests/test_llm_parser.py: 13 Tests für _call_ollama, _call_openai_compatible und parse_tracklist (Retry-Logik, Markdown-Block, Track-Artist, Mock) cli: neuer check-Befehl zeigt Tags und Cover-Status aller Audiodateien; ♪ markiert Dateien mit eingebettetem Cover BEDIENUNGSANLEITUNG: neuer Abschnitt 7 (check-Befehl), Cover-Konvention (frontcover.jpg/backcover.jpg, Embedding, 500px) in Schritt 3 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
256be0ae33
commit
88b89fbb50
3 changed files with 261 additions and 4 deletions
|
|
@ -16,6 +16,9 @@ from musiksammlung.ocr import ocr_images
|
|||
from musiksammlung.organizer import apply_mapping, build_mapping, check_disc_counts
|
||||
from musiksammlung.playlist import generate_playlist
|
||||
from musiksammlung.ripper import RipperConfig, interactive_rip
|
||||
from mutagen import File as MutagenFile
|
||||
|
||||
from musiksammlung.config import AUDIO_EXTENSIONS
|
||||
from musiksammlung.tagger import embed_album_cover, tag_album
|
||||
from musiksammlung.vision_llm import parse_image
|
||||
|
||||
|
|
@ -373,5 +376,81 @@ def process(
|
|||
typer.echo(f"Fertig! Album: {album_dir}")
|
||||
|
||||
|
||||
@app.command()
|
||||
def check(
|
||||
directory: Path = typer.Argument(..., help="Album- oder Disc-Verzeichnis"),
|
||||
) -> None:
|
||||
"""Zeigt Audio-Tags und Cover-Status aller Dateien in einem Verzeichnis.
|
||||
|
||||
Durchsucht das Verzeichnis rekursiv nach Audiodateien und gibt für jede
|
||||
Datei die wichtigsten Tags aus. Zeigt außerdem ob frontcover.jpg/backcover.jpg
|
||||
vorhanden sind und ob ein Cover eingebettet ist.
|
||||
"""
|
||||
if not directory.exists():
|
||||
typer.echo(f"Fehler: Verzeichnis nicht gefunden: {directory}", err=True)
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Cover-Status auf Album-Ebene
|
||||
front = find_cover(directory, "front")
|
||||
back = find_cover(directory, "back")
|
||||
typer.echo(f"\nVerzeichnis: {directory}")
|
||||
typer.echo(f" frontcover: {front.name if front else '— (fehlt)'}")
|
||||
typer.echo(f" backcover: {back.name if back else '— (fehlt)'}")
|
||||
|
||||
# Alle Audiodateien finden (flach + Unterverzeichnisse)
|
||||
audio_files: list[Path] = sorted(
|
||||
(f for f in directory.rglob("*") if f.suffix.lower() in AUDIO_EXTENSIONS),
|
||||
key=lambda p: (p.parent.name, p.name),
|
||||
)
|
||||
|
||||
if not audio_files:
|
||||
typer.echo("\n Keine Audiodateien gefunden.")
|
||||
return
|
||||
|
||||
current_subdir: str | None = None
|
||||
for path in audio_files:
|
||||
subdir = path.parent.name if path.parent != directory else ""
|
||||
if subdir != current_subdir:
|
||||
current_subdir = subdir
|
||||
typer.echo(f"\n {subdir or '.'}/" if subdir else "\n ./")
|
||||
|
||||
audio = MutagenFile(str(path), easy=True)
|
||||
if audio is None:
|
||||
typer.echo(f" {path.name} [unlesbares Format]")
|
||||
continue
|
||||
|
||||
def tag(key: str) -> str:
|
||||
vals = audio.get(key)
|
||||
return vals[0] if vals else "—"
|
||||
|
||||
has_cover = _has_embedded_cover(path)
|
||||
cover_marker = "♪" if has_cover else " "
|
||||
|
||||
typer.echo(
|
||||
f" [{cover_marker}] {path.name}\n"
|
||||
f" Titel: {tag('title')}\n"
|
||||
f" Künstler: {tag('artist')} | AlbumArtist: {tag('albumartist')}\n"
|
||||
f" Album: {tag('album')} | Jahr: {tag('date')}\n"
|
||||
f" Track: {tag('tracknumber')} | Disc: {tag('discnumber')}"
|
||||
)
|
||||
|
||||
|
||||
def _has_embedded_cover(path: Path) -> bool:
|
||||
"""Prüft ob eine Audiodatei ein eingebettetes Cover enthält."""
|
||||
from mutagen.flac import FLAC
|
||||
from mutagen.mp3 import MP3
|
||||
|
||||
suffix = path.suffix.lower()
|
||||
try:
|
||||
if suffix == ".flac":
|
||||
return bool(FLAC(str(path)).pictures)
|
||||
if suffix == ".mp3":
|
||||
tags = MP3(str(path)).tags
|
||||
return tags is not None and any(k.startswith("APIC") for k in tags.keys())
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue