Vervollständigt die Informationen über ein Musik-Datei-Verzeichnis, passt die Dateinamen und Metadaten der Dateien entsprechend an.
Find a file
dschlueter aaa32622b2 Fix 'Unknown' track artists leaking from bad ID3 tags and classical schema
- hint_extractor: filter existing tags through _is_good() so 'Unknown',
  'Unknown Artist' etc. in existing ID3 tags don't override filename-parsed
  artist names
- executor: _is_classical() now returns False when track_artist is a placeholder
  ('unknown', 'unknown artist') — prevents pop albums from getting the
  Performer-Composer-Work filename schema
- executor/music_enricher: pass albumartist to _proposed_filename() so fallback
  works when track artist is missing; fix display to use albumartist fallback too

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 08:15:46 +02:00
.gitignore Add project-specific .gitignore entries 2026-04-29 05:44:05 +02:00
BEDIENUNGSANLEITUNG.md Add BEDIENUNGSANLEITUNG.md (German user manual) 2026-04-29 06:52:44 +02:00
cover_handler.py Support WebP cover images: convert to JPEG via PIL, correct MIME type fallback 2026-04-29 05:50:46 +02:00
executor.py Fix 'Unknown' track artists leaking from bad ID3 tags and classical schema 2026-04-29 08:15:46 +02:00
hint_extractor.py Fix 'Unknown' track artists leaking from bad ID3 tags and classical schema 2026-04-29 08:15:46 +02:00
metadata_resolver.py Add YouTube ID detection and metadata lookup via yt-dlp 2026-04-29 05:42:03 +02:00
models.py Add YouTube ID detection and metadata lookup via yt-dlp 2026-04-29 05:42:03 +02:00
music_enricher.py Fix 'Unknown' track artists leaking from bad ID3 tags and classical schema 2026-04-29 08:15:46 +02:00
README.md Update README: Ollama/OpenRouter LLM, OCR, YouTube, WebP, underscore schema 2026-04-29 06:03:39 +02:00
scanner.py Recursive album discovery + Jellyfin Playlist Generator integration 2026-04-29 07:07:55 +02:00
test_suite_enricher.py Fix file permissions after rebase (644 → 755) 2026-04-29 06:01:38 +02:00

Music Metadata Enricher

KI-gestützter Musik-Metadaten-Enricher für Jellyfin-Bibliotheken.

Analysiert Album-Verzeichnisse, vervollständigt Tags, besorgt Cover-Art und benennt Dateien optional nach einem einheitlichen Schema um — vollständig ohne API-Key nutzbar, mit lokalem LLM (Ollama) für lückenhafte Metadaten.


Features

  • Lokale Analyse — Verzeichnisname, Dateinamen, bestehende ID3/FLAC/M4A-Tags, Tracklist-Textdateien (.txt, .htm, .html)
  • YouTube-ID-Erkennung — erkennt YouTube-Video-IDs im Dateinamen, holt Titel, Uploader und Kapitel automatisch via yt-dlp
  • OCR Back-Cover — liest Tracklisten aus back.jpg / Booklet-Bildern via Ollama Vision (qwen3-vl, minicpm-v)
  • MusicBrainz-Lookup — Textsuche + AcoustID-Fingerprinting (optional)
  • Discogs-Fallback — bei MusicBrainz-Misses
  • LLM-Reasoning — Ollama (lokal, kostenlos) → OpenRouter (DeepSeek V3) für unklare / widersprüchliche Metadaten
  • Cover-Art — lokal (JPG/PNG/WebP) → MusicBrainz Cover Art Archive → einbetten in MP3/FLAC/M4A; WebP wird automatisch zu JPEG konvertiert
  • Tag-Schreiben — title, artist, album, albumartist, tracknumber, discnumber, date, genre, label (mutagen, ID3v2.4)
  • Umbenennen — normiertes Dateinamen-Schema mit Unterstrichen (Pop und Klassik)
  • M3U-Playlist — wird nach dem Umbenennen automatisch neu generiert
  • Backup — Sicherungskopien vor jeder Änderung
  • CSV-Report — vollständiges Protokoll aller Änderungen
  • Interaktiver / Auto-Modus — mit Konfidenz-Schwellwert

