Fix CDDB parser for compilations and add grab-progress fallback

- _parse_cddb_lines now handles both 'Artist - Title' and 'Artist / Title'
  (slash separator used by abcde for compilation albums like Various Artists)
- _stream_abcde collects grab-progress lines (track N: Artist / Title)
  as a fallback TrackInfo source when no CDDB lines are found
- New _parse_grab_tracks() splits grab titles on ' / ' into artist+title
- 5 new tests (TestParseCddbLines.test_compilation_slash_separator,
  TestParseGrabTracks.*)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-18 09:42:03 +01:00
commit 09c01c9370
2 changed files with 96 additions and 7 deletions

View file

@ -10,6 +10,7 @@ from musiksammlung.ripper import (
_clean_input,
_extract_tracks,
_parse_cddb_lines,
_parse_grab_tracks,
_rename_files,
_sanitize_name,
interactive_rip,
@ -97,10 +98,55 @@ class TestParseCddbLines:
tracks = _parse_cddb_lines(lines)
assert len(tracks) == 1
def test_compilation_slash_separator(self) -> None:
"""Kompilations-Format: 'N: Artist / Title' wird korrekt geparst."""
lines = [
"1: Trini Lopez / This Land Is Your Land (live)",
"2: The Foundations / In the Bad Bad Old Days",
]
tracks = _parse_cddb_lines(lines)
assert len(tracks) == 2
assert tracks[0].artist == "Trini Lopez"
assert tracks[0].title == "This Land Is Your Land (live)"
assert tracks[1].artist == "The Foundations"
assert tracks[1].title == "In the Bad Bad Old Days"
def test_empty_input(self) -> None:
assert _parse_cddb_lines([]) == []
class TestParseGrabTracks:
"""Tests für _parse_grab_tracks."""
def test_artist_slash_title(self) -> None:
data = [(1, "Trini Lopez / This Land Is Your Land (live)")]
tracks = _parse_grab_tracks(data)
assert len(tracks) == 1
assert tracks[0].track_number == 1
assert tracks[0].artist == "Trini Lopez"
assert tracks[0].title == "This Land Is Your Land (live)"
def test_title_only_no_slash(self) -> None:
"""Ohne Slash → leerer Künstler, Titel = gesamter String."""
data = [(3, "Beethoven 5. Sinfonie")]
tracks = _parse_grab_tracks(data)
assert tracks[0].artist == ""
assert tracks[0].title == "Beethoven 5. Sinfonie"
def test_multiple_tracks(self) -> None:
data = [
(1, "KC and the Sunshine Band / Give It Up"),
(2, "Sam & Dave / Can't You Find Another Way"),
]
tracks = _parse_grab_tracks(data)
assert len(tracks) == 2
assert tracks[1].artist == "Sam & Dave"
assert tracks[1].title == "Can't You Find Another Way"
def test_empty_input(self) -> None:
assert _parse_grab_tracks([]) == []
class TestRipperConfig:
"""Tests für RipperConfig."""