Add genre normalization: German/variant genres → canonical Jellyfin names

_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 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-04-29 08:43:12 +02:00
commit 8212a918dd

View file

@ -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,