This commit is contained in:
2026-08-27 23:46:19 +02:00
parent a8ed350aec
commit 8aed3b022b
16 changed files with 602 additions and 186 deletions

View File

@@ -42,7 +42,8 @@ async def test_every_section_is_scanned(config_dir: Path) -> None:
"Eule",
"Kinderparty Lieder",
"Conni in den Bergen",
"Wissen macht Ah",
"Alt",
"Neu",
}
@@ -80,17 +81,27 @@ async def test_audiobooks_group_by_the_name_before_the_comma(config_dir: Path) -
assert album.category == "Conni"
async def test_podcasts_are_named_after_the_folder_not_the_tags(config_dir: Path) -> None:
album = album_named(await build(config_dir), "Wissen macht Ah")
# The tags say album="Wissen macht Ah! - Podcast" and artist=<six presenters>.
assert album.kind == "book"
assert album.artist == "Wissen macht Ah"
assert album.series == "Wissen macht Ah"
async def test_podcast_episodes_are_their_own_albums_grouped_by_the_show(
config_dir: Path,
) -> None:
"""One podcast mp3 behaves like a whole audiobook: its own album, its own cover,
rather than a chapter buried in one album named after the entire feed."""
library = await build(config_dir)
episodes = [a for a in library.albums if a.category == "Wissen macht Ah"]
assert {a.title for a in episodes} == {"Alt", "Neu"}
# The tags say album="Wissen macht Ah! - Podcast" and artist=<six presenters> -
# neither groups usefully, so the folder (the show) wins for artist/series, same
# as it always did; only the *title* now comes from the episode's own tags.
assert all(a.kind == "book" for a in episodes)
assert all(a.artist == "Wissen macht Ah" for a in episodes)
assert all(a.series == "Wissen macht Ah" for a in episodes)
assert all(len(a.tracks) == 1 for a in episodes)
async def test_podcast_episodes_are_newest_first(config_dir: Path) -> None:
album = album_named(await build(config_dir), "Wissen macht Ah")
assert [track.title for track in album.tracks] == ["Neu", "Alt"]
library = await build(config_dir)
episodes = [a for a in library.albums if a.category == "Wissen macht Ah"]
assert [a.title for a in episodes] == ["Neu", "Alt"]
async def test_other_sections_keep_filename_order(config_dir: Path) -> None:
@@ -117,11 +128,12 @@ async def test_durations_come_from_the_files(config_dir: Path) -> None:
async def test_scratch_files_never_reach_a_playlist(config_dir: Path) -> None:
album = album_named(await build(config_dir), "Wissen macht Ah")
names = {track.path.name for track in album.tracks}
library = await build(config_dir)
episodes = [a for a in library.albums if a.category == "Wissen macht Ah"]
names = {episode.tracks[0].path.name for episode in episodes}
assert "archive.json" not in names
assert not any(name.startswith(".") for name in names)
assert len(album.tracks) == 2
assert len(episodes) == 2
async def test_unknown_top_level_folders_are_ignored(config_dir: Path) -> None:
@@ -141,7 +153,7 @@ async def test_a_missing_section_warns_rather_than_failing(
library = await build(config_dir)
assert "Musik" in caplog.text
assert len(library.albums) == 4
assert len(library.albums) == 5
async def test_an_empty_album_folder_is_not_an_album(config_dir: Path) -> None:
@@ -199,6 +211,27 @@ async def test_a_changed_folder_is_rescanned(config_dir: Path) -> None:
assert len(album_named(library, "Eule").tracks) == 3
async def test_a_new_episode_only_costs_scanning_that_one_file(config_dir: Path) -> None:
"""The point of fingerprinting a podcast per episode rather than per folder: a
show's other, untouched episodes come straight out of the cache, ids and all."""
first = await build(config_dir)
before = {a.id: a for a in first.albums if a.category == "Wissen macht Ah"}
assert len(before) == 2
write_track(
config_dir / "music" / "Kinderpodcasts" / "Wissen macht Ah" / "20260201 - Neuer.mp3",
title="Neuer",
album="Wissen macht Ah! - Podcast",
albumartist="Ein Name, Noch Einer, Und Einer",
)
second = await build(config_dir)
after = {a.id: a for a in second.albums if a.category == "Wissen macht Ah"}
assert len(after) == 3
# The two pre-existing episodes kept their identity across the rescan.
assert set(before) <= set(after)
async def test_a_rescan_leaves_analysis_alone(config_dir: Path) -> None:
"""The whole reason the cache is a directory rather than one file."""
library = await build(config_dir)
@@ -238,7 +271,7 @@ async def test_a_corrupt_index_is_rebuilt_rather_than_fatal(config_dir: Path) ->
(config_dir / ".cache" / "index.json").write_text("{ not json")
library = await build(config_dir)
assert len(library.albums) == 5
assert len(library.albums) == 6
def test_album_ids_are_stable_and_path_derived(tmp_path: Path) -> None: