Skip to content

Commit af40505

Browse files
committed
Fix exporting large playlists to spotify
We cannot add more than 100 tracks to a playlist, therefore break down submitting tracks into chunks. Earlier, we used to replace all tracks in one go. But replace endpoint doesn't take a position parameter to tell it which tracks should be removed. Therefore, we can't use it directly. The new workflow is to call replace items with an empty uris array to reset the playlist tracks. Then, call add items on each chunk of tracks to insert those.
1 parent 78ec9c9 commit af40505

File tree

4 files changed

+30
-18
lines changed

4 files changed

+30
-18
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ countryinfo>=0.1.2
66
pylistenbrainz@git+https://github.com/metabrainz/[email protected]
77
python-dateutil>=2.8.2
88
spotipy==2.22.1
9+
more_itertools

troi/playlist.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from collections import defaultdict
22
import json
3-
from typing import Dict, Tuple
43

54

65
import requests
76
import spotipy
7+
from more_itertools import chunked
88
from spotipy import SpotifyException
99

1010
from troi import Recording, Playlist, PipelineError, Element, Artist, Release
@@ -277,8 +277,14 @@ def submit_to_spotify(self,
277277
description=playlist.description)
278278
playlist_id = spotify_playlist["id"]
279279
playlist_url = spotify_playlist["external_urls"]["spotify"]
280+
else:
281+
# existing playlist, clear it
282+
sp.playlist_replace_items(playlist_id, [])
283+
284+
# spotify API allows a max of 100 tracks in 1 request
285+
for chunk in chunked(spotify_track_ids, 100):
286+
sp.playlist_add_items(playlist_id, chunk)
280287

281-
result = sp.playlist_replace_items(playlist_id, spotify_track_ids)
282288
fixup_spotify_playlist(sp, playlist_id, mbid_spotify_index, spotify_mbid_index)
283289
submitted.append((playlist_url, playlist_id))
284290

troi/tests/test_playlist.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ def test_submit_to_spotify(self, mock_requests):
9393
}
9494
})
9595

96-
mock_requests.put(f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks", json={"snapshot_id": "foo"})
96+
mock_requests.put(f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks", json={"snapshot_id": "baz"})
97+
98+
mock_requests.post(f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks", json={"snapshot_id": "foo"})
9799

98100
mock_requests.get(f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks", json={
99101
"items": [
@@ -172,27 +174,24 @@ def test_submit_to_spotify(self, mock_requests):
172174
# we had 4 tracks with mbids, out of those no matching spotify id was found for the last track id.
173175
# so we submitted the first spotify track id for the remaining tracks preserving the order in which
174176
# occur in the original playlist
175-
self.assertEqual(history[2].json(), {
176-
"uris": [
177-
"spotify:track:47BBI51FKFwOMlIiX6m8ya",
178-
"spotify:track:7y9bltr6hV3CsbqXWgwVZv",
179-
"spotify:track:4hyVrAsoKKjxAvQjPRt0ai"
180-
]
181-
})
177+
self.assertEqual(history[2].json(), [
178+
"spotify:track:47BBI51FKFwOMlIiX6m8ya",
179+
"spotify:track:7y9bltr6hV3CsbqXWgwVZv",
180+
"spotify:track:4hyVrAsoKKjxAvQjPRt0ai"
181+
])
182182

183183
# history[3] is the request to retrieve tracks for checking whether the playlist has unplayable tracks
184184
# history[4] is the request to retrieve info for alternative spotify ids of unplayable tracks, only thing
185185
# to check about this is the query strings which the request matcher in the beginning of this method does
186+
# history[5] is the request to reset the playlist tracks
186187

187188
# we had found one track to be unplayable and replaced it with an alternative playable track. now updating the
188189
# playlist again so check the all the correct tracks and the new replaced tracks are sent and also in the
189190
# original order in which the tracks occur in the playlist
190-
self.assertEqual(history[5].json(), {
191-
"uris": [
192-
"spotify:track:47BBI51FKFwOMlIiX6m8ya",
193-
"spotify:track:4LWQfAhwP1Tf1wbzmT6NwW",
194-
"spotify:track:4hyVrAsoKKjxAvQjPRt0ai"
195-
]
196-
})
191+
self.assertEqual(history[6].json(), [
192+
"spotify:track:47BBI51FKFwOMlIiX6m8ya",
193+
"spotify:track:4LWQfAhwP1Tf1wbzmT6NwW",
194+
"spotify:track:4hyVrAsoKKjxAvQjPRt0ai"
195+
])
197196

198197
self.assertEqual(playlist.playlists[0].additional_metadata["external_urls"]["spotify"], playlist_url)

troi/tools/spotify_lookup.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import requests
44
import spotipy
5+
from more_itertools import chunked
56

67
SPOTIFY_IDS_LOOKUP_URL = "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json"
78

@@ -106,4 +107,9 @@ def fixup_spotify_playlist(sp: spotipy.Spotify, playlist_id: str, mbid_spotify_i
106107
all_items.sort(key=lambda x: x[0])
107108
# update all track ids the spotify playlist
108109
finalized_ids = [x[1] for x in all_items]
109-
result = sp.playlist_replace_items(playlist_id, finalized_ids)
110+
111+
# clear existing playlist
112+
sp.playlist_replace_items(playlist_id, [])
113+
# spotify API allows a max of 100 tracks in 1 request
114+
for chunk in chunked(finalized_ids, 100):
115+
sp.playlist_add_items(playlist_id, chunk)

0 commit comments

Comments
 (0)