Document new methods

This commit is contained in:
Šarūnas Nejus 2026-03-07 00:10:10 +00:00
parent 4cccb70921
commit 0670611d7a
No known key found for this signature in database
5 changed files with 50 additions and 18 deletions

View file

@ -291,6 +291,12 @@ class IDResponse(TypedDict):
class SearchParams(NamedTuple):
"""Bundle normalized search context passed to provider search hooks.
Shared search orchestration constructs this value so plugin hooks receive
one object describing search intent, query text, and provider filters.
"""
query_type: QueryType
query: str
filters: dict[str, str]
@ -328,6 +334,20 @@ class SearchApiMetadataSourcePlugin(
name: str,
va_likely: bool,
) -> tuple[str, dict[str, str]]:
"""Build query text and API filters for a provider search.
Subclasses can override this hook when their API requires a query format
or filter set that differs from the default text-based construction.
:param query_type: The type of query to perform. Either *album* or *track*
:param items: List of items the search is being performed for
:param artist: Artist name
:param name: Album or track name, depending on ``query_type``
:param va_likely: Whether the search is likely to be for various artists
:return: Tuple of (``query`` text, ``filters`` dict) to use for the
search API call
"""
query = f'album:"{name}"' if query_type == "album" else name
if query_type == "track" or not va_likely:
query += f' artist:"{artist}"'
@ -336,18 +356,25 @@ class SearchApiMetadataSourcePlugin(
@abc.abstractmethod
def get_search_response(self, params: SearchParams) -> Sequence[R]:
"""Fetch raw search results for a provider request.
Implementations should return records containing source IDs so shared
candidate resolution can perform ID-based album and track lookups.
:param params: :py:namedtuple:`~SearchParams` named tuple
:return: Sequence of IDResponse dicts containing at least an "id" key for each
"""
raise NotImplementedError
def _search_api(
self, query_type: QueryType, query: str, filters: dict[str, str]
) -> Sequence[R]:
"""Perform a search on the API.
"""Run shared provider search orchestration and return ID-bearing results.
:param query_type: The type of query to perform.
:param filters: A dictionary of filters to apply to the search.
:param query_string: Additional query to include in the search.
Should return a list of identifiers for the requested type (album or track).
This path applies optional query normalization and default limits, then
delegates API access to provider hooks with consistent logging and
failure handling.
"""
if self.config["search_query_ascii"].get():
query = unidecode.unidecode(query)
@ -372,6 +399,8 @@ class SearchApiMetadataSourcePlugin(
def _get_candidates(
self, query_type: QueryType, *args, **kwargs
) -> Sequence[R]:
"""Resolve query hooks and execute one provider search request."""
return self._search_api(
query_type,
*self.get_search_query_with_filters(query_type, *args, **kwargs),

View file

@ -218,6 +218,8 @@ class DeezerPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
)
def get_search_response(self, params: SearchParams) -> list[IDResponse]:
"""Search Deezer and return the raw result payload entries."""
response = requests.get(
f"{self.search_url}{params.query_type}",
params={

View file

@ -240,6 +240,12 @@ class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
name: str,
va_likely: bool,
) -> tuple[str, dict[str, str]]:
"""Build a Discogs release query and fixed release-type filter.
The query is normalized to improve hit rates for punctuation-heavy album
names and medium suffixes that can reduce recall.
"""
query = f"{artist} {name}" if va_likely else name
# Strip non-word characters from query. Things like "!" and "-" can
# cause a query to return no results, even if they match the artist or
@ -253,7 +259,7 @@ class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
return query, {"type": "release"}
def get_search_response(self, params: SearchParams) -> Sequence[IDResponse]:
"""Returns a list of AlbumInfo objects for a discogs search query."""
"""Search Discogs releases and return raw result mappings with IDs."""
results = self.discogs_client.search(params.query, **params.filters)
results.per_page = params.limit
return [r.data for r in results.page(1)]

View file

@ -728,6 +728,8 @@ class MusicBrainzPlugin(
name: str,
va_likely: bool,
) -> tuple[str, dict[str, str]]:
"""Build MusicBrainz criteria filters for album and recording search."""
if query_type == "album":
criteria = self.get_album_criteria(items, artist, name, va_likely)
else:
@ -738,12 +740,8 @@ class MusicBrainzPlugin(
}
def get_search_response(self, params: SearchParams) -> Sequence[IDResponse]:
"""Perform MusicBrainz API search and return results.
"""Search MusicBrainz and return release or recording result mappings."""
Execute a search against the MusicBrainz API for recordings or releases
using the provided criteria. Handles API errors by converting them into
MusicBrainzAPIError exceptions with contextual information.
"""
mb_entity: Literal["release", "recording"] = (
"release" if params.query_type == "album" else "recording"
)

View file

@ -470,13 +470,10 @@ class SpotifyPlugin(
def get_search_response(
self, params: SearchParams
) -> Sequence[SearchResponseAlbums | SearchResponseTracks]:
"""Query the Spotify Search API for the specified ``query_string``,
applying the provided ``filters``.
"""Search Spotify and return raw album or track result items.
:param query_type: Item type to search across. Valid types are: 'album',
'artist', 'playlist', and 'track'.
:param filters: Field filters to apply.
:param query_string: Additional query to include in the search.
Unauthorized responses trigger one token refresh attempt before the
method gives up and falls back to an empty result set.
"""
for _ in range(2):
response = requests.get(