Skip to content

Commit 56d48f2

Browse files
authored
Merge pull request #378 from EbbLabs/feature/use-sensible-limits
Feature/use sensible limits
2 parents 29d5153 + aae07bb commit 56d48f2

File tree

8 files changed

+100
-31
lines changed

8 files changed

+100
-31
lines changed

HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
History
44
=======
55

6+
v0.8.9
7+
--------
8+
* Bugfix: Favorite videos default limit incorrect - tehkillerbee_
9+
* Tests: Added get_favorite_* tests - tehkillerbee_
10+
611
v0.8.8
712
--------
813
* Bugfix: OAuth Client ID, secret updated - tehkillerbee_

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ shell:
2020
install:
2121
rm -rf dist
2222
poetry build
23-
pip install dist/*.whl
23+
pip install dist/*.whl --break-system-packages
2424

2525
format:
2626
${POETRY} isort tidalapi tests

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
author = "The tidalapi Developers"
2424

2525
# The full version, including alpha/beta/rc tags
26-
release = "0.8.8"
26+
release = "0.8.9"
2727

2828

2929
# -- General configuration ---------------------------------------------------

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "tidalapi"
3-
version = "0.8.8"
3+
version = "0.8.9"
44
description = "Unofficial API for TIDAL music streaming service."
55
authors = ["Thomas Amland <[email protected]>"]
66
maintainers = ["tehkillerbee <[email protected]>"]

tests/test_user.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,14 @@ def assert_playlists_present(expected_ids: list[str], should_exist: bool):
417417
assert_playlists_present(playlists_multiple, should_exist=False)
418418

419419

420+
def test_get_favorite_tracks(session):
421+
favorites = session.user.favorites
422+
tracks = favorites.tracks_paginated()
423+
tracks_count = favorites.get_tracks_count()
424+
assert len(tracks) > 0 # and tracks_count == len(tracks)
425+
assert isinstance(tracks[0], tidalapi.Track)
426+
427+
420428
def test_add_remove_favorite_track(session):
421429
favorites = session.user.favorites
422430
track_id = 32961853
@@ -458,17 +466,18 @@ def assert_tracks_present(expected_ids: list[str], should_exist: bool):
458466
assert_tracks_present(tracks_multiple, should_exist=False)
459467

460468

461-
def test_add_remove_favorite_video(session):
469+
def test_get_favorite_videos(session):
462470
favorites = session.user.favorites
463-
video_id = 160850422
464-
add_remove(video_id, favorites.add_video, favorites.remove_video, favorites.videos)
471+
videos = favorites.videos_paginated()
472+
videos_count = favorites.get_videos_count()
473+
assert len(videos) == videos_count and videos_count > 0
474+
assert isinstance(videos[0], tidalapi.media.Video)
465475

466476

467-
def test_get_favorite_mixes(session):
477+
def test_add_remove_favorite_video(session):
468478
favorites = session.user.favorites
469-
mixes = favorites.mixes()
470-
assert len(mixes) > 0
471-
assert isinstance(mixes[0], tidalapi.MixV2)
479+
video_id = 160850422
480+
add_remove(video_id, favorites.add_video, favorites.remove_video, favorites.videos)
472481

473482

474483
def test_get_favorite_playlists_order(session):
@@ -517,6 +526,14 @@ def get_playlist_ids(**kwargs) -> list[str]:
517526
assert session.user.favorites.remove_playlist(playlist_ids)
518527

519528

529+
def test_get_favorite_albums(session):
530+
favorites = session.user.favorites
531+
albums = favorites.albums_paginated()
532+
albums_count = favorites.get_albums_count()
533+
assert len(albums) > 0 and albums_count == len(albums)
534+
assert isinstance(albums[0], tidalapi.Album)
535+
536+
520537
def test_get_favorite_albums_order(session):
521538
album_ids = [
522539
"446470480",
@@ -576,6 +593,13 @@ def get_album_ids(**kwargs) -> list[str]:
576593
assert session.user.favorites.remove_album(album_id)
577594

578595

596+
def test_get_favorite_mixes(session):
597+
favorites = session.user.favorites
598+
mixes = favorites.mixes()
599+
assert len(mixes) > 0
600+
assert isinstance(mixes[0], tidalapi.MixV2)
601+
602+
579603
def test_get_favorite_mixes_order(session):
580604
mix_ids = [
581605
"0007646f7c64d03d56846ed25dae3d",
@@ -633,6 +657,14 @@ def get_mix_ids(**kwargs) -> list[str]:
633657
assert session.user.favorites.remove_mixes(mix_ids, validate=True)
634658

635659

660+
def test_get_favorite_artists(session):
661+
favorites = session.user.favorites
662+
artists = favorites.artists_paginated()
663+
artists_count = favorites.get_artists_count()
664+
assert len(artists) > 0 and artists_count == len(artists)
665+
assert isinstance(artists[0], tidalapi.Artist)
666+
667+
636668
def test_get_favorite_artists_order(session):
637669
artist_ids = [
638670
"4836523",

tidalapi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
User,
1818
)
1919

20-
__version__ = "0.8.8"
20+
__version__ = "0.8.9"

tidalapi/session.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,9 @@ def token_refresh(self, refresh_token: str) -> bool:
739739
request = self.request_session.post(url, params)
740740
json = request.json()
741741
if request.status_code != 200:
742-
raise AuthenticationError("Authentication failed")
743-
# raise AuthenticationError(Authentication failed json["error"], json["error_description"])
742+
raise AuthenticationError(
743+
f"Authentication failed with error '{json['error']}: {json['error_description']}'"
744+
)
744745
if not request.ok:
745746
log.warning("The refresh token has expired, a new login is required.")
746747
return False

tidalapi/user.py

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ def public_playlists(
167167
) -> List[Union["Playlist", "UserPlaylist"]]:
168168
"""Get the (public) playlists created by the user.
169169
170-
:param limit: The index of the first item you want included.
171-
:param offset: The amount of items you want returned.
170+
:param limit: The number of items you want returned.
171+
:param offset: The index of the first item you want included.
172172
:return: List of public playlists.
173173
"""
174174
params = {"limit": limit, "offset": offset}
@@ -564,14 +564,14 @@ def artists_paginated(
564564

565565
def artists(
566566
self,
567-
limit: Optional[int] = None,
567+
limit: int = 50,
568568
offset: int = 0,
569569
order: Optional[ArtistOrder] = None,
570570
order_direction: Optional[OrderDirection] = None,
571571
) -> List["Artist"]:
572572
"""Get the users favorite artists.
573573
574-
:param limit: Optional; The amount of artists you want returned.
574+
:param limit: The number of artist you want returned.
575575
:param offset: The index of the first artist you want included.
576576
:param order: Optional; A :class:`ArtistOrder` describing the ordering type when returning the user favorite artists. eg.: "NAME, "DATE"
577577
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
@@ -625,14 +625,14 @@ def albums_paginated(
625625

626626
def albums(
627627
self,
628-
limit: Optional[int] = None,
628+
limit: int = 50,
629629
offset: int = 0,
630630
order: Optional[AlbumOrder] = None,
631631
order_direction: Optional[OrderDirection] = None,
632632
) -> List["Album"]:
633633
"""Get the users favorite albums.
634634
635-
:param limit: Optional; The amount of albums you want returned.
635+
:param limit: The number of albums you want returned.
636636
:param offset: The index of the first album you want included.
637637
:param order: Optional; A :class:`AlbumOrder` describing the ordering type when returning the user favorite albums. eg.: "NAME, "DATE"
638638
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
@@ -684,15 +684,15 @@ def playlists_paginated(
684684

685685
def playlists(
686686
self,
687-
limit: Optional[int] = 50,
687+
limit: int = 50,
688688
offset: int = 0,
689689
order: Optional[PlaylistOrder] = None,
690690
order_direction: Optional[OrderDirection] = None,
691691
) -> List["Playlist"]:
692692
"""Get the users favorite playlists (v2 endpoint), relative to the root folder
693693
This function is limited to 50 by TIDAL, requiring pagination.
694694
695-
:param limit: Optional; The number of playlists you want returned (Note: Cannot exceed 50)
695+
:param limit: The number of playlists you want returned (Note: Cannot exceed 50)
696696
:param offset: The index of the first playlist to fetch
697697
:param order: Optional; A :class:`PlaylistOrder` describing the ordering type when returning the user favorite playlists. eg.: "NAME, "DATE"
698698
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
@@ -728,15 +728,15 @@ def playlists(
728728

729729
def playlist_folders(
730730
self,
731-
limit: Optional[int] = 50,
731+
limit: int = 50,
732732
offset: int = 0,
733733
order: Optional[PlaylistOrder] = None,
734734
order_direction: Optional[OrderDirection] = None,
735735
parent_folder_id: str = "root",
736736
) -> List["Folder"]:
737737
"""Get a list of folders created by the user.
738738
739-
:param limit: Optional; The number of playlists you want returned (Note: Cannot exceed 50)
739+
:param limit: The number of playlists you want returned (Note: Cannot exceed 50)
740740
:param offset: The index of the first playlist folder to fetch
741741
:param order: Optional; A :class:`PlaylistOrder` describing the ordering type when returning the user favorite playlists. eg.: "NAME, "DATE"
742742
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
@@ -797,7 +797,7 @@ def tracks_paginated(
797797
798798
:param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the user favorite tracks. eg.: "NAME, "DATE"
799799
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
800-
:return: A :class:`list` :class:`~tidalapi.playlist.Playlist` objects containing the favorite tracks.
800+
:return: A :class:`list` :class:`~tidalapi.media.Track` objects containing the favorite tracks.
801801
"""
802802
count = self.session.user.favorites.get_tracks_count()
803803
return get_items(
@@ -806,15 +806,15 @@ def tracks_paginated(
806806

807807
def tracks(
808808
self,
809-
limit: Optional[int] = None,
809+
limit: int = 50,
810810
offset: int = 0,
811811
order: Optional[ItemOrder] = None,
812812
order_direction: Optional[OrderDirection] = None,
813813
) -> List["Track"]:
814814
"""Get the users favorite tracks.
815815
816-
:param limit: Optional; The amount of items you want returned.
817-
:param offset: The index of the first item you want included.
816+
:param limit: The number of tracks you want returned.
817+
:param offset: The index of the first track you want included.
818818
:param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the user favorite tracks. eg.: "NAME, "DATE"
819819
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
820820
:return: A :class:`list` of :class:`~tidalapi.media.Track` objects containing all of the favorite tracks.
@@ -847,16 +847,32 @@ def get_tracks_count(
847847
json_obj = self.requests.map_request(f"{self.base_url}/tracks", params=params)
848848
return json_obj.get("totalNumberOfItems", 0)
849849

850+
def videos_paginated(
851+
self,
852+
order: Optional[ItemOrder] = None,
853+
order_direction: Optional[OrderDirection] = None,
854+
) -> List["Video"]:
855+
"""Get the users favorite videos, using pagination.
856+
857+
:param order: Optional; A :class:`ItemOrder` describing the ordering type when returning the user items. eg.: "NAME, "DATE"
858+
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
859+
:return: A :class:`list` :class:`~tidalapi.media.Video` objects containing the favorite videos.
860+
"""
861+
count = self.session.user.favorites.get_videos_count()
862+
return get_items(
863+
self.session.user.favorites.videos, count, order, order_direction
864+
)
865+
850866
def videos(
851867
self,
852-
limit: Optional[int] = None,
868+
limit: int = 50,
853869
offset: int = 0,
854870
order: Optional[VideoOrder] = None,
855871
order_direction: Optional[OrderDirection] = None,
856872
) -> List["Video"]:
857873
"""Get the users favorite videos.
858874
859-
:param limit: Optional; The amount of videos you want returned.
875+
:param limit: The number of videos you want returned.
860876
:param offset: The index of the first video you want included.
861877
:param order: Optional; A :class:`VideoOrder` describing the ordering type when returning the user favorite videos. eg.: "NAME, "DATE"
862878
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"
@@ -877,16 +893,31 @@ def videos(
877893
),
878894
)
879895

896+
def get_videos_count(
897+
self,
898+
) -> int:
899+
"""Get the total number of videos in the user's collection.
900+
901+
This performs a minimal API request (limit=1) to fetch metadata about the tracks
902+
without retrieving all of them. The API response contains 'totalNumberOfItems',
903+
which represents the total items (videos) available.
904+
:return: The number of items available.
905+
"""
906+
params = {"limit": 1, "offset": 0}
907+
908+
json_obj = self.requests.map_request(f"{self.base_url}/videos", params=params)
909+
return json_obj.get("totalNumberOfItems", 0)
910+
880911
def mixes(
881912
self,
882-
limit: Optional[int] = 50,
913+
limit: int = 50,
883914
offset: int = 0,
884915
order: Optional[MixOrder] = None,
885916
order_direction: Optional[OrderDirection] = None,
886917
) -> List["MixV2"]:
887918
"""Get the users favorite mixes & radio.
888919
889-
:param limit: Optional; The amount of mixes you want returned.
920+
:param limit: The number of mixes you want returned.
890921
:param offset: The index of the first mix you want included.
891922
:param order: Optional; A :class:`MixOrder` describing the ordering type when returning the user favorite mixes. eg.: "NAME, "DATE"
892923
:param order_direction: Optional; A :class:`OrderDirection` describing the ordering direction when sorting by `order`. eg.: "ASC", "DESC"

0 commit comments

Comments
 (0)