Add project skeleton: CLI pipeline for CD digitization

Modular Python package with Typer CLI (scan/apply/process commands),
Pydantic data models, OCR via Tesseract, LLM-based tracklist parsing,
mutagen audio tagging, M3U playlist generation, and cover processing.
Includes 8 passing tests and ruff lint config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-15 00:47:54 +01:00
commit 3e073250ca
17 changed files with 1027 additions and 0 deletions

78
tests/test_organizer.py Normal file
View file

@ -0,0 +1,78 @@
"""Tests für den Organizer."""
from pathlib import Path
from musiksammlung.models import Album, Disc, Track
from musiksammlung.organizer import build_mapping, discover_audio_files
def test_discover_audio_files(tmp_path: Path):
"""Findet und sortiert Audiodateien korrekt."""
(tmp_path / "Track_03.flac").touch()
(tmp_path / "Track_01.flac").touch()
(tmp_path / "Track_02.flac").touch()
(tmp_path / "cover.jpg").touch() # soll ignoriert werden
files = discover_audio_files(tmp_path)
assert len(files) == 3
assert files[0].name == "Track_01.flac"
assert files[2].name == "Track_03.flac"
def test_build_mapping_single_disc(tmp_path: Path):
"""Mapping für ein Single-CD-Album."""
(tmp_path / "Track_01.flac").touch()
(tmp_path / "Track_02.flac").touch()
album = Album(
artist="TestArtist",
album="TestAlbum",
year=2000,
discs=[
Disc(
disc_number=1,
tracks=[
Track(track_number=1, title="Erster Song"),
Track(track_number=2, title="Zweiter Song"),
],
)
],
)
output = tmp_path / "output"
mapping = build_mapping(album, tmp_path, output)
assert len(mapping) == 2
targets = list(mapping.values())
assert targets[0].name == "01 Erster Song.flac"
assert targets[1].name == "02 Zweiter Song.flac"
# Single-Disc: kein CD1-Unterordner
assert "CD1" not in str(targets[0])
def test_build_mapping_multi_disc(tmp_path: Path):
"""Mapping für ein Multi-CD-Album."""
cd1 = tmp_path / "CD1"
cd2 = tmp_path / "CD2"
cd1.mkdir()
cd2.mkdir()
(cd1 / "Track_01.flac").touch()
(cd2 / "Track_01.flac").touch()
album = Album(
artist="Artist",
album="Box Set",
year=1999,
discs=[
Disc(disc_number=1, tracks=[Track(track_number=1, title="Song A")]),
Disc(disc_number=2, tracks=[Track(track_number=1, title="Song B")]),
],
)
output = tmp_path / "output"
mapping = build_mapping(album, tmp_path, output)
assert len(mapping) == 2
targets = list(mapping.values())
assert "CD1" in str(targets[0])
assert "CD2" in str(targets[1])