From 1c6f166d862b006d32356e593fb327f6154db3b9 Mon Sep 17 00:00:00 2001 From: NilaTheDragon Date: Thu, 29 May 2025 15:34:02 +0200 Subject: [PATCH 1/2] Implement sorting for remaining favourites + playlists --- tidalapi/playlist.py | 37 +++++++++++++-- tidalapi/types.py | 37 +++++++++++++++ tidalapi/user.py | 111 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 163 insertions(+), 22 deletions(-) diff --git a/tidalapi/playlist.py b/tidalapi/playlist.py index 259e9834..af028353 100644 --- a/tidalapi/playlist.py +++ b/tidalapi/playlist.py @@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, List, Optional, Sequence, Union, cast from tidalapi.exceptions import ObjectNotFound, TooManyRequests -from tidalapi.types import JsonObj +from tidalapi.types import JsonObj, ItemOrder, OrderDirection from tidalapi.user import LoggedInUser if TYPE_CHECKING: @@ -159,14 +159,30 @@ def parse_factory(self, json_obj: JsonObj) -> "Playlist": self.parse(json_obj) return copy.copy(self.factory()) - def tracks(self, limit: Optional[int] = None, offset: int = 0) -> List["Track"]: + def tracks( + self, + limit: Optional[int] = None, + offset: int = 0, + order: Optional[ItemOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List["Track"]: """Gets the playlists' tracks from TIDAL. :param limit: The amount of items you want returned. :param offset: The index of the first item you want included. + :param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the playlist tracks. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A list of :class:`Tracks <.Track>` """ - params = {"limit": limit, "offset": offset} + params = { + "limit": limit, + "offset": offset, + } + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + request = self.request.request( "GET", self._base_url % self.id + "/tracks", params=params ) @@ -177,14 +193,27 @@ def tracks(self, limit: Optional[int] = None, offset: int = 0) -> List["Track"]: ) ) - def items(self, limit: int = 100, offset: int = 0) -> List[Union["Track", "Video"]]: + def items( + self, + limit: int = 100, + offset: int = 0, + order: Optional[ItemOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List[Union["Track", "Video"]]: """Fetches up to the first 100 items, including tracks and videos. :param limit: The amount of items you want, up to 100. :param offset: The index of the first item you want returned + :param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the playlist items. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A list of :class:`Tracks<.Track>` and :class:`Videos<.Video>` """ params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + request = self.request.request( "GET", self._base_url % self.id + "/items", params=params ) diff --git a/tidalapi/types.py b/tidalapi/types.py index e37a394b..6f1567b4 100644 --- a/tidalapi/types.py +++ b/tidalapi/types.py @@ -1,5 +1,42 @@ # Copyright (C) 2023- The Tidalapi Developers +from enum import Enum from typing import Any, Dict JsonObj = Dict[str, Any] + +class AlbumOrder(Enum): + Artist = "ARTIST" + DateAdded = "DATE" + Name = "NAME" + ReleaseDate = "RELEASE_DATE" + +class ArtistOrder(Enum): + DateAdded = "DATE" + Name = "NAME" + +class ItemOrder(Enum): + Album = "ALBUM" + Artist = "ARTIST" + Date = "DATE" + Index = "INDEX" + Length = "LENGTH" + Name = "NAME" + +class MixOrder(Enum): + DateAdded = "DATE" + MixType = "MIX_TYPE" + Name = "NAME" + +class PlaylistOrder(Enum): + DateCreated = "DATE" + Name = "NAME" + +class VideoOrder(Enum): + Artist = "ARTIST" + DateAdded = "DATE" + Name = "NAME" + +class OrderDirection(Enum): + Ascending = "ASC" + Descending = "DESC" \ No newline at end of file diff --git a/tidalapi/user.py b/tidalapi/user.py index a9924edb..c5e54778 100644 --- a/tidalapi/user.py +++ b/tidalapi/user.py @@ -27,7 +27,7 @@ from urllib.parse import urljoin from tidalapi.exceptions import ObjectNotFound -from tidalapi.types import JsonObj +from tidalapi.types import JsonObj, AlbumOrder, ItemOrder, OrderDirection, PlaylistOrder, ArtistOrder, MixOrder, VideoOrder if TYPE_CHECKING: from tidalapi.album import Album @@ -448,12 +448,27 @@ def remove_folders_playlists(self, trns: [str], type: str = "folder") -> bool: params=params, ).ok - def artists(self, limit: Optional[int] = None, offset: int = 0) -> List["Artist"]: + def artists( + self, + limit: Optional[int] = None, + offset: int = 0, + order: Optional[ArtistOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List["Artist"]: """Get the users favorite artists. + :param limit: Optional; The amount of artists you want returned. + :param offset: The index of the first artist you want included. + :param order: Optional; A :class:`ArtistOrder` describing the ordering type when returning the user favorite artists. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` of :class:`~tidalapi.artist.Artist` objects containing the favorite artists. """ params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + return cast( List["Artist"], self.requests.map_request( @@ -463,12 +478,27 @@ def artists(self, limit: Optional[int] = None, offset: int = 0) -> List["Artist" ), ) - def albums(self, limit: Optional[int] = None, offset: int = 0) -> List["Album"]: + def albums( + self, + limit: Optional[int] = None, + offset: int = 0, + order: Optional[AlbumOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List["Album"]: """Get the users favorite albums. + :param limit: Optional; The amount of albums you want returned. + :param offset: The index of the first album you want included. + :param order: Optional; A :class:`AlbumOrder` describing the ordering type when returning the user favorite albums. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` of :class:`~tidalapi.album.Album` objects containing the favorite albums. """ params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + return cast( List["Album"], self.requests.map_request( @@ -477,13 +507,26 @@ def albums(self, limit: Optional[int] = None, offset: int = 0) -> List["Album"]: ) def playlists( - self, limit: Optional[int] = None, offset: int = 0 + self, + limit: Optional[int] = None, + offset: int = 0, + order: Optional[PlaylistOrder] = None, + order_direction: Optional[OrderDirection] = None, ) -> List["Playlist"]: """Get the users favorite playlists. + :param limit: Optional; The amount of playlists you want returned. + :param offset: The index of the first playlist you want included. + :param order: Optional; A :class:`PlaylistOrder` describing the ordering type when returning the user favorite playlists. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` :class:`~tidalapi.playlist.Playlist` objects containing the favorite playlists. """ params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + return cast( List["Playlist"], self.requests.map_request( @@ -497,23 +540,22 @@ def tracks( self, limit: Optional[int] = None, offset: int = 0, - order: str = "NAME", - order_direction: str = "ASC", + order: Optional[ItemOrder] = None, + order_direction: Optional[OrderDirection] = None, ) -> List["Track"]: """Get the users favorite tracks. :param limit: Optional; The amount of items you want returned. :param offset: The index of the first item you want included. - :param order: A :class:`str` describing the ordering type when returning the user favorite tracks. eg.: "NAME, "DATE" - :param order_direction: A :class:`str` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" + :param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the user favorite tracks. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` of :class:`~tidalapi.media.Track` objects containing all of the favorite tracks. """ - params = { - "limit": limit, - "offset": offset, - "order": order, - "orderDirection": order_direction, - } + params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value return cast( List["Track"], @@ -522,24 +564,57 @@ def tracks( ), ) - def videos(self) -> List["Video"]: + def videos( + self, + limit: Optional[int] = None, + offset: int = 0, + order: Optional[VideoOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List["Video"]: """Get the users favorite videos. + :param limit: Optional; The amount of videos you want returned. + :param offset: The index of the first video you want included. + :param order: Optional; A :class:`VideoOrder` describing the ordering type when returning the user favorite videos. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` of :class:`~tidalapi.media.Video` objects containing all the favorite videos """ + params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + return cast( List["Video"], - self.requests.get_items( - f"{self.base_url}/videos", parse=self.session.parse_media + self.requests.map_request( + f"{self.base_url}/videos", + params=params, + parse=self.session.parse_media, ), ) - def mixes(self, limit: Optional[int] = 50, offset: int = 0) -> List["MixV2"]: + def mixes( + self, + limit: Optional[int] = 50, + offset: int = 0, + order: Optional[MixOrder] = None, + order_direction: Optional[OrderDirection] = None, + ) -> List["MixV2"]: """Get the users favorite mixes & radio. + :param limit: Optional; The amount of mixes you want returned. + :param offset: The index of the first mix you want included. + :param order: Optional; A :class:`MixOrder` describing the ordering type when returning the user favorite mixes. eg.: "NAME, "DATE" + :param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC" :return: A :class:`list` of :class:`~tidalapi.media.Mix` objects containing the user favourite mixes & radio """ params = {"limit": limit, "offset": offset} + if order: + params["order"] = order.value + if order_direction: + params["orderDirection"] = order_direction.value + return cast( List["MixV2"], self.requests.map_request( From 4217756532e43ce0f501b33b70665d56fe246897 Mon Sep 17 00:00:00 2001 From: NilaTheDragon Date: Thu, 29 May 2025 15:43:02 +0200 Subject: [PATCH 2/2] Formatting --- tidalapi/playlist.py | 2 +- tidalapi/types.py | 9 ++++++++- tidalapi/user.py | 11 ++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/tidalapi/playlist.py b/tidalapi/playlist.py index af028353..6ba76843 100644 --- a/tidalapi/playlist.py +++ b/tidalapi/playlist.py @@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, List, Optional, Sequence, Union, cast from tidalapi.exceptions import ObjectNotFound, TooManyRequests -from tidalapi.types import JsonObj, ItemOrder, OrderDirection +from tidalapi.types import ItemOrder, JsonObj, OrderDirection from tidalapi.user import LoggedInUser if TYPE_CHECKING: diff --git a/tidalapi/types.py b/tidalapi/types.py index 6f1567b4..50b4711b 100644 --- a/tidalapi/types.py +++ b/tidalapi/types.py @@ -5,16 +5,19 @@ JsonObj = Dict[str, Any] + class AlbumOrder(Enum): Artist = "ARTIST" DateAdded = "DATE" Name = "NAME" ReleaseDate = "RELEASE_DATE" + class ArtistOrder(Enum): DateAdded = "DATE" Name = "NAME" + class ItemOrder(Enum): Album = "ALBUM" Artist = "ARTIST" @@ -23,20 +26,24 @@ class ItemOrder(Enum): Length = "LENGTH" Name = "NAME" + class MixOrder(Enum): DateAdded = "DATE" MixType = "MIX_TYPE" Name = "NAME" + class PlaylistOrder(Enum): DateCreated = "DATE" Name = "NAME" + class VideoOrder(Enum): Artist = "ARTIST" DateAdded = "DATE" Name = "NAME" + class OrderDirection(Enum): Ascending = "ASC" - Descending = "DESC" \ No newline at end of file + Descending = "DESC" diff --git a/tidalapi/user.py b/tidalapi/user.py index c5e54778..9d09fac5 100644 --- a/tidalapi/user.py +++ b/tidalapi/user.py @@ -27,7 +27,16 @@ from urllib.parse import urljoin from tidalapi.exceptions import ObjectNotFound -from tidalapi.types import JsonObj, AlbumOrder, ItemOrder, OrderDirection, PlaylistOrder, ArtistOrder, MixOrder, VideoOrder +from tidalapi.types import ( + AlbumOrder, + ArtistOrder, + ItemOrder, + JsonObj, + MixOrder, + OrderDirection, + PlaylistOrder, + VideoOrder, +) if TYPE_CHECKING: from tidalapi.album import Album