fix(import): treat cd1/cd2 folders as one audiobook

This commit is contained in:
Steppenstreuner
2026-08-28 19:21:24 +02:00
parent 24e3e9658e
commit b8f042025d
5 changed files with 153 additions and 18 deletions
+49 -2
View File
@@ -1,4 +1,5 @@
import importlib
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
@@ -214,8 +215,9 @@ def test_scan_rel_dir_and_request_update(client):
for i in range(2):
(nested / f"t{i}.mp3").write_bytes(b"")
item = client.get("/api/import/scan").json()["items"][0]
assert item["name"] == "CD1"
assert item["rel_dir"] == "Sammlung/Sonderfolgen"
# a disc subfolder is folded into its parent, which carries the title
assert item["name"] == "Sonderfolgen"
assert item["rel_dir"] == "Sammlung"
req = client.post("/api/requests", json={"library_id": lib_id, "title": "X"}).json()
updated = client.put(f"/api/requests/{req['id']}", json={
@@ -264,3 +266,48 @@ def test_retag_after_update(client):
assert str(tags["TXXX:SERIES"]) == "Rowling Kinderbücher"
assert str(tags["TXXX:SERIES-PART"]) == "2"
assert str(tags["TCOM"]) == "Ben Becker"
def test_multi_disc_folder_is_one_audiobook(client):
root = client.tmp_path / "library" / "dr3i"
lib_id = client.post("/api/libraries", json={
"name": "DR3i", "media_type": "audiobook", "root_path": str(root),
"folder_template": "{Title}", "file_template": "{Title}",
}).json()["id"]
req_id = client.post("/api/requests", json={
"library_id": lib_id, "title": "Die dr3i - Böses Erwachen",
}).json()["id"]
folder = client.downloads / "DiE DR3i - Boeses Erwachen"
for disc, tracks in (("CD 1", 3), ("CD2", 2)):
d = folder / disc
d.mkdir(parents=True)
for i in range(1, tracks + 1):
(d / f"Track {i}.mp3").write_bytes(b"")
items = client.get("/api/import/scan").json()["items"]
assert len(items) == 1
item = items[0]
assert item["name"] == "DiE DR3i - Boeses Erwachen"
assert item["is_dir"] and len(item["files"]) == 5
# discs in order, tracks naturally sorted within a disc
assert [Path(f).parent.name for f in item["files"]] == ["CD 1"] * 3 + ["CD2"] * 2
assert item["suggested_request_id"] == req_id
res = client.post("/api/import", json={"items": [{
"path": item["path"], "is_dir": True, "files": item["files"],
"request_id": req_id,
}]}).json()["results"][0]
assert res["ok"], res
dest = sorted(p.name for p in Path(res["dest"]).iterdir())
assert dest == [f"Die dr3i - Böses Erwachen - Part 0{i}.mp3" for i in range(1, 6)]
assert not folder.exists()
def test_natural_track_order_in_flat_folder(client):
folder = client.downloads / "Folge 12"
folder.mkdir(parents=True)
for i in (1, 2, 10):
(folder / f"track{i}.mp3").write_bytes(b"")
item = client.get("/api/import/scan").json()["items"][0]
assert [Path(f).name for f in item["files"]] == ["track1.mp3", "track2.mp3", "track10.mp3"]
+23
View File
@@ -47,3 +47,26 @@ def test_volume_number_must_match():
"media_type": "audiobook", "is_dir": False, "files": ["/d/f.mp3"]}]
out = best_matches(items, [r1, r123])
assert out[0]["suggested_request_id"] == 2
def test_leetspeak_title_matches():
assert normalize("DiE DR3i - Folge 03") == "die drei folge 03"
# digits at a word edge stay put, they carry the episode number
assert normalize("Folge03 v2 [m4b]") == "folge03"
r = req(1, "Die dr3i - Böses Erwachen", media_type="audiobook")
r.volume = 2
items = [{"path": "/d/x", "name": "Die drei 02 - Boeses Erwachen",
"media_type": "audiobook", "is_dir": True, "files": ["/d/x/a.mp3"]}]
out = best_matches(items, [r])
assert out[0]["suggested_request_id"] == 1
def test_digit_inside_word_is_not_a_volume():
from wordarr.importer.matcher import score
r = req(1, "Die dr3i - Folge 5", media_type="audiobook")
r.volume = 5
# "dr3i" must not contribute a "3" that satisfies the volume check
r3 = req(2, "Die dr3i - Folge 3", media_type="audiobook")
r3.volume = 3
assert score("DiE DR3i - 05 - Der Fall", r) > score("DiE DR3i - 05 - Der Fall", r3)