feat: --retag-from-path to recover tags damaged by a prior --repair
Offline re-tag of artist/title from the artist folder + filename: strips (Official Video)/(Lyrics)-style decorations and trailing [id], and treats an 'Artist - Title' filename as authoritative (recovering the real artist for music videos filed under a channel name). Overwrites artist/title only; leaves album/year. Honors --dry-run. Refactors the source-folder walk into _iter_source_files, shared by --repair and --retag-from-path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -157,3 +157,67 @@ def test_repair_library_scans_only_source_dirs(tmp_path, monkeypatch):
|
||||
|
||||
def test_repair_library_missing_root():
|
||||
assert mf.repair_library("/no/such/dir", dry_run=False) == (0, 0)
|
||||
|
||||
|
||||
# ---- offline retag-from-path ----
|
||||
def test_title_from_filename():
|
||||
assert mf._title_from_filename(f"Song [{YT_ID}].opus") == "Song"
|
||||
assert mf._title_from_filename("STARDUST (Official Music Video) [3nsYNXtALhA].opus") \
|
||||
== "STARDUST (Official Music Video)"
|
||||
assert mf._title_from_filename("no brackets.mp3") == "no brackets"
|
||||
|
||||
|
||||
def test_strip_decorations():
|
||||
assert mf._strip_decorations("STARDUST (Official Music Video)") == "STARDUST"
|
||||
assert mf._strip_decorations("Away From You (Lyrics)") == "Away From You"
|
||||
assert mf._strip_decorations("More Than a Feeling (Official HD Video)") == "More Than a Feeling"
|
||||
# real info like a feature credit is kept
|
||||
assert mf._strip_decorations("WHO GON' SLIDE (Feat. Shakewell) [Official Music Video]") \
|
||||
== "WHO GON' SLIDE (Feat. Shakewell)"
|
||||
|
||||
|
||||
def test_derive_from_filename():
|
||||
# plain title -> folder is the artist
|
||||
assert mf._derive_from_filename(f"Aerodynamic [{YT_ID}].opus", "Daft Punk") == ("Daft Punk", "Aerodynamic")
|
||||
# decorated music video filed under the artist
|
||||
assert mf._derive_from_filename("STARDUST (Official Music Video) [3nsYNXtALhA].opus", "1nonly") \
|
||||
== ("1nonly", "STARDUST")
|
||||
# 'Artist - Title' name wins over a channel folder
|
||||
assert mf._derive_from_filename("BLCKLGHT - Away From You (Lyrics) [QapF4b1jYw8].opus", "7clouds Techno") \
|
||||
== ("BLCKLGHT", "Away From You")
|
||||
|
||||
|
||||
def test_retag_file_from_path_fixes_clobbered_tags(monkeypatch):
|
||||
audio = _FakeAudio({"artist": ["7clouds Techno"], "title": ["BLCKLGHT - Away From You (Lyrics)"]})
|
||||
monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None))
|
||||
changed = mf.retag_file_from_path(
|
||||
"X/7clouds Techno/youtube/BLCKLGHT - Away From You (Lyrics) [QapF4b1jYw8].opus",
|
||||
"7clouds Techno", dry_run=False)
|
||||
assert set(changed) == {"artist=BLCKLGHT", "title=Away From You"}
|
||||
assert audio["artist"] == ["BLCKLGHT"]
|
||||
assert audio["title"] == ["Away From You"]
|
||||
assert audio.saved is True
|
||||
|
||||
|
||||
def test_retag_file_from_path_dry_run(monkeypatch):
|
||||
audio = _FakeAudio({"artist": ["wrong"], "title": ["wrong"]})
|
||||
monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None))
|
||||
changed = mf.retag_file_from_path(f"X/Daft Punk/youtube/Aerodynamic [{YT_ID}].opus",
|
||||
"Daft Punk", dry_run=True)
|
||||
assert changed
|
||||
assert audio == {"artist": ["wrong"], "title": ["wrong"]}
|
||||
assert audio.saved is False
|
||||
|
||||
|
||||
def test_retag_library_walks_source_files(tmp_path, monkeypatch):
|
||||
root = tmp_path
|
||||
(root / "Daft Punk" / "youtube").mkdir(parents=True)
|
||||
(root / "Daft Punk" / "youtube" / f"Aerodynamic [{YT_ID}].opus").write_text("x")
|
||||
(root / "Daft Punk" / "Discovery").mkdir(parents=True) # album folder -> skip
|
||||
(root / "Daft Punk" / "Discovery" / "x.flac").write_text("x")
|
||||
visited = []
|
||||
monkeypatch.setattr(mf, "retag_file_from_path",
|
||||
lambda path, artist, dry_run: visited.append(artist) or ["artist=x"])
|
||||
scanned, changed = mf.retag_library_from_path(str(root), dry_run=False)
|
||||
assert (scanned, changed) == (1, 1)
|
||||
assert visited == ["Daft Punk"]
|
||||
|
||||
Reference in New Issue
Block a user