Extract podcast metadata from tags and sidecar files
This commit is contained in:
BIN
tests/data/silence.mp3
Normal file
BIN
tests/data/silence.mp3
Normal file
Binary file not shown.
@ -11,8 +11,9 @@ def test_get_default_config():
|
||||
|
||||
assert "[webhooks]" in config
|
||||
assert "enabled = true" in config
|
||||
assert "username =" in config
|
||||
assert "password =" in config
|
||||
assert "urls =" in config
|
||||
assert "media_dirs =" in config
|
||||
assert "podcast_dirs =" in config
|
||||
|
||||
|
||||
def test_get_config_schema():
|
||||
@ -20,8 +21,10 @@ def test_get_config_schema():
|
||||
|
||||
schema = ext.get_config_schema()
|
||||
|
||||
assert "username" in schema
|
||||
assert "password" in schema
|
||||
assert "urls" in schema
|
||||
assert "tokens" in schema
|
||||
assert "media_dirs" in schema
|
||||
assert "podcast_dirs" in schema
|
||||
|
||||
|
||||
def test_setup():
|
||||
|
||||
@ -4,6 +4,7 @@ import pytest
|
||||
|
||||
from mopidy import models
|
||||
from mopidy_webhooks import frontend as frontend_lib
|
||||
from mopidy_webhooks import metadata as metadata_lib
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -12,13 +13,41 @@ def frontend():
|
||||
"webhooks": {
|
||||
"urls": "http://127.0.0.1/receiver/,http://127.0.0.1/receiver/two/",
|
||||
"tokens": "secrettoken,anotherone",
|
||||
"media_dirs": "",
|
||||
"podcast_dirs": "",
|
||||
}
|
||||
}
|
||||
core = mock.sentinel.core
|
||||
return frontend_lib.WebhooksFrontend(config, core)
|
||||
|
||||
|
||||
def test_on_start_creates_lastfm_network(pylast_mock, frontend):
|
||||
@pytest.fixture
|
||||
def track():
|
||||
return models.Track(
|
||||
uri="local:track:Sublime%20-%20Sublime/Disc%201%20-%2004%20-%20Same%20in%20the%20End.mp3",
|
||||
name="Same in the End",
|
||||
artists=[models.Artist(name="Sublime", uri="local:artist:md5:abc")],
|
||||
album=models.Album(name="Sublime"),
|
||||
track_no=4,
|
||||
length=156604,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def podcast_track():
|
||||
return models.Track(
|
||||
uri="local:podcast:TED%20Talks/2026-09-22_Do%20the%20hard%20thing.mp3",
|
||||
name="2026-09-22_Do the hard thing",
|
||||
artists=[models.Artist(name="TED Talks Daily")],
|
||||
album=models.Album(name="TED Talks Daily"),
|
||||
length=1800000,
|
||||
)
|
||||
|
||||
|
||||
def test_on_start_parses_urls_tokens_and_dirs(frontend):
|
||||
frontend.config["webhooks"]["media_dirs"] = "/media,/srv/media"
|
||||
frontend.config["webhooks"]["podcast_dirs"] = "/media/podcasts"
|
||||
|
||||
frontend.on_start()
|
||||
|
||||
assert frontend.webhook_urls == [
|
||||
@ -26,144 +55,176 @@ def test_on_start_creates_lastfm_network(pylast_mock, frontend):
|
||||
"http://127.0.0.1/receiver/two/",
|
||||
]
|
||||
assert frontend.webhook_tokens == ["secrettoken", "anotherone"]
|
||||
assert frontend.media_dirs == ["/media", "/srv/media"]
|
||||
assert frontend.podcast_dirs == ["/media/podcasts"]
|
||||
|
||||
|
||||
def test_on_start_stops_actor_on_error(pylast_mock, frontend):
|
||||
pylast_mock.NetworkError = pylast.NetworkError
|
||||
pylast_mock.LastFMNetwork.side_effect = pylast.NetworkError(None, "foo")
|
||||
frontend.stop = mock.Mock()
|
||||
def test_build_post_data_for_track(frontend, track):
|
||||
post_data = frontend._build_post_data(track, time_position=0)
|
||||
|
||||
assert post_data["name"] == "Same in the End"
|
||||
assert post_data["artist"] == "Sublime"
|
||||
assert post_data["album"] == "Sublime"
|
||||
assert post_data["track_number"] == 4
|
||||
assert post_data["run_time"] == "156"
|
||||
assert post_data["media_type"] == "track"
|
||||
assert "podcast_name" not in post_data
|
||||
|
||||
|
||||
def test_build_post_data_for_podcast_prefers_tags(
|
||||
frontend, podcast_track, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
metadata_lib,
|
||||
"resolve_media_path",
|
||||
lambda uri, dirs: "/media/podcasts/ep.mp3",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
metadata_lib,
|
||||
"read_metadata",
|
||||
lambda path: {
|
||||
"title": "#564: EVE Online Departs for Python 3",
|
||||
"artist": "Michael Kennedy",
|
||||
"album": "Talk Python To Me",
|
||||
"track_number": "564",
|
||||
"date": "2026-09-11T15:46:15",
|
||||
"comment": "A great episode",
|
||||
"feed_url": "https://talkpython.fm/episodes/rss",
|
||||
"guid": "talk-python-564",
|
||||
"episode_url": "https://talkpython.fm/564",
|
||||
"duration": 4071,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
metadata_lib, "is_podcast", lambda uri, **kwargs: "podcast" in uri
|
||||
)
|
||||
|
||||
post_data = frontend._build_post_data(podcast_track, time_position=0)
|
||||
|
||||
assert post_data["media_type"] == "podcast"
|
||||
assert post_data["name"] == "#564: EVE Online Departs for Python 3"
|
||||
assert post_data["artist"] == "Michael Kennedy"
|
||||
assert post_data["album"] == "Talk Python To Me"
|
||||
assert post_data["track_number"] == "564"
|
||||
assert post_data["podcast_name"] == "Talk Python To Me"
|
||||
assert post_data["podcast_producer"] == "Michael Kennedy"
|
||||
assert post_data["podcast_feed_url"] == "https://talkpython.fm/episodes/rss"
|
||||
assert post_data["podcast_description"] == "A great episode"
|
||||
assert post_data["episode_num"] == 564
|
||||
assert post_data["pub_date"] == "2026-09-11"
|
||||
assert post_data["episode_url"] == "https://talkpython.fm/564"
|
||||
assert post_data["episode_guid"] == "talk-python-564"
|
||||
assert post_data["duration_seconds"] == 4071
|
||||
|
||||
|
||||
def test_build_post_data_for_podcast_falls_back_to_track(
|
||||
frontend, podcast_track
|
||||
):
|
||||
post_data = frontend._build_post_data(podcast_track, time_position=0)
|
||||
|
||||
assert post_data["media_type"] == "podcast"
|
||||
assert post_data["name"] == podcast_track.name
|
||||
assert post_data["podcast_name"] == "TED Talks Daily"
|
||||
assert post_data["podcast_producer"] == "TED Talks Daily"
|
||||
assert post_data["podcast_feed_url"] == ""
|
||||
|
||||
|
||||
def test_build_post_data_for_remote_podcast_uri(frontend):
|
||||
track = models.Track(
|
||||
uri="podcast+https://feeds.npr.org/510318/podcast.xml#85b9c4c4-guid",
|
||||
name="UN General Assembly Week",
|
||||
artists=[models.Artist(name="NPR")],
|
||||
album=models.Album(name="Up First"),
|
||||
length=900000,
|
||||
)
|
||||
|
||||
post_data = frontend._build_post_data(track, time_position=0)
|
||||
|
||||
assert post_data["media_type"] == "podcast"
|
||||
assert post_data["podcast_name"] == "Up First"
|
||||
assert (
|
||||
post_data["podcast_feed_url"]
|
||||
== "https://feeds.npr.org/510318/podcast.xml"
|
||||
)
|
||||
assert post_data["episode_guid"] == "85b9c4c4-guid"
|
||||
|
||||
|
||||
@mock.patch("mopidy_webhooks.frontend.requests.post")
|
||||
def test_track_playback_started_posts_started(mock_post, frontend, track):
|
||||
frontend.on_start()
|
||||
|
||||
frontend.stop.assert_called_with()
|
||||
frontend.track_playback_started(models.TlTrack(track=track, tlid=1))
|
||||
|
||||
assert mock_post.call_count == 2
|
||||
first_call = mock_post.call_args_list[0]
|
||||
assert first_call.args[0] == "http://127.0.0.1/receiver/"
|
||||
assert first_call.kwargs["headers"] == {
|
||||
"Authorization": "Token secrettoken"
|
||||
}
|
||||
assert '"status": "started"' in first_call.kwargs["json"]
|
||||
|
||||
|
||||
def test_track_playback_started_updates_now_playing(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
artists = [models.Artist(name="ABC"), models.Artist(name="XYZ")]
|
||||
album = models.Album(name="The Collection")
|
||||
@mock.patch("mopidy_webhooks.frontend.requests.post")
|
||||
def test_track_playback_ended_scrobbles_played_track(
|
||||
mock_post, frontend, track
|
||||
):
|
||||
frontend.on_start()
|
||||
|
||||
frontend.track_playback_ended(models.TlTrack(track=track, tlid=1), 150000)
|
||||
|
||||
assert mock_post.call_count == 2
|
||||
assert '"status": "stopped"' in mock_post.call_args.kwargs["json"]
|
||||
|
||||
|
||||
@mock.patch("mopidy_webhooks.frontend.requests.post")
|
||||
def test_does_not_scrobble_if_played_less_than_half(mock_post, frontend, track):
|
||||
frontend.on_start()
|
||||
|
||||
frontend.track_playback_ended(models.TlTrack(track=track, tlid=1), 60432)
|
||||
|
||||
assert mock_post.call_count == 0
|
||||
|
||||
|
||||
@mock.patch("mopidy_webhooks.frontend.requests.post")
|
||||
def test_track_playback_paused_and_resumed_post(mock_post, frontend, track):
|
||||
frontend.on_start()
|
||||
|
||||
frontend.track_playback_paused(models.TlTrack(track=track, tlid=1), 15000)
|
||||
frontend.track_playback_resumed(models.TlTrack(track=track, tlid=1), 15000)
|
||||
|
||||
statuses = [call.kwargs["json"] for call in mock_post.call_args_list]
|
||||
assert any('"status": "paused"' in payload for payload in statuses)
|
||||
assert any('"status": "resumed"' in payload for payload in statuses)
|
||||
|
||||
|
||||
@mock.patch("mopidy_webhooks.frontend.requests.post")
|
||||
def test_no_urls_configured_posts_nothing(mock_post, frontend, track):
|
||||
frontend.on_start()
|
||||
frontend.webhook_urls = []
|
||||
|
||||
frontend.track_playback_started(models.TlTrack(track=track, tlid=1))
|
||||
|
||||
assert mock_post.call_count == 0
|
||||
|
||||
|
||||
def test_metadata_cache_avoids_repeated_reads(frontend, monkeypatch, tmp_path):
|
||||
media_file = tmp_path / "song.mp3"
|
||||
media_file.write_bytes(b"data")
|
||||
frontend.media_dirs = [str(tmp_path)]
|
||||
track = models.Track(
|
||||
name="One Two Three",
|
||||
artists=artists,
|
||||
album=album,
|
||||
track_no=3,
|
||||
length=180432,
|
||||
musicbrainz_id="123-456",
|
||||
uri=f"local:track:{media_file.name}",
|
||||
name="Song",
|
||||
artists=[models.Artist(name="Someone", uri="local:artist:someone")],
|
||||
length=180000,
|
||||
)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_started(tl_track)
|
||||
|
||||
frontend.lastfm.update_now_playing.assert_called_with(
|
||||
"ABC, XYZ",
|
||||
"One Two Three",
|
||||
duration="180",
|
||||
album="The Collection",
|
||||
track_number="3",
|
||||
mbid="123-456",
|
||||
reads = []
|
||||
monkeypatch.setattr(
|
||||
metadata_lib,
|
||||
"read_metadata",
|
||||
lambda path: reads.append(path) or {"title": "Tagged"},
|
||||
)
|
||||
|
||||
frontend._build_post_data(track, time_position=0)
|
||||
frontend._build_post_data(track, time_position=1000)
|
||||
|
||||
def test_track_playback_started_has_default_values(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
track = models.Track()
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_started(tl_track)
|
||||
|
||||
frontend.lastfm.update_now_playing.assert_called_with(
|
||||
"", "", duration="0", album="", track_number="0", mbid=""
|
||||
)
|
||||
|
||||
|
||||
def test_track_playback_started_catches_pylast_error(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
pylast_mock.NetworkError = pylast.NetworkError
|
||||
frontend.lastfm.update_now_playing.side_effect = pylast.NetworkError(
|
||||
None, "foo"
|
||||
)
|
||||
track = models.Track()
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_started(tl_track)
|
||||
|
||||
|
||||
def test_track_playback_ended_scrobbles_played_track(pylast_mock, frontend):
|
||||
frontend.last_start_time = 123
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
artists = [models.Artist(name="ABC"), models.Artist(name="XYZ")]
|
||||
album = models.Album(name="The Collection")
|
||||
track = models.Track(
|
||||
name="One Two Three",
|
||||
artists=artists,
|
||||
album=album,
|
||||
track_no=3,
|
||||
length=180432,
|
||||
musicbrainz_id="123-456",
|
||||
)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 150000)
|
||||
|
||||
frontend.lastfm.scrobble.assert_called_with(
|
||||
"ABC, XYZ",
|
||||
"One Two Three",
|
||||
"123",
|
||||
duration="180",
|
||||
album="The Collection",
|
||||
track_number="3",
|
||||
mbid="123-456",
|
||||
)
|
||||
|
||||
|
||||
def test_track_playback_ended_has_default_values(pylast_mock, frontend):
|
||||
frontend.last_start_time = 123
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
track = models.Track(length=180432)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 150000)
|
||||
|
||||
frontend.lastfm.scrobble.assert_called_with(
|
||||
"", "", "123", duration="180", album="", track_number="0", mbid=""
|
||||
)
|
||||
|
||||
|
||||
def test_does_not_scrobble_tracks_shorter_than_30_sec(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
track = models.Track(length=20432)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 20432)
|
||||
|
||||
assert frontend.lastfm.scrobble.call_count == 0
|
||||
|
||||
|
||||
def test_does_not_scrobble_if_played_less_than_half(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
track = models.Track(length=180432)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 60432)
|
||||
|
||||
assert frontend.lastfm.scrobble.call_count == 0
|
||||
|
||||
|
||||
def test_does_scrobble_if_played_not_half_but_240_sec(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
track = models.Track(length=880432)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 241432)
|
||||
|
||||
assert frontend.lastfm.scrobble.call_count == 1
|
||||
|
||||
|
||||
def test_track_playback_ended_catches_pylast_error(pylast_mock, frontend):
|
||||
frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork)
|
||||
pylast_mock.NetworkError = pylast.NetworkError
|
||||
frontend.lastfm.scrobble.side_effect = pylast.NetworkError(None, "foo")
|
||||
track = models.Track(length=180432)
|
||||
tl_track = models.TlTrack(track=track, tlid=17)
|
||||
|
||||
frontend.track_playback_ended(tl_track, 150000)
|
||||
assert len(reads) == 1
|
||||
|
||||
186
tests/test_metadata.py
Normal file
186
tests/test_metadata.py
Normal file
@ -0,0 +1,186 @@
|
||||
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",
|
||||
)
|
||||
Reference in New Issue
Block a user