diff --git a/music_enricher.py b/music_enricher.py index e14645f..4b980b1 100755 --- a/music_enricher.py +++ b/music_enricher.py @@ -228,6 +228,71 @@ def process_album( return stats +def _print_status(args: argparse.Namespace) -> None: + """Scannt die Bibliothek und zeigt Alben mit fehlenden/schlechten Metadaten.""" + from mutagen import File as MutagenFile + + IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp"} + AUDIO_EXTS = {".mp3", ".flac", ".m4a", ".wav", ".ogg", ".opus"} + + album_dirs: List[Path] = [] + if args.album: + album_dirs.append(args.album.expanduser().resolve()) + for raw in args.paths: + root = Path(raw).expanduser().resolve() + if root.is_dir(): + album_dirs.extend(collect_album_dirs(root)) + + no_cover, bad_tags, ok = [], [], [] + + for album_dir in sorted(album_dirs): + has_cover = any( + f.suffix.lower() in IMAGE_EXTS + for f in album_dir.rglob("*") if f.is_file() + ) + audio_files = [ + f for f in sorted(album_dir.rglob("*")) + if f.is_file() and f.suffix.lower() in AUDIO_EXTS + ] + missing_tags = [] + for af in audio_files[:3]: # nur erste 3 prüfen (schnell) + try: + tags = MutagenFile(str(af), easy=True) + if tags is None: + missing_tags.append(af.name) + continue + title = (tags.get("title") or [""])[0].strip() + artist = (tags.get("artist") or [""])[0].strip() + if not title or title.lower() in ("unknown", "audiotrack", "") \ + or not artist or artist.lower() in ("unknown", ""): + missing_tags.append(af.name) + except Exception: + missing_tags.append(af.name) + + problems = [] + if not has_cover: + problems.append("kein Cover") + if missing_tags: + problems.append(f"schlechte Tags ({len(missing_tags)}/{min(3,len(audio_files))} geprüft)") + + if problems: + bad_tags.append((album_dir, problems)) + else: + ok.append(album_dir) + + print(f"\n{'=' * 60}") + print(f"📊 Bibliotheksstatus — {len(album_dirs)} Alben") + print(f"{'=' * 60}") + print(f" ✅ In Ordnung: {len(ok)}") + print(f" ⚠️ Mit Problemen: {len(bad_tags)}") + print() + for album_dir, problems in bad_tags: + print(f" 💿 {album_dir.name}") + for p in problems: + print(f" → {p}") + print("=" * 60) + + def main() -> None: parser = argparse.ArgumentParser( description="KI-gestützter Musik-Metadaten-Enricher für Jellyfin", @@ -262,9 +327,17 @@ def main() -> None: parser.add_argument("--playlist-generator", type=Path, dest="playlist_generator", help="Pfad zu jellyfin_playlist_generator.py\n" "(Standard: ../Jellyfin_Playlist_Generator/jellyfin_playlist_generator.py)") + parser.add_argument("--status", action="store_true", + help="Bibliotheksstatus anzeigen (fehlende Cover, schlechte Tags) — nichts schreiben") args = parser.parse_args() + if args.status: + if not args.paths and not args.album: + parser.error("--status benötigt mindestens einen Pfad.") + _print_status(args) + return + if not args.album and not args.paths: parser.error("Mindestens ein Pfad oder --album erforderlich.")