#EXTINF:0,Title was missing the artist, causing VLC and other players to show only the track title without the performer. Standard M3U extended format is '#EXTINF:<duration>,<Artist> - <Title>'. Falls back to album.artist when no per-track artist is set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
"""Tests für die Playlist-Generierung."""
|
|
|
|
from pathlib import Path
|
|
|
|
from musiksammlung.models import Album, Disc, Track
|
|
from musiksammlung.playlist import generate_playlist
|
|
|
|
|
|
def _make_album(n_tracks: int = 2, n_discs: int = 1) -> Album:
|
|
discs = [
|
|
Disc(
|
|
disc_number=d,
|
|
tracks=[
|
|
Track(track_number=i, title=f"Disc{d} Song {i}") for i in range(1, n_tracks + 1)
|
|
],
|
|
)
|
|
for d in range(1, n_discs + 1)
|
|
]
|
|
return Album(artist="Artist", album="TestAlbum", year=2000, discs=discs)
|
|
|
|
|
|
def test_generate_playlist_single_disc(tmp_path: Path):
|
|
"""M3U für Single-CD: keine CD-Prefix, korrekte Dateinamen."""
|
|
album = _make_album(n_tracks=2, n_discs=1)
|
|
|
|
# Dummy-Audiodateien im Schema 01_-_Titel_-_Künstler
|
|
(tmp_path / "01_-_Disc1_Song_1_-_Artist.flac").touch()
|
|
(tmp_path / "02_-_Disc1_Song_2_-_Artist.flac").touch()
|
|
|
|
playlist_path = generate_playlist(album, tmp_path)
|
|
assert playlist_path.exists()
|
|
|
|
content = playlist_path.read_text()
|
|
assert "#EXTM3U" in content
|
|
assert "01_-_Disc1_Song_1_-_Artist.flac" in content
|
|
assert "02_-_Disc1_Song_2_-_Artist.flac" in content
|
|
# Kein CD-Prefix bei Single-Disc
|
|
assert "CD1/" not in content
|
|
|
|
|
|
def test_generate_playlist_multi_disc(tmp_path: Path):
|
|
"""M3U für Multi-CD: CD-Prefix in Pfaden."""
|
|
album = _make_album(n_tracks=2, n_discs=2)
|
|
|
|
cd1 = tmp_path / "CD1"
|
|
cd2 = tmp_path / "CD2"
|
|
cd1.mkdir()
|
|
cd2.mkdir()
|
|
(cd1 / "01_-_Disc1_Song_1_-_Artist.flac").touch()
|
|
(cd1 / "02_-_Disc1_Song_2_-_Artist.flac").touch()
|
|
(cd2 / "01_-_Disc2_Song_1_-_Artist.flac").touch()
|
|
(cd2 / "02_-_Disc2_Song_2_-_Artist.flac").touch()
|
|
|
|
playlist_path = generate_playlist(album, tmp_path)
|
|
content = playlist_path.read_text()
|
|
|
|
assert "CD1/01_-_Disc1_Song_1_-_Artist.flac" in content
|
|
assert "CD2/01_-_Disc2_Song_1_-_Artist.flac" in content
|
|
|
|
|
|
def test_generate_playlist_filename(tmp_path: Path):
|
|
"""Playlist-Dateiname ist sanitierter Albumname."""
|
|
album = Album(
|
|
artist="A",
|
|
album="Mein Album",
|
|
discs=[Disc(disc_number=1, tracks=[Track(track_number=1, title="Song")])],
|
|
)
|
|
playlist_path = generate_playlist(album, tmp_path)
|
|
assert playlist_path.name == "Mein_Album.m3u"
|
|
|
|
|
|
def test_generate_playlist_extinf_contains_title(tmp_path: Path):
|
|
"""#EXTINF-Einträge enthalten den originalen Titel."""
|
|
album = Album(
|
|
artist="A",
|
|
album="X",
|
|
discs=[
|
|
Disc(
|
|
disc_number=1,
|
|
tracks=[Track(track_number=1, title="Für Elise")],
|
|
)
|
|
],
|
|
)
|
|
(tmp_path / "01_-_Für_Elise_-_A.flac").touch()
|
|
playlist_path = generate_playlist(album, tmp_path)
|
|
content = playlist_path.read_text()
|
|
assert "#EXTINF:0,A - Für Elise" in content
|
|
|
|
|
|
def test_generate_playlist_fallback_if_file_missing(tmp_path: Path):
|
|
"""Wenn Audiodatei fehlt, wird Fallback-Name erzeugt (kein Absturz)."""
|
|
album = Album(
|
|
artist="A",
|
|
album="X",
|
|
discs=[Disc(disc_number=1, tracks=[Track(track_number=1, title="Ghost")])],
|
|
)
|
|
# Keine Audiodatei anlegen
|
|
playlist_path = generate_playlist(album, tmp_path)
|
|
content = playlist_path.read_text()
|
|
assert "01_-_Ghost.flac" in content
|