import server.mf # noqa: F401 — loads musicfetch, registers musicfetch_core import musicfetch_core as mf YT_ID = "dQw4w9WgXcQ" # valid 11-char YouTube id # ---- _is_source_dir ---- def test_is_source_dir(): assert mf._is_source_dir("youtube") is True assert mf._is_source_dir("soundcloud") is True assert mf._is_source_dir("downloads") is True assert mf._is_source_dir("Discovery") is False # Lidarr album folder assert mf._is_source_dir("Random Access Memories") is False assert mf._is_source_dir("") is False # ---- _parse_track_file ---- def test_parse_track_file(): assert mf._parse_track_file("Under My Skin [nGSNF2l44Zc].opus") == ("Under My Skin", "nGSNF2l44Zc") assert mf._parse_track_file("Ignomon [2202690443].m4a") == ("Ignomon", "2202690443") # greedy title: real id is the LAST bracket assert mf._parse_track_file("WHO GON' SLIDE [Official Music Video] [AxjP9s6J3uY].opus") \ == ("WHO GON' SLIDE [Official Music Video]", "AxjP9s6J3uY") assert mf._parse_track_file("no-id-here.opus") is None assert mf._parse_track_file("cover.jpg") is None # ---- _repair_id_ok ---- def test_repair_id_ok(): assert mf._repair_id_ok("youtube", YT_ID) is True assert mf._repair_id_ok("youtube", "Official Video") is False # space, wrong length assert mf._repair_id_ok("youtube", "Cover") is False assert mf._repair_id_ok("soundcloud", "2202690443") is True assert mf._repair_id_ok("soundcloud", "abc") is False assert mf._repair_id_ok("bandcamp", "x") is False # ---- _valid_year ---- def test_valid_year(): assert mf._valid_year({"release_year": 2001}) == "2001" assert mf._valid_year({"release_date": "1976-09-10"}) == "1976" assert mf._valid_year({"upload_date": "20110101"}) == "" # upload date ignored assert mf._valid_year({"release_year": 6577}) == "" # out of range assert mf._valid_year({}) == "" # ---- _repair_probe_url ---- def test_repair_probe_url(): assert mf._repair_probe_url("youtube", YT_ID) == f"https://music.youtube.com/watch?v={YT_ID}" assert mf._repair_probe_url("soundcloud", "123") == "https://api.soundcloud.com/tracks/123" assert mf._repair_probe_url("bandcamp", "x") is None # ---- repair_file (fake audio + mocked metadata) ---- class _FakeAudio(dict): def __init__(self, initial): super().__init__(initial) self.saved = False def save(self): self.saved = True def test_repair_file_fixes_album_and_year(monkeypatch): monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: {"artist": "Daft Punk", "title": "Aerodynamic", "album": "Discovery", "release_year": 2001}) audio = _FakeAudio({"artist": ["Daft Punk"], "title": ["Aerodynamic"]}) # album/date missing monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None)) changed = mf.repair_file(f"X/youtube/Aerodynamic [{YT_ID}].opus", "youtube", dry_run=False) assert set(changed) == {"album=Discovery", "date=2001"} assert audio["album"] == ["Discovery"] assert audio["date"] == ["2001"] assert audio.saved is True def test_repair_file_dry_run_writes_nothing(monkeypatch): monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: {"artist": "A", "title": "T", "album": "Alb", "release_year": 2020}) audio = _FakeAudio({}) monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None)) changed = mf.repair_file(f"X/youtube/T [{YT_ID}].opus", "youtube", dry_run=True) assert changed assert audio == {} assert audio.saved is False def test_repair_file_skips_music_video(monkeypatch): # No album AND no valid release year -> treat as a video, leave tags alone. monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: {"title": "Artist - Song (Official Music Video)", "uploader": "SomeVEVO", "upload_date": "20110101"}) audio = _FakeAudio({"artist": ["Real Artist"], "title": ["Song"]}) monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None)) changed = mf.repair_file(f"X/youtube/Song [{YT_ID}].opus", "youtube", dry_run=False) assert changed == [] assert audio == {"artist": ["Real Artist"], "title": ["Song"]} # untouched def test_repair_file_fills_missing_but_never_clobbers(monkeypatch): # Source artist is a channel name; existing artist must be kept. monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: {"artist": "SomeChannelVEVO", "title": "Channel Decorated Title", "album": "Real Album", "release_year": 2019}) audio = _FakeAudio({"artist": ["Correct Artist"], "title": ["Clean Title"]}) monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None)) changed = mf.repair_file(f"X/youtube/x [{YT_ID}].opus", "youtube", dry_run=False) assert set(changed) == {"album=Real Album", "date=2019"} assert audio["artist"] == ["Correct Artist"] # NOT overwritten with channel assert audio["title"] == ["Clean Title"] # NOT overwritten with decorated title def test_repair_file_fills_missing_artist_when_absent(monkeypatch): monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: {"artist": "Real Artist", "title": "T", "album": "Alb", "release_year": 2020}) audio = _FakeAudio({}) # nothing present -> fill artist + title too monkeypatch.setattr(mf, "_open_audio", lambda path: (audio, None)) changed = mf.repair_file(f"X/youtube/T [{YT_ID}].opus", "youtube", dry_run=False) assert set(changed) == {"album=Alb", "date=2020", "artist=Real Artist", "title=T"} def test_repair_file_skips_bad_id(monkeypatch): called = {"meta": False} monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: called.update(meta=True) or {}) # last bracket is a descriptor, not a real id assert mf.repair_file("X/youtube/Song [Official Video].opus", "youtube", dry_run=False) == [] assert called["meta"] is False # never hit the network def test_repair_file_skips_unparseable(monkeypatch): called = {"meta": False} monkeypatch.setattr(mf, "run_yt_dlp_get_metadata", lambda url: called.update(meta=True) or {}) assert mf.repair_file("X/youtube/no-id.opus", "youtube", dry_run=False) == [] assert called["meta"] is False # ---- repair_library (real temp tree, repair_file mocked) ---- def test_repair_library_scans_only_source_dirs(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) # Lidarr album -> skip (root / "Daft Punk" / "Discovery" / "Aerodynamic.flac").write_text("x") (root / "Ephixa" / "soundcloud").mkdir(parents=True) (root / "Ephixa" / "soundcloud" / "Ignomon [123].m4a").write_text("x") visited = [] monkeypatch.setattr(mf, "repair_file", lambda path, source, dry_run: visited.append((source, path)) or ["album=X"]) scanned, changed = mf.repair_library(str(root), dry_run=False) assert scanned == 2 and changed == 2 assert sorted(s for s, _ in visited) == ["soundcloud", "youtube"] # album folder skipped def test_repair_library_missing_root(): assert mf.repair_library("/no/such/dir", dry_run=False) == (0, 0)