Voraussetzungen

pip install mutagen musicbrainzngs pyacoustid discogs_client \
            Pillow requests tqdm beautifulsoup4
sudo apt install libchromaprint-tools   # fpcalc für AcoustID-Fingerprinting
sudo apt install yt-dlp                 # YouTube-Metadaten (optional)
Paket Zweck Optional?
mutagen Tags lesen/schreiben nein
musicbrainzngs MusicBrainz-API ja
pyacoustid AcoustID-Fingerprinting ja
discogs_client Discogs-API ja
Pillow Cover-Bildgröße prüfen, WebP→JPEG ja
requests Cover-Art-Download ja
tqdm Fortschrittsbalken ja
beautifulsoup4 HTML-Tracklisten parsen ja
fpcalc Audio-Fingerprinting-Binary ja
yt-dlp YouTube-Metadaten + Kapitel ja

Für LLM-Reasoning wird ein laufender Ollama-Server erwartet (Port 11434). Empfohlenes Modell: qwen3:8b (5 GB). OCR: qwen3-vl:latest.


Umgebungsvariablen (optional)

Variable Beschreibung Default
OLLAMA_HOST Ollama-Server-URL http://localhost:11434
OLLAMA_RESOLVE_MODEL LLM für Metadaten-Reasoning qwen3:8b
OLLAMA_OCR_MODEL Vision-Modelle für OCR (kommagetrennt) qwen3-vl:latest,minicpm-v:latest,deepseek-ocr:latest
OPENROUTER_API_KEY OpenRouter-Fallback (DeepSeek V3)
ACOUSTID_API_KEY AcoustID-Fingerprinting
DISCOGS_TOKEN Discogs-API

CLI

music_enricher.py [Optionen] PFAD [PFAD ...]
music_enricher.py --album PFAD [Optionen]
Option Beschreibung
--dry-run Vorschläge anzeigen, nichts schreiben
--auto Kein interaktiver Review-Schritt
--confidence FLOAT Min-Konfidenz für --auto (default: 0.85)
--rename Dateien nach Schema umbenennen
--embed-cover Cover-Art in Audiodatei einbetten
--backup PFAD Backup-Verzeichnis vor Änderungen
--report PFAD CSV-Report der Änderungen
--no-fingerprint AcoustID-Fingerprinting überspringen
--no-api Keine externen API-Calls (MusicBrainz, Discogs, OCR)
--no-cover Kein Cover-Art-Download
--album PFAD Einzelnes Album verarbeiten
--no-tqdm Fortschrittsanzeige deaktivieren

Verwendung

# Einzelnes Album — Dry-Run, nur lokale Analyse
python3 music_enricher.py --dry-run --no-api \
    --album ~/Musik/Abba_-_Greatest_Hits

# Mit MusicBrainz-Lookup und Ollama-LLM
python3 music_enricher.py --dry-run \
    --album ~/Musik/Bach_Organ_-_Peter_Hurford

# Vollständig: Tags + Cover einbetten + umbenennen, mit Backup und Report
python3 music_enricher.py --embed-cover --rename \
    --backup /tmp/musik_backup \
    --report report.csv \
    ~/Musik

# Auto-Modus: nur Vorschläge ≥ 90% Konfidenz anwenden
python3 music_enricher.py --auto --confidence 0.90 \
    --embed-cover --rename --backup /tmp/backup \
    ~/Musik

# Album mit YouTube-IDs in Dateinamen (yt-dlp holt Titel + Kapitel automatisch)
python3 music_enricher.py --auto --confidence 0.1 --rename --embed-cover \
    --album ~/Musik/Die_Bergvagabunden_Am_Lagefeuer.audio

