import json import shutil from pathlib import Path import pytest from mutagen import File as MutagenFile from mutagen.id3 import COMM, WXXX from mopidy_webhooks import metadata DATA_DIR = Path(__file__).parent / "data" @pytest.fixture def tagged_episode(tmp_path): episode = tmp_path / "2026-09-22 Episode One.mp3" shutil.copy(DATA_DIR / "silence.mp3", episode) audio = MutagenFile(episode, easy=True) audio["title"] = ["Episode One"] audio["artist"] = ["Jane Host"] audio["album"] = ["The Test Podcast"] audio["tracknumber"] = ["42"] audio["date"] = ["2026-09-22"] audio["genre"] = ["Technology"] audio.save() raw = MutagenFile(episode) raw.tags.add( COMM(encoding=3, lang="eng", desc="", text="A tag description") ) raw.tags.add(WXXX(encoding=3, desc="", url="https://example.com/episode")) raw.save() return episode def test_resolve_media_path_for_file_uri(tagged_episode): path = metadata.resolve_media_path(tagged_episode.as_uri(), []) assert path == str(tagged_episode) def test_resolve_media_path_for_local_uri(tagged_episode, tmp_path): uri = f"local:podcast:{tagged_episode.name}" path = metadata.resolve_media_path(uri, [str(tmp_path)]) assert path == str(tagged_episode) def test_resolve_media_path_for_local_track_uri(tagged_episode, tmp_path): uri = f"local:track:{tagged_episode.name}" path = metadata.resolve_media_path(uri, [str(tmp_path)]) assert path == str(tagged_episode) def test_resolve_media_path_percent_decodes_uri(tagged_episode, tmp_path): uri = "local:podcast:2026-09-22%20Episode%20One.mp3" path = metadata.resolve_media_path(uri, [str(tmp_path)]) assert path == str(tagged_episode) def test_resolve_media_path_returns_none_for_remote_uri(): assert metadata.resolve_media_path("spotify:track:123", []) is None assert ( metadata.resolve_media_path("podcast+https://example.com/feed#1", []) is None ) assert metadata.resolve_media_path("", []) is None def test_read_tags_returns_normalized_metadata(tagged_episode): tags = metadata.read_tags(str(tagged_episode)) assert tags["title"] == "Episode One" assert tags["artist"] == "Jane Host" assert tags["album"] == "The Test Podcast" assert tags["track_number"] == "42" assert tags["date"] == "2026-09-22" assert tags["genre"] == "Technology" assert tags["comment"] == "A tag description" assert tags["website"] == "https://example.com/episode" def test_read_tags_handles_missing_file(): assert metadata.read_tags("/does/not/exist.mp3") == {} assert metadata.read_tags(None) == {} def test_read_sidecar_json_prefers_same_directory(tagged_episode): sidecar = tagged_episode.with_suffix(".json") sidecar.write_text(json.dumps({"feed_url": "https://example.com/feed"})) data = metadata.read_sidecar_json(str(tagged_episode)) assert data["feed_url"] == "https://example.com/feed" def test_read_metadata_merges_sidecar_and_tags(tagged_episode, tmp_path): metadata_dir = tmp_path / "metadata" metadata_dir.mkdir() sidecar = metadata_dir / f"{tagged_episode.stem}.json" sidecar.write_text( json.dumps( { "title": "Sidecar Title", "date": "2026-09-21T10:00:00", "feed_url": "https://example.com/feed", "guid": "episode-guid", "episode_url": "https://example.com/episode.mp3", "description": "A sidecar description", "duration": 1234, } ) ) data = metadata.read_metadata(str(tagged_episode)) assert data["title"] == "Episode One" assert data["artist"] == "Jane Host" assert data["album"] == "The Test Podcast" assert data["feed_url"] == "https://example.com/feed" assert data["guid"] == "episode-guid" assert data["episode_url"] == "https://example.com/episode.mp3" assert data["description"] == "A sidecar description" assert data["duration"] == 1234 assert data["date"] == "2026-09-21T10:00:00" @pytest.mark.parametrize( "value,expected", [ ("2026-09-22", "2026-09-22"), ("2026-09-22T10:00:00", "2026-09-22"), ("2026-09", "2026-09-01"), ("2026", ""), ("", ""), ("not a date", ""), ], ) def test_normalize_date(value, expected): assert metadata.normalize_date(value) == expected def test_normalize_date_handles_date_objects(): import datetime assert metadata.normalize_date(datetime.date(2026, 9, 22)) == "2026-09-22" def test_split_podcast_uri(): feed, guid = metadata.split_podcast_uri( "podcast+https://feed.example/rss#abc" ) assert feed == "https://feed.example/rss" assert guid == "abc" assert metadata.split_podcast_uri("local:track:foo.mp3") == (None, None) def test_parse_dirs(): assert metadata.parse_dirs("/a, /b ,, ") == ["/a", "/b"] assert metadata.parse_dirs("") == [] assert metadata.parse_dirs(None) == [] assert metadata.parse_dirs(["/a", "/b"]) == ["/a", "/b"] def test_is_podcast_detection(tagged_episode, tmp_path): assert metadata.is_podcast("local:podcast:foo.mp3") assert metadata.is_podcast("podcast+https://feed.example/rss#abc") assert metadata.is_podcast("file:///media/podcasts/foo.mp3") assert metadata.is_podcast( "file:///tmp/foo.mp3", path=str(tagged_episode), podcast_dirs=[str(tmp_path)], ) assert metadata.is_podcast( "file:///tmp/foo.mp3", path="/tmp/foo.mp3", metadata={"feed_url": "x"} ) assert not metadata.is_podcast( "local:track:Artist/Album/01%20Song.mp3", path="/tmp/song.mp3", )