diff --git a/beets/metadata_plugins.py b/beets/metadata_plugins.py index f42e8f690..1c2b6d843 100644 --- a/beets/metadata_plugins.py +++ b/beets/metadata_plugins.py @@ -54,7 +54,7 @@ def album_for_id(_id: str) -> AlbumInfo | None: A single ID can yield just a single album, so we return the first match. """ for plugin in find_metadata_source_plugins(): - if info := plugin.album_for_id(album_id=_id): + if info := plugin.album_for_id(_id): send("albuminfo_received", info=info) return info diff --git a/test/autotag/test_match.py b/test/autotag/test_match.py index 5eaf52d93..a79615def 100644 --- a/test/autotag/test_match.py +++ b/test/autotag/test_match.py @@ -1,6 +1,9 @@ +from typing import ClassVar + import pytest -from beets.autotag import TrackInfo, match +from beets import metadata_plugins +from beets.autotag import AlbumInfo, TrackInfo, match from beets.library import Item from beets.test.helper import ConfigMixin @@ -91,3 +94,109 @@ class TestAssignment(ConfigMixin): expected = dict(zip(items, trackinfo)), [], [] assert match.assign_items(items, trackinfo) == expected + + +class TestTagMultipleDataSources: + @pytest.fixture + def shared_track_id(self): + return "track-12345" + + @pytest.fixture + def shared_album_id(self): + return "album-12345" + + @pytest.fixture(autouse=True) + def _setup_plugins(self, monkeypatch, shared_album_id, shared_track_id): + class StubPlugin: + data_source: ClassVar[str] + data_source_mismatch_penalty = 0 + + @property + def track(self): + return TrackInfo( + artist="Artist", + title="Title", + track_id=shared_track_id, + data_source=self.data_source, + ) + + @property + def album(self): + return AlbumInfo( + [self.track], + artist="Albumartist", + album="Album", + album_id=shared_album_id, + data_source=self.data_source, + ) + + def album_for_id(self, *_): + return self.album + + def track_for_id(self, *_): + return self.track + + def candidates(self, *_, **__): + yield self.album + + def item_candidates(self, *_, **__): + yield self.track + + class DeezerPlugin(StubPlugin): + data_source = "Deezer" + + class DiscogsPlugin(StubPlugin): + data_source = "Discogs" + + monkeypatch.setattr( + metadata_plugins, + "find_metadata_source_plugins", + lambda: [DeezerPlugin(), DiscogsPlugin()], + ) + + def check_proposal(self, proposal): + sources = [ + candidate.info.data_source for candidate in proposal.candidates + ] + assert len(sources) == 2 + assert set(sources) == {"Discogs", "Deezer"} + + @pytest.mark.xfail( + reason="Album ID collisions drop extra sources (#6177)", + raises=AssertionError, + strict=True, + ) + def test_search_album_ids(self, shared_album_id): + _, _, proposal = match.tag_album([Item()], search_ids=[shared_album_id]) + + self.check_proposal(proposal) + + @pytest.mark.xfail( + reason="Album ID collisions drop extra sources (#6177)", + raises=AssertionError, + strict=True, + ) + def test_search_album_current_id(self, shared_album_id): + _, _, proposal = match.tag_album([Item(mb_albumid=shared_album_id)]) + + self.check_proposal(proposal) + + @pytest.mark.xfail( + reason="Track ID collisions drop extra sources (#6177)", + raises=AssertionError, + strict=True, + ) + def test_search_track_ids(self, shared_track_id): + proposal = match.tag_item(Item(), search_ids=[shared_track_id]) + + self.check_proposal(proposal) + + @pytest.mark.xfail( + reason="Track ID collisions drop extra sources (#6177)", + raises=AssertionError, + strict=True, + ) + def test_search_track_current_id(self, shared_track_id): + proposal = match.tag_item(Item(mb_trackid=shared_track_id)) + + self.check_proposal(proposal)