from __future__ import annotations
from typing import TYPE_CHECKING
from ..._shared import TTLCache
from ._shared import PrivateQobuzResourceAPI
if TYPE_CHECKING:
from typing import Any
from ...._types import Collection
[docs]
class PrivateFavoritesAPI(PrivateQobuzResourceAPI):
"""
Favorites API endpoints for the private Qobuz API.
.. important::
This class is managed by
:class:`~minim.api.qobuz.PrivateQobuzAPIClient` and should not be
instantiated directly.
"""
_FAVORITE_TYPES = {
"albums",
"artists",
"articles",
"awards",
"labels",
"tracks",
}
__slots__ = ()
[docs]
def save_items(
self,
*,
album_ids: str | Collection[str] | None = None,
artist_ids: int | str | Collection[int | str] | None = None,
track_ids: int | str | Collection[int | str] | None = None,
) -> dict[str, str]:
"""
Favorite one or more albums, artists, and/or tracks.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
.. important::
At least one of :code:`album_ids`, :code:`artist_ids`, or
:code:`track_ids` must be specified.
Parameters
----------
album_ids : str or Collection[str]; keyword-only; optional
Qobuz IDs of the albums.
**Examples**: :code:`"0075679933652"`,
:code:`"0075679933652,aaxy9wirwgn2a"`.
artist_ids : int, str, or Collection[int | str]; keyword-only; \
optional
Qobuz IDs of the artists.
**Examples**: :code:`865362`, :code:`"21473137"`,
:code:`"865362,21473137"`, :code:`[865362, "21473137"]`.
track_ids : int, str, or Collection[int | str]; keyword-only; \
optional
Qobuz IDs of the tracks.
**Examples**: :code:`23929516`, :code:`"344521217"`,
:code:`"23929516,344521217"`,
:code:`[23929516, "344521217"]`.
Returns
-------
status : dict[str, str]
Whether the items were favorited successfully.
**Sample response**: :code:`{"status": "success"}`.
"""
self._client._require_authentication("favorites.save_items")
payload = {}
if album_ids is not None:
payload["album_ids"] = self._prepare_album_ids(album_ids)
if artist_ids is not None:
payload["artist_ids"] = self._prepare_qobuz_ids(
artist_ids, data_type=str
)
if track_ids is not None:
payload["track_ids"] = self._prepare_qobuz_ids(
track_ids, data_type=str
)
if not payload:
raise RuntimeError(
"At least one of `album_ids`, `artist_ids`, or "
"`track_ids` must be specified."
)
return self._client._request(
"POST", "favorite/create", data=payload
).json()
[docs]
def remove_saved_items(
self,
*,
album_ids: str | Collection[str] | None = None,
artist_ids: int | str | Collection[int | str] | None = None,
track_ids: int | str | Collection[int | str] | None = None,
) -> dict[str, str]:
"""
Unfavorite one or more albums, artists, and/or tracks.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
.. important::
At least one of :code:`album_ids`, :code:`artist_ids`, or
:code:`track_ids` must be specified.
Parameters
----------
album_ids : str or Collection[str]; keyword-only; optional
Qobuz IDs of the albums.
**Examples**: :code:`"0075679933652"`,
:code:`"0075679933652,aaxy9wirwgn2a"`.
artist_ids : int, str, or Collection[int | str]; keyword-only; \
optional
Qobuz IDs of the artists.
**Examples**: :code:`865362`, :code:`"21473137"`,
:code:`"865362,21473137"`, :code:`[865362, "21473137"]`.
track_ids : int, str, or Collection[int | str]; keyword-only; \
optional
Qobuz IDs of the tracks.
**Examples**: :code:`23929516`, :code:`"344521217"`,
:code:`"23929516,344521217"`,
:code:`[23929516, "344521217"]`.
Returns
-------
status : dict[str, str]
Whether the items were unfavorited successfully.
**Sample response**: :code:`{"status": "success"}`.
"""
self._client._require_authentication("favorites.remove_saved_items")
payload = {}
if album_ids is not None:
payload["album_ids"] = self._prepare_album_ids(album_ids)
if artist_ids is not None:
payload["artist_ids"] = self._prepare_qobuz_ids(
artist_ids, data_type=str
)
if track_ids is not None:
payload["track_ids"] = self._prepare_qobuz_ids(
track_ids, data_type=str
)
if not payload:
raise RuntimeError(
"At least one of `album_ids`, `artist_ids`, or "
"`track_ids` must be specified."
)
return self._client._request(
"POST", "favorite/delete", data=payload
).json()
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_saved_items(
self,
item_type: str | None = None,
/,
*,
limit: int | None = None,
offset: int | None = None,
) -> dict[str, Any]:
"""
Get Qobuz catalog information for the current user's favorites.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
Parameters
----------
item_type : str; positional-only; optional
Type of item to return. If not specified, favorites of all
types are returned.
**Valid values**: :code:`"albums"`, :code:`"articles"`,
:code:`"artists"`, :code:`"awards"`, :code:`"labels"`,
:code:`"tracks"`.
limit : int; keyword-only; optional
Maximum number of items to return.
**Valid range**: :code:`1` to :code:`500`.
**API default**: :code:`50`.
offset : int; keyword-only; optional
Index of the first item to return. Use with `limit` to get
the next batch of items.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
Returns
-------
items : dict[str, Any]
Page of Qobuz metadata for the current user's favorites.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"albums": {
"items": [
{
"articles": [],
"artist": {
"albums_count": <int>,
"id": <int>,
"image": None,
"name": <str>,
"picture": None,
"slug": <str>,
},
"artists": [
{
"id": <int>,
"name": <str>,
"roles": <list[str]>
}
],
"displayable": <bool>,
"downloadable": <bool>,
"duration": <int>,
"favorited_at": <int>,
"genre": {
"color": <str>,
"id": <int>,
"name": <str>,
"path": <list[int]>,
"slug": <str>
},
"hires": <bool>,
"hires_streamable": <bool>,
"id": <str>,
"image": {
"back": None,
"large": <str>,
"small": <str>,
"thumbnail": <str>,
},
"label": {
"albums_count": <int>,
"id": <int>,
"name": <str>,
"slug": <str>,
"supplier_id": <int>
},
"maximum_bit_depth": <int>,
"maximum_channel_count": <int>,
"maximum_sampling_rate": <float>,
"media_count": <int>,
"parental_warning": <bool>,
"popularity": <int>,
"previewable": <bool>,
"purchasable": <bool>,
"purchasable_at": <int>,
"qobuz_id": <int>,
"release_date_download": <str>,
"release_date_original": <str>,
"release_date_stream": <str>,
"released_at": <int>,
"sampleable": <bool>,
"slug": <str>,
"streamable": <bool>,
"streamable_at": <int>,
"title": <str>,
"tracks_count": <int>,
"upc": <str>,
"url": <str>,
"version": <str>
}
],
"limit": <int>,
"offset": <int>,
"total": <int>
},
"artists": {
"items": [
{
"albums_count": <int>,
"favorited_at": <int>,
"id": <int>,
"image": {
"extralarge": <str>,
"large": <str>,
"medium": <str>,
"mega": <str>,
"small": <str>,
},
"name": <str>,
"slug": <str>
}
],
"limit": <int>,
"offset": <int>,
"total": <int>
},
"tracks": {
"items": [
{
"album": {
"artist": {
"albums_count": <int>,
"id": <int>,
"image": None,
"name": <str>,
"picture": None,
"slug": <str>
},
"displayable": <bool>,
"downloadable": <bool>,
"duration": <int>,
"genre": {
"id": <int>,
"name": <str>,
"path": <list[int]>,
"slug": <str>
},
"hires": <bool>,
"hires_streamable": <bool>,
"id": <str>,
"image": {
"large": <str>,
"small": <str>,
"thumbnail": <str>
},
"label": {
"albums_count": <int>,
"id": <int>,
"name": <str>,
"slug": <str>,
"supplier_id": <int>
},
"maximum_bit_depth": <int>,
"maximum_channel_count": <int>,
"maximum_sampling_rate": <float>,
"media_count": <int>,
"parental_warning": <bool>,
"previewable": <bool>,
"purchasable": <bool>,
"purchasable_at": null,
"qobuz_id": <int>,
"release_date_download": <str>,
"release_date_original": <str>,
"release_date_purchase": <str>,
"release_date_stream": <str>,
"released_at": <int>,
"sampleable": <bool>,
"streamable": <bool>,
"streamable_at": <int>,
"title": <str>,
"tracks_count": <int>,
"upc": <str>,
"version": None
},
"audio_info": {
"replaygain_track_gain": <float>,
"replaygain_track_peak": <float>,
},
"composer": {
"id": <int>,
"name": <str>
},
"copyright": <str>,
"displayable": <bool>,
"downloadable": <bool>,
"duration": <int>,
"favorited_at": <int>,
"hires": <bool>,
"hires_streamable": <bool>,
"id": <int>,
"isrc": <str>,
"maximum_bit_depth": <int>,
"maximum_channel_count": <int>,
"maximum_sampling_rate": <float>,
"media_number": <int>,
"parental_warning": <bool>,
"performer": {
"id": <int>,
"name": <str>
},
"performers": <str>,
"previewable": <bool>,
"purchasable": <bool>,
"purchasable_at": <int>,
"release_date_download": <str>,
"release_date_original": <str>,
"release_date_purchase": <str>,
"release_date_stream": <str>,
"sampleable": <bool>,
"streamable": <bool>,
"streamable_at": <int>,
"title": <str>,
"track_number": <int>,
"version": <str>,
"work": None
}
],
"limit": <int>,
"offset": <int>,
"total": <int>
},
"user": {
"id": <int>,
"login": <str>
}
}
"""
self._client._require_authentication("favorites.get_my_saved_items")
params = {}
if item_type is not None:
item_type = self._prepare_string("item_type", item_type).lower()
if item_type not in self._FAVORITE_TYPES:
raise ValueError(
f"Invalid item type {item_type!r}. Valid values: "
f"{self._join_values(self._FAVORITE_TYPES)}."
)
return self._get_paginated_resources(
"favorite/getUserFavorites",
limit=limit,
offset=offset,
params=params,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_saved_item_ids(self) -> dict[str, Any]:
"""
Get Qobuz IDs of the current user's favorites.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
Returns
-------
saved_ids : dict[str, Any]
Qobuz IDs of the current user's favorites.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"albums": <list[str]>,
"articles": [],
"artists": <list[int]>,
"awards": [],
"labels": [],
"tracks": <list[int]>,
}
"""
self._client._require_authentication("favorites.get_my_saved_item_ids")
return self._client._request(
"GET", "favorite/getUserFavoriteIds"
).json()
[docs]
@TTLCache.cached_method(ttl="user")
def is_item_saved(
self, item_type: str, item_id: int | str, /
) -> dict[str, bool]:
"""
Check whether the current user has an item favorited.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
Parameters
----------
item_type : str; positional-only
Type of item.
**Valid values**: :code:`"album"`, :code:`"artist"`,
:code:`"article"`, :code:`"award"`, :code:`"label"`,
:code:`"track"`.
item_id : int or str; positional-only
Qobuz ID of the item.
Returns
-------
saved : dict[str, bool]
Whether the current user has the item favorited.
**Sample response**: :code:`{"status": <bool>}`.
"""
self._client._require_authentication("favorites.is_item_saved")
item_type = self._prepare_string("item_type", item_type).lower()
if f"{item_type}s" not in self._FAVORITE_TYPES:
raise ValueError(
f"Invalid item type {item_type!r}. Valid values: "
f"{self._join_values(ft[:-1] for ft in self._FAVORITE_TYPES)}."
)
if item_type == "album":
self._validate_album_id(item_id)
else:
self._validate_qobuz_ids(item_id, recursive=False)
return self._client._request(
"GET",
"favorite/status",
params={"type": item_type, "item_id": item_id},
).json()
[docs]
def toggle_item_saved(
self, item_type: str, item_id: int | str, /
) -> dict[str, bool]:
"""
Toggle the favorite status of an item.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage your library.
Parameters
----------
item_type : str; positional-only
Type of item.
**Valid values**: :code:`"album"`, :code:`"artist"`,
:code:`"article"`, :code:`"award"`, :code:`"label"`,
:code:`"track"`.
item_id : int or str; positional-only
Qobuz ID of the item.
Returns
-------
saved : dict[str, bool]
Whether the item is now favorited.
**Sample response**: :code:`{"status": <bool>}`.
"""
self._client._require_authentication("favorites.toggle_item_saved")
item_type = self._prepare_string("item_type", item_type).lower()
if f"{item_type}s" not in self._FAVORITE_TYPES:
raise ValueError(
f"Invalid item type {item_type!r}. Valid values: "
f"{self._join_values(ft[:-1] for ft in self._FAVORITE_TYPES)}."
)
if item_type == "album":
self._validate_album_id(item_id)
else:
self._validate_qobuz_ids(item_id, recursive=False)
return self._client._request(
"POST",
"favorite/toggle",
params={"type": item_type, "item_id": item_id},
).json()