[soundcloud] improved track iteration (#922)
authorAaron Loo <domanchi@users.noreply.github.com>
Sun, 12 Nov 2023 20:10:24 +0000 (12:10 -0800)
committerGitHub <noreply@github.com>
Sun, 12 Nov 2023 20:10:24 +0000 (21:10 +0100)
music_assistant/server/providers/soundcloud/__init__.py
music_assistant/server/providers/soundcloud/soundcloudpy/asyncsoundcloudpy.py

index 0a8021bbbcd2f870cb663c72169727f80449700e..5941cd5b4714406ee979550d0427ca0ebf6eb6fe 100644 (file)
@@ -192,12 +192,7 @@ class SoundcloudMusicProvider(MusicProvider):
     async def get_library_tracks(self) -> AsyncGenerator[Track, None]:
         """Retrieve library tracks from Soundcloud."""
         time_start = time.time()
-        tracks = await self._soundcloud.get_tracks_liked()
-        self.logger.debug(
-            "Processing Soundcloud library tracks took %s seconds",
-            round(time.time() - time_start, 2),
-        )
-        for item in tracks["collection"]:
+        async for item in self._soundcloud.get_tracks_liked():
             track = await self._soundcloud.get_track_details(item)
             try:
                 yield await self._parse_track(track[0])
@@ -207,6 +202,11 @@ class SoundcloudMusicProvider(MusicProvider):
                 self.logger.debug("Parse track failed: %s", track, exc_info=error)
                 continue
 
+        self.logger.debug(
+            "Processing Soundcloud library tracks took %s seconds",
+            round(time.time() - time_start, 2),
+        )
+
     async def get_artist(self, prov_artist_id) -> Artist:
         """Get full artist details by id."""
         artist_obj = await self._soundcloud.get_user_details(user_id=prov_artist_id)
index f1f25c66b9b42432bd16f21c5fdce0eb5e039f7a..9ba86ac8a140ea518728419b9486f49aa2d17d1c 100644 (file)
@@ -6,6 +6,7 @@ Original package https://github.com/naim-prog/soundcloud-py
 """
 from __future__ import annotations
 
+from collections.abc import AsyncGenerator
 from typing import TYPE_CHECKING
 
 BASE_URL = "https://api-v2.soundcloud.com"
@@ -118,13 +119,42 @@ class SoundcloudAsyncAPI:
             headers=self.headers,
         )
 
-    async def get_tracks_liked(self, limit=50):
-        """:param limit: number of tracks to get"""
-        return await self.get(
+    async def get_tracks_liked(self, limit: int = 0) -> AsyncGenerator[int, None]:
+        """Obtain the authenticated user's liked tracks.
+
+        :param limit: number of tracks to get. if 0, will fetch all tracks.
+        :returns: list of track ids liked by the current user
+        """
+        query_limit = limit
+        if query_limit == 0:
+            # NOTE(2023-11-11): At the time of writing, soundcloud does not look like it caps
+            # the limit. However, we still implement pagination for future proofing.
+            query_limit = 100
+
+        url = (
             f"{BASE_URL}/me/track_likes/ids?client_id={self.client_id}&"
-            f"limit={limit}&app_version={self.app_version}",
-            headers=self.headers,
+            f"limit={query_limit}&app_version={self.app_version}"
         )
+        num_items = 0
+        while True:
+            response = await self.get(url, headers=self.headers)
+
+            # Sanity check.
+            if "collection" not in response:
+                raise RuntimeError("Unexpected Soundcloud API response")
+
+            for item in response["collection"]:
+                num_items += 1
+                if limit > 0 and num_items >= limit:
+                    return
+
+                yield item
+
+            # Handle case when results requested exceeds number of actual results.
+            if len(response["collection"]) < query_limit:
+                return
+
+            url = response["next_href"]
 
     async def get_track_by_genre_recent(self, genre, limit=10):
         """Get track by genre recent.