diff --git a/src/musiksammlung/llm_parser.py b/src/musiksammlung/llm_parser.py index c69118c..25d2e90 100644 --- a/src/musiksammlung/llm_parser.py +++ b/src/musiksammlung/llm_parser.py @@ -17,20 +17,23 @@ SYSTEM_PROMPT = """\ Du bist ein Parser für CD-Tracklisten. Extrahiere die Metadaten als JSON. REGELN: -- "artist": Wenn verschiedene Interpreten pro Track → "Various Artists". \ -NUR wenn alle Tracks denselben Interpreten haben, nimm diesen als artist. +- "artist" (Album-Ebene): Hauptinterpret. Bei Samplern/Compilations → "Various Artists". - "album": Der Albumtitel (z.B. "Deutsche Volkslieder"). - "year": NUR wenn ein Jahr explizit im Text steht. Sonst null. NICHTS ERFINDEN. -- "title": NUR der Songtitel. KEINE Komponisten, KEINE Interpreten, KEINE Zeitangaben. +- "title": NUR der Songtitel. KEINE Komponisten, KEINE Zeitangaben. Beispiel: "Wer recht in Freuden wandern will" — NICHT \ "Wer recht in Freuden wandern will (Klauer – Geibel)" +- "artist" (Track-Ebene, optional): NUR setzen wenn der Track-Interpret vom Album-Künstler \ +abweicht (z.B. bei Samplern oder Klassik mit verschiedenen Solisten/Dirigenten). \ +Ist der Interpret überall gleich, dieses Feld weglassen. - Jede Tracknummer darf nur EINMAL vorkommen. Keine Duplikate. - "CD 1", "CD 2", "Disc 1" etc. → eigene disc_number. Sonst disc_number=1. Gib ausschließlich valides JSON zurück: {"artist":"Various Artists","album":"Albumname","year":null,\ "discs":[{"disc_number":1,"name":null,\ -"tracks":[{"track_number":1,"title":"Nur der Songtitel"}]}]} +"tracks":[{"track_number":1,"title":"Songtitel","artist":"Interpret A"},\ +{"track_number":2,"title":"Songtitel","artist":"Interpret B"}]}]} /no_think""" diff --git a/src/musiksammlung/ripper.py b/src/musiksammlung/ripper.py index e1dadd0..1443413 100644 --- a/src/musiksammlung/ripper.py +++ b/src/musiksammlung/ripper.py @@ -11,6 +11,9 @@ from typing import NamedTuple from pydantic import BaseModel from musiksammlung.config import AudioFormat +from musiksammlung.models import Album as AlbumModel +from musiksammlung.models import Disc as DiscModel +from musiksammlung.models import Track as TrackModel logger = logging.getLogger(__name__) @@ -433,6 +436,7 @@ def interactive_rip(config: RipperConfig) -> None: album_name = f"Album{album_counter}" disc_counter = 1 + all_discs: list[DiscModel] = [] while True: print(f"\n Album: {album_name}") @@ -466,6 +470,17 @@ def interactive_rip(config: RipperConfig) -> None: print(f" ✓ Done — {len(tracks)} tracks") for t in tracks: print(f" {t.track_number:2d}. {t.title} [{t.artist}]") + all_discs.append(DiscModel( + disc_number=disc_num, + tracks=[ + TrackModel( + track_number=t.track_number, + title=t.title, + artist=t.artist, + ) + for t in tracks + ], + )) else: print(" ✓ Done (no CDDB data)") @@ -483,6 +498,17 @@ def interactive_rip(config: RipperConfig) -> None: disc_counter += 1 + if all_discs: + artist = all_discs[0].tracks[0].artist or album_name + album_model = AlbumModel(artist=artist, album=album_name, discs=all_discs) + album_root = config.output_dir / _sanitize_name(album_name) + json_path = album_root / "album.json" + json_path.write_text( + album_model.model_dump_json(indent=2), encoding="utf-8" + ) + print(f"\n album.json gespeichert: {json_path}") + print(" → Weiter mit: musiksammlung apply album.json") + raw_album = input("\nNext album? (y/n): ") if _clean_input(raw_album).lower() != "y": break diff --git a/src/musiksammlung/vision_llm.py b/src/musiksammlung/vision_llm.py index 1ac84a4..92c02b8 100644 --- a/src/musiksammlung/vision_llm.py +++ b/src/musiksammlung/vision_llm.py @@ -20,14 +20,17 @@ Lies das Foto einer CD-Rückseite oder eines Booklets ab. Das Bild kann gedreht Extrahiere daraus die Metadaten und die vollständige Trackliste. WICHTIG: -- "artist" ist der Hauptinterpret oder "Various Artists" bei Samplern/Compilations. +- "artist" (Album-Ebene): Hauptinterpret oder "Various Artists" bei Samplern/Compilations. - "album" ist der Albumtitel (z.B. "Deutsche Volkslieder", "Abbey Road"). - "year" ist das Erscheinungsjahr (Zahl oder null wenn nicht sichtbar). - Lies die Tracktitel GENAU so ab, wie sie auf der CD stehen. - Achte besonders auf korrekte deutsche Umlaute (ä, ö, ü, ß). +- Lasse Zeitangaben (z.B. "3:12") aus dem Titel weg. +- "artist" (Track-Ebene, optional): NUR setzen wenn der Interpret dieses Tracks vom \ +Album-Künstler abweicht (z.B. bei Samplern oder Klassik mit verschiedenen Solisten). \ +Ist der Interpret überall gleich, dieses Feld weglassen. - 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. @@ -36,8 +39,8 @@ Antworte NUR mit dem JSON, ohne Erklärung. Beispiel: {"artist":"Various Artists","album":"Deutsche Volkslieder","year":null,""" # noqa: E501 VISION_PROMPT += """"discs":[{"disc_number":1,"name":null,"tracks":[""" -VISION_PROMPT += """{"track_number":1,"title":"Erster Song"},""" -VISION_PROMPT += """{"track_number":2,"title":"Zweiter Song"}]}]}""" +VISION_PROMPT += """{"track_number":1,"title":"Erster Song","artist":"Interpret A"},""" +VISION_PROMPT += """{"track_number":2,"title":"Zweiter Song","artist":"Interpret B"}]}]}""" VISION_PROMPT += """ Jetzt lies das Bild ab und gib das vollständige JSON aus. /no_think"""