From 8212a918dd9b972c1012b3899c6f244209eecc88 Mon Sep 17 00:00:00 2001 From: dschlueter Date: Wed, 29 Apr 2026 08:43:12 +0200 Subject: [PATCH] =?UTF-8?q?Add=20genre=20normalization:=20German/variant?= =?UTF-8?q?=20genres=20=E2=86=92=20canonical=20Jellyfin=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _GENRE_MAP translates common German genre names (Volksmusik, Schlager, Marschmusik, Klassik …) and English variants (rhythm and blues, swing music …) to consistent Jellyfin-friendly labels. All-upper or all-lower genres without a mapping entry are title-cased. Applied in resolve() before building AlbumProposal. Co-Authored-By: Claude Sonnet 4.6 --- metadata_resolver.py | 59 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/metadata_resolver.py b/metadata_resolver.py index f109d04..94a1ca7 100755 --- a/metadata_resolver.py +++ b/metadata_resolver.py @@ -33,6 +33,63 @@ try: except ImportError: HAS_ANTHROPIC = False +# --------------------------------------------------------------------------- +# Genre normalization +# --------------------------------------------------------------------------- + +_GENRE_MAP: Dict[str, str] = { + # Deutsch → Englisch (Jellyfin-Standardbegriffe) + "volksmusik": "Folk", + "volkslieder": "Folk", + "volkslied": "Folk", + "heimatlieder": "Folk", + "schlager": "Schlager", + "deutsche schlager": "Schlager", + "marsch": "March", + "marschmusik": "March", + "militaermusik": "March", + "militärmusik": "March", + "kirchenmusik": "Sacred", + "chormusik": "Choral", + "kinderlieder": "Children", + "weihnachtslieder": "Christmas", + "weihnachtsmusik": "Christmas", + "blasmusik": "Brass Band", + "operette": "Operetta", + "oper": "Opera", + "kammermusik": "Chamber Music", + "klassik": "Classical", + "classic": "Classical", + "klassische musik": "Classical", + "barock": "Baroque", + "romantik": "Romantic", + # Englische Varianten vereinheitlichen + "rhythm and blues": "R&B", + "rhythmic soul": "R&B", + "rock and roll": "Rock 'n' Roll", + "rock & roll": "Rock 'n' Roll", + "easy listening": "Easy Listening", + "vocal pop": "Pop", + "adult contemporary": "Pop", + "big band": "Big Band", + "swing music": "Swing", + "latin jazz": "Latin Jazz", + "bossa nova": "Bossa Nova", + "nueva cancion": "Nueva Canción", +} + + +def normalize_genre(genre: Optional[str]) -> Optional[str]: + if not genre: + return genre + key = genre.strip().lower() + normalized = _GENRE_MAP.get(key) + if normalized: + return normalized + # Titlcase wenn nicht in der Map (verhindert ALL CAPS oder all lowercase) + return genre.strip().title() if genre == genre.upper() or genre == genre.lower() else genre.strip() + + _MB_RATE_LIMIT = 1.1 # seconds between MusicBrainz requests _last_mb_call = 0.0 ACOUSTID_API_KEY = os.getenv("ACOUSTID_API_KEY", "") @@ -505,7 +562,7 @@ def resolve( album=album, albumartist=albumartist, date=year, - genre=genre, + genre=normalize_genre(genre), label=label, mbid=release_mbid, cover_path=None,