From c23eda1c42625ff300f65c4ce66fb469f2695c0d Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sat, 14 Dec 2019 14:42:59 +0100 Subject: [PATCH] Added support for paging playlist edit calls It also addresses the comments in #236 --- mopidy_spotify/playlists.py | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/mopidy_spotify/playlists.py b/mopidy_spotify/playlists.py index 726300bd..b9992f96 100644 --- a/mopidy_spotify/playlists.py +++ b/mopidy_spotify/playlists.py @@ -11,6 +11,9 @@ class SpotifyPlaylistsProvider(backend.PlaylistsProvider): + # Maximum number of items accepted by the Spotify Web API + _chunk_size = 100 + def __init__(self, backend): self._backend = backend self._timeout = self._backend._config["spotify"]["timeout"] @@ -55,6 +58,11 @@ def _get_user_and_playlist_id_from_uri(uri): playlist_id = uri.split(':')[-1] return user_id, playlist_id + @staticmethod + def partitions(lst, n): + for i in range(0, len(lst), n): + yield lst[i:i+n] + def _playlist_edit(self, playlist, method, **kwargs): user_id, playlist_id = self._get_user_and_playlist_id_from_uri(playlist.uri) url = f'users/{user_id}/playlists/{playlist_id}/tracks' @@ -91,7 +99,8 @@ def refresh(self): def create(self, name): logger.info(f'Creating playlist {name}') url = f'users/{web_client.user_id}/playlists' - response = self._backend._web_client.post(url) + response = self._backend._web_client.post(url, json={'name': name}) + self.refresh() return self.lookup(response['uri']) def delete(self, uri): @@ -116,12 +125,10 @@ def save(self, playlist): logger.info(f'Removing {len(removed_uris)} tracks from playlist ' + f'{saved_playlist.name}: {removed_uris}') - cur_tracks = { - track.uri: track - for track in self._playlist_edit( - playlist, method='delete', - tracks=[{'uri': uri for uri in removed_uris}]).tracks - } + for chunk in self.partitions(removed_uris, self._chunk_size): + saved_playlist = self._playlist_edit(saved_playlist, method='delete', + tracks=[{'uri': uri for uri in removed_uris}]) + cur_tracks = {track.uri: track for track in saved_playlist.tracks} # Add tracks logic position = None @@ -138,14 +145,15 @@ def save(self, playlist): if added_uris: for pos, uris in added_uris.items(): - logger.info(f'Adding {uris} to playlist {playlist.name}') + logger.info(f'Adding {uris} to playlist {saved_playlist.name}') + processed_tracks = 0 + + for chunk in self.partitions(uris): + saved_playlist = self._playlist_edit(saved_playlist, method='post', + uris=chunk, position=pos+processed_tracks) - cur_tracks = { - track.uri: track - for track in self._playlist_edit( - playlist, method='post', - uris=uris, position=pos).tracks - } + cur_tracks = {track.uri: track for track in saved_playlist.tracks} + processed_tracks += len(chunk) # Swap tracks logic cur_tracks_by_uri = {} @@ -161,12 +169,12 @@ def save(self, playlist): cur_pos = cur_tracks_by_uri[track.uri] new_pos = i+1 logger.info(f'Moving item position [{cur_pos}] to [{new_pos}] in ' + - f'playlist {playlist.name}') + f'playlist {saved_playlist.name}') cur_tracks = { track.uri: track for track in self._playlist_edit( - playlist, method='put', + saved_playlist, method='put', range_start=cur_pos, insert_before=new_pos).tracks }