Verarbeitungs-Pipeline

Album-Verzeichnis
      │
      ▼
1. AlbumScanner        — Dateitypen klassifizieren (Audio, Bild, Tracklist, Playlist)
      │
      ▼
2. HintExtractor       — lokal, keine API
   ├─ Verzeichnisname  → Artist, Album, Jahr
   ├─ Dateinamen       → Tracknummer, Artist, Titel (YouTube-ID wird entfernt)
   ├─ ID3/FLAC-Tags    → bestehende Werte
   ├─ Tracklist .txt/.htm → Tracklisten parsen (4-stufiges Matching)
   ├─ YouTube-IDs      → Titel, Uploader, Kapitel via yt-dlp
   └─ OCR Back-Cover   → Tracklist aus back.jpg via Ollama Vision
      │
      ▼
3. MetadataResolver
   ├─ AcoustID         → MusicBrainz via Fingerprint
   ├─ Textsuche        → MusicBrainz-API
   ├─ Discogs          → Fallback
   └─ LLM-Reasoning   → Ollama lokal → OpenRouter (DeepSeek V3)
      │
      ▼
4. CoverHandler        — lokal (JPG/PNG/WebP) → MusicBrainz → einbetten
      │
      ▼
5. ReviewStep          — interaktiv oder --auto mit Konfidenz-Schwellwert
      │
      ▼
6. Executor            — Backup → Tags → Cover → Umbenennen → M3U → Report

Konfidenz-Modell

Quelle Bonus
AcoustID-Match ≥ 90% +0.20
MusicBrainz via Fingerprint +0.25
MusicBrainz-Texttreffer (Score/100) +0.30 × Score
Discogs +0.15
LLM-Reasoning (Ollama/OpenRouter) +0.10
Lokale Hints (Ordner-/Dateiname, YouTube) +0.05

Dateinamen-Schema (mit --rename)

Pop/Default — Leerzeichen als Unterstriche, Teile durch _-_ getrennt:

01_-_ABBA_-_Dancing_Queen.mp3
2-07_-_Die_Bergvagabunden_-_Hohe_Tannen.flac

Klassik — Performer und Komponist explizit getrennt:

01_-_Peter_Hurford_-_Johann_Sebastian_Bach_-_Toccata_And_Fugue_In_D_Minor_BWV_565.mp3
01_-_Gardiner_-_Bach_-_Kantate_BWV_147_-_English_Baroque_Soloists.flac

Schema: [D-]TT_-_Performer_-_Komponist_-_Werk[_-_Orchester_Dirigent].ext

Bei Single-Disc entfällt die Disc-Nummer. Bei Multi-CD wird sie immer gesetzt (1-01, 2-01 etc.).


Projektstruktur

Music_Metadata_Enricher/
├── music_enricher.py       Haupt-CLI, Pipeline-Orchestrierung
├── models.py               Dataclasses: AlbumScan, AlbumHints, TrackProposal, …
├── scanner.py              Dateisystem-Scanner, Typ-Klassifikation
├── hint_extractor.py       Dateiname/Tag/Tracklist/OCR/YouTube-Auswertung
├── metadata_resolver.py    MusicBrainz + Discogs + Ollama/OpenRouter LLM
├── cover_handler.py        Cover-Art: Suche, WebP-Konvertierung, Download, Einbettung
├── executor.py             Backup, Tag-Schreiben, Umbenennen, M3U, CSV-Report
└── test_suite_enricher.py  17 Unit-/Integrationstests

Tests

python3 test_suite_enricher.py
# 📊 17/17 Tests erfolgreich

Unterstützte Formate

Format Tags Cover-Einbettung
MP3 ID3v2.4 (EasyID3) APIC-Frame
FLAC Vorbis-Comments METADATA_BLOCK_PICTURE
M4A/AAC MP4-Tags covr-Atom
Sonstige mutagen generic