Musiksammlung/src/musiksammlung/playlist.py
dschlueter 255496bd1b Add per-track artist to filename: 09_Titel_-_Kuenstler.flac
- Track model: add optional artist field (None = fall back to album artist)
- organizer: append _-_<artist> to each filename
- tagger: use track.artist over album.artist for the 'artist' tag
- playlist: widen glob pattern to match new _-_<artist> suffix
- tests: update assertions + add test for track-artist override

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 00:11:07 +01:00

57 lines
1.8 KiB
Python

"""M3U-Playlist-Generierung für Jellyfin."""
from __future__ import annotations
import logging
from pathlib import Path
from musiksammlung.models import Album
from musiksammlung.organizer import _sanitize_filename
logger = logging.getLogger(__name__)
def generate_playlist(album: Album, album_dir: Path) -> Path:
"""Erzeugt eine M3U-Playlist für das gesamte Album.
Die Playlist liegt im Album-Root und referenziert alle Tracks
über relative Pfade (CD1/01 Titel.flac, CD2/01 Titel.flac, ...).
Returns:
Pfad zur erzeugten Playlist-Datei.
"""
playlist_name = _sanitize_filename(album.album) + ".m3u"
playlist_path = album_dir / playlist_name
multi_disc = len(album.discs) > 1
lines = ["#EXTM3U"]
for disc in album.discs:
if multi_disc:
disc_prefix = f"CD{disc.disc_number}/"
else:
disc_prefix = ""
for track in disc.tracks:
safe_title = _sanitize_filename(track.title)
# Audiodatei im Zielverzeichnis finden
pattern = f"{track.track_number:02d}_{safe_title}*"
if multi_disc:
search_dir = album_dir / f"CD{disc.disc_number}"
else:
search_dir = album_dir
matches = list(search_dir.glob(pattern))
if matches:
filename = matches[0].name
else:
# Fallback: generischer Name mit .flac
filename = f"{track.track_number:02d}_{safe_title}.flac"
logger.warning("Datei nicht gefunden, Fallback: %s", filename)
lines.append(f"#EXTINF:0,{track.title}")
lines.append(f"{disc_prefix}{filename}")
playlist_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
logger.info("Playlist erstellt: %s", playlist_path)
return playlist_path