Skip to content

Implement sorting for remaining favourites + playlists #328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions tidalapi/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ItemOrder, JsonObj, OrderDirection
from tidalapi.user import LoggedInUser

if TYPE_CHECKING:
Expand Down Expand Up @@ -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
)
Expand All @@ -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
)
Expand Down
44 changes: 44 additions & 0 deletions tidalapi/types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
# 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"
120 changes: 102 additions & 18 deletions tidalapi/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,16 @@
from urllib.parse import urljoin

from tidalapi.exceptions import ObjectNotFound
from tidalapi.types import JsonObj
from tidalapi.types import (
AlbumOrder,
ArtistOrder,
ItemOrder,
JsonObj,
MixOrder,
OrderDirection,
PlaylistOrder,
VideoOrder,
)

if TYPE_CHECKING:
from tidalapi.album import Album
Expand Down Expand Up @@ -448,12 +457,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(
Expand All @@ -463,12 +487,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(
Expand All @@ -477,13 +516,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(
Expand All @@ -497,23 +549,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"],
Expand All @@ -522,24 +573,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(
Expand Down