diff --git a/beets/metadata_plugins.py b/beets/metadata_plugins.py index e116d1d520..6d285fe804 100644 --- a/beets/metadata_plugins.py +++ b/beets/metadata_plugins.py @@ -412,6 +412,13 @@ def _search_api( if self.config["search_query_ascii"].get(): query = unidecode.unidecode(query) + if not query and not filters: + # Items without usable metadata produce an empty search request, + # which APIs reject (e.g. MusicBrainz answers with a 400). Report + # no candidates instead of making a doomed request. + self._log.debug("Skipping search with empty query and filters") + return () + limit = self.config["search_limit"].get(int) params = SearchParams(query_type, query, filters, limit) diff --git a/docs/changelog.rst b/docs/changelog.rst index acbd3294fe..34296f95a4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -95,6 +95,12 @@ Bug fixes valid date/time string" error instead of crashing with an uncaught ``KeyError``. A ``|`` was being accepted as a relative-date unit due to a regular expression character-class typo. +- Plugins built on ``SearchApiMetadataSourcePlugin`` (:doc:`plugins/musicbrainz`, + :doc:`plugins/spotify`, :doc:`plugins/deezer` and :doc:`plugins/discogs`) no + longer send a search request when both the query text and the filters are + empty, as happens for items with no artist and title tags. Such a request + cannot match anything and MusicBrainz rejects it with ``400 Bad Request``; + these searches now report no candidates instead. :bug:`6862` .. For plugin developers diff --git a/test/test_metadata_plugins.py b/test/test_metadata_plugins.py index 8f57ddbd9f..4d677989fe 100644 --- a/test/test_metadata_plugins.py +++ b/test/test_metadata_plugins.py @@ -127,6 +127,14 @@ def test_search_api_raises_when_raise_on_error_enabled( with pytest.raises(ValueError, match="Search failure"): search_plugin._search_api("track", "query", {}) + def test_search_api_skips_request_without_query_and_filters( + self, config, search_plugin + ): + """Empty searches are rejected by APIs, so do not send them.""" + config["raise_on_error"] = True + + assert search_plugin._search_api("track", "", {}) == () + def test_albums_for_ids_calls_each_plugin_once(monkeypatch): start_workers = Event()