Source code for minim.api.qobuz._private_api.genres

from __future__ import annotations
from typing import TYPE_CHECKING

from ..._shared import TTLCache
from ._shared import PrivateQobuzResourceAPI

if TYPE_CHECKING:
    from typing import Any


[docs] class PrivateGenresAPI(PrivateQobuzResourceAPI): """ Genres API endpoints for the private Qobuz API. .. important:: This class is managed by :class:`~minim.api.qobuz.PrivateQobuzAPIClient` and should not be instantiated directly. """ __slots__ = ()
[docs] @TTLCache.cached_method(ttl="static") def available_genres(self) -> dict[str, dict[str, Any]]: """ Available genres. .. note:: Accessing this property may call :meth:`get_genres` and make multiple requests to the Qobuz API. """ genres = [] # Use iterative depth-first search stack = [None] while stack: subgenres = self._client.genres.get_genres(stack.pop())["genres"] if subgenres["total"] > 0: genres.extend(subgenres["items"]) stack.extend(genre["id"] for genre in subgenres["items"]) # Use recursive depth-first search # def get_genres( # genre_id: str | None = None, /, *, genres: list[dict[str, Any]] # ) -> dict[str, Any]: # subgenres = self._client.genres.get_genres(genre_id)["genres"] # if subgenres["total"] > 0: # genres.extend(subgenres["items"]) # for subgenre in subgenres["items"]: # get_genres(subgenre["id"], genres=genres) # get_genres(genres=genres) return {genre.pop("id"): genre for genre in genres}
def _validate_genre_id(self, genre_id: int | str, /) -> None: """ Validate genre ID. Parameters ---------- genre_id : str; positional-only Genre ID. """ try: genre_id = int(genre_id) except TypeError: raise TypeError( "Qobuz genre IDs must be integers or their string " "representations." ) if ( cache := self._client._cache ) and "available_genres" in cache._store: if genre_id not in self.available_genres: raise ValueError( f"Invalid genre ID {genre_id!r}. Valid values: " f"{self._join_values(self.available_genres.keys())}." )
[docs] @TTLCache.cached_method(ttl="static") def get_genre(self, genre_id: int | str, /) -> dict[str, Any]: """ Get Qobuz catalog information for a genre. Parameters ---------- genre_id : int or str; positional-only Qobuz ID of the genre. **Examples**: :code:`10`, :code:`"64"`. Returns ------- genre : dict[str, Any] Qobuz metadata for the genre. .. admonition:: Sample response :class: response dropdown .. code-block:: { "color": <str>, "id": <int>, "name": <str>, "path": <list[int]>, "slug": <str> } """ self._validate_qobuz_ids(genre_id, recursive=False) return self._client._request( "GET", "genre/get", params={"genre_id": genre_id} ).json()
[docs] @TTLCache.cached_method(ttl="static") def get_genres( self, genre_id: int | str | None = None, /, *, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ Get Qobuz catalog information for available top-level genres or the subgenres of a specific top-level genre. Parameters ---------- genre_id : int or str; positional-only; optional Qobuz ID of the top-level genre. If not provided, all top-level genres are returned. **Examples**: :code:`10`, :code:`"64"`. limit : int; keyword-only; optional Maximum number of genres to return. **Valid range**: :code:`1` to :code:`500`. **API default**: :code:`25`. offset : int; keyword-only; optional Index of the first genre to return. Use with `limit` to get the next batch of genres. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- genres : dict[str, Any] Page of Qobuz metadata for the genres. .. admonition:: Sample response :class: response dropdown .. code-block:: { "genres": { "items": [ { "color": <str>, "id": <int>, "name": <str>, "path": <list[int]>, "slug": <str> } ], "limit": <int>, "offset": <int>, "total": <int> }, "parent": { "color": <str>, "id": <int>, "name": <str>, "path": <list[int]>, "slug": <str> } } """ params = {} if genre_id is not None: self._validate_qobuz_ids(genre_id, recursive=False) params["parent_id"] = genre_id return self._get_paginated_resources( "genre/list", limit=limit, offset=offset, params=params )