mirror of
https://github.com/spotipy-dev/spotipy.git
synced 2026-06-19 09:13:53 +00:00
Update Playlist Endpoints and Add Following Endpoints (#531)
* Update playlist endpoints to modern format Deprecate user_playlist_* in favor of the following replacements: * user_playlist_change_details -> playlist_change_details * user_playlist_unfollow -> current_user_unfollow_playlist * user_playlist_add_tracks -> playlist_add_tracks * user_playlist_replace_tracks -> playlist_replace_tracks * user_playlist_reorder_tracks -> playlist_reorder_tracks * user_playlist_remove_all_occurrences_of_tracks -> playlist_remove_all_occurrences_of_tracks * user_playlist_remove_specific_occurrences_of_tracks -> playlist_remove_specific_occurrences_of_tracks * user_playlist_follow_playlist -> current_user_follow_playlist * user_playlist_is_following -> playlist_is_following * Add current_user_following_artists and current_user_following_users * Update tests and examples Resolve TODO in test_user_endpoints.py > SpotifyFollowApiTests.test_user_follows_and_unfollows_user Use modern playlist endpoints (no username required) in tests and examples. * Update changelog * Deprecate playlist_tracks in favor of playlist_items * Link deprecated functions to new functions and change tracks to items * Fix references to playlist_tracks * Change test_playlist_add_items as requested
This commit is contained in:
parent
cc5e234a28
commit
88cf75b778
21
CHANGELOG.md
21
CHANGELOG.md
@ -11,8 +11,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Support to search multiple markets at once.
|
- Support to search multiple markets at once.
|
||||||
- Support to search all available Spotify markets.
|
- Support to search all available Spotify markets.
|
||||||
- Allow for OAuth 2.0 authorization by instructing the user to open the URL in a browser instead of opening the browser.
|
- Support to test whether the current user is following certain
|
||||||
|
users or artists
|
||||||
|
- Proper replacements for all deprecated playlist endpoints
|
||||||
|
(See https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ and below)
|
||||||
|
- Allow for OAuth 2.0 authorization by instructing the user to open the URL in a browser instead of opening the browser.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- `user_playlist_change_details` in favor of `playlist_change_details`
|
||||||
|
- `user_playlist_unfollow` in favor of `current_user_unfollow_playlist`
|
||||||
|
- `user_playlist_add_tracks` in favor of `playlist_add_items`
|
||||||
|
- `user_playlist_replace_tracks` in favor of `playlist_replace_items`
|
||||||
|
- `user_playlist_reorder_tracks` in favor of `playlist_reorder_items`
|
||||||
|
- `user_playlist_remove_all_occurrences_of_tracks` in favor of
|
||||||
|
`playlist_remove_all_occurrences_of_items`
|
||||||
|
- `user_playlist_remove_specific_occurrences_of_tracks` in favor of
|
||||||
|
`playlist_remove_specific_occurrences_of_items`
|
||||||
|
- `user_playlist_follow_playlist` in favor of
|
||||||
|
`current_user_follow_playlist`
|
||||||
|
- `user_playlist_is_following` in favor of `playlist_is_following`
|
||||||
|
- `playlist_tracks` in favor of `playlist_items`
|
||||||
|
|
||||||
## [2.13.0] - 2020-06-25
|
## [2.13.0] - 2020-06-25
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,7 @@ def main():
|
|||||||
args = get_args()
|
args = get_args()
|
||||||
|
|
||||||
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
||||||
user_id = sp.me()['id']
|
sp.playlist_add_items(args.playlist, args.tids)
|
||||||
sp.user_playlist_add_tracks(user_id, args.playlist, args.tids)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@ -35,8 +35,7 @@ def get_args():
|
|||||||
def main():
|
def main():
|
||||||
args = get_args()
|
args = get_args()
|
||||||
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
||||||
sp.user_playlist_change_details(
|
sp.playlist_change_details(
|
||||||
args.username,
|
|
||||||
args.playlist,
|
args.playlist,
|
||||||
name=args.name,
|
name=args.name,
|
||||||
public=args.public or args.private,
|
public=args.public or args.private,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials())
|
|||||||
|
|
||||||
# load the first 100 songs
|
# load the first 100 songs
|
||||||
tracks = []
|
tracks = []
|
||||||
result = sp.playlist_tracks(PlaylistExample)
|
result = sp.playlist_items(PlaylistExample, additional_types=['track'])
|
||||||
tracks.extend(result['items'])
|
tracks.extend(result['items'])
|
||||||
|
|
||||||
# if playlist is larger than 100 songs, continue loading it until end
|
# if playlist is larger than 100 songs, continue loading it until end
|
||||||
|
|||||||
@ -8,9 +8,10 @@ pl_id = 'spotify:playlist:5RIbzhG2QqdkaP24iXLnZX'
|
|||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
response = sp.playlist_tracks(pl_id,
|
response = sp.playlist_items(pl_id,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
fields='items.track.id,total')
|
fields='items.track.id,total',
|
||||||
|
additional_types=['track'])
|
||||||
pprint(response['items'])
|
pprint(response['items'])
|
||||||
offset = offset + len(response['items'])
|
offset = offset + len(response['items'])
|
||||||
print(offset, "/", response['total'])
|
print(offset, "/", response['total'])
|
||||||
|
|||||||
@ -22,8 +22,6 @@ else:
|
|||||||
scope = 'playlist-modify-public'
|
scope = 'playlist-modify-public'
|
||||||
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
||||||
|
|
||||||
user_id = sp.me()['id']
|
results = sp.playlist_remove_specific_occurrences_of_items(
|
||||||
|
playlist_id, track_ids)
|
||||||
results = sp.user_playlist_remove_specific_occurrences_of_tracks(
|
|
||||||
user_id, playlist_id, track_ids)
|
|
||||||
pprint.pprint(results)
|
pprint.pprint(results)
|
||||||
|
|||||||
@ -17,8 +17,6 @@ scope = 'playlist-modify-public'
|
|||||||
|
|
||||||
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
||||||
|
|
||||||
user_id = sp.me()['id']
|
results = sp.playlist_remove_all_occurrences_of_items(
|
||||||
|
playlist_id, track_ids)
|
||||||
results = sp.user_playlist_remove_all_occurrences_of_tracks(
|
|
||||||
user_id, playlist_id, track_ids)
|
|
||||||
pprint.pprint(results)
|
pprint.pprint(results)
|
||||||
|
|||||||
@ -16,8 +16,6 @@ else:
|
|||||||
scope = 'playlist-modify-public'
|
scope = 'playlist-modify-public'
|
||||||
|
|
||||||
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
|
||||||
user_id = sp.me()['id']
|
|
||||||
|
|
||||||
results = sp.user_playlist_replace_tracks(user_id, playlist_id, track_ids)
|
results = sp.playlist_replace_items(playlist_id, track_ids)
|
||||||
pprint.pprint(results)
|
pprint.pprint(results)
|
||||||
|
|
||||||
|
|||||||
@ -607,6 +607,34 @@ class Spotify(object):
|
|||||||
- additional_types - list of item types to return.
|
- additional_types - list of item types to return.
|
||||||
valid types are: track and episode
|
valid types are: track and episode
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"You should use `playlist_items(playlist_id, ...,"
|
||||||
|
"additional_types=('track',))` instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
return self.playlist_items(playlist_id, fields, limit, offset,
|
||||||
|
market, additional_types)
|
||||||
|
|
||||||
|
def playlist_items(
|
||||||
|
self,
|
||||||
|
playlist_id,
|
||||||
|
fields=None,
|
||||||
|
limit=100,
|
||||||
|
offset=0,
|
||||||
|
market=None,
|
||||||
|
additional_types=("track", "episode")
|
||||||
|
):
|
||||||
|
""" Get full details of the tracks and episodes of a playlist.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- fields - which fields to return
|
||||||
|
- limit - the maximum number of tracks to return
|
||||||
|
- offset - the index of the first track to return
|
||||||
|
- market - an ISO 3166-1 alpha-2 country code.
|
||||||
|
- additional_types - list of item types to return.
|
||||||
|
valid types are: track and episode
|
||||||
|
"""
|
||||||
plid = self._get_id("playlist", playlist_id)
|
plid = self._get_id("playlist", playlist_id)
|
||||||
return self._get(
|
return self._get(
|
||||||
"playlists/%s/tracks" % (plid),
|
"playlists/%s/tracks" % (plid),
|
||||||
@ -724,6 +752,10 @@ class Spotify(object):
|
|||||||
collaborative=None,
|
collaborative=None,
|
||||||
description=None,
|
description=None,
|
||||||
):
|
):
|
||||||
|
warnings.warn(
|
||||||
|
"You should use `playlist_change_details(playlist_id, ...)` instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
""" Changes a playlist's name and/or public/private state
|
""" Changes a playlist's name and/or public/private state
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -735,18 +767,8 @@ class Spotify(object):
|
|||||||
- description - optional description of the playlist
|
- description - optional description of the playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = {}
|
return self.playlist_change_details(playlist_id, name, public,
|
||||||
if isinstance(name, six.string_types):
|
collaborative, description)
|
||||||
data["name"] = name
|
|
||||||
if isinstance(public, bool):
|
|
||||||
data["public"] = public
|
|
||||||
if isinstance(collaborative, bool):
|
|
||||||
data["collaborative"] = collaborative
|
|
||||||
if isinstance(description, six.string_types):
|
|
||||||
data["description"] = description
|
|
||||||
return self._put(
|
|
||||||
"users/%s/playlists/%s" % (user, playlist_id), payload=data
|
|
||||||
)
|
|
||||||
|
|
||||||
def user_playlist_unfollow(self, user, playlist_id):
|
def user_playlist_unfollow(self, user, playlist_id):
|
||||||
""" Unfollows (deletes) a playlist for a user
|
""" Unfollows (deletes) a playlist for a user
|
||||||
@ -755,13 +777,19 @@ class Spotify(object):
|
|||||||
- user - the id of the user
|
- user - the id of the user
|
||||||
- name - the name of the playlist
|
- name - the name of the playlist
|
||||||
"""
|
"""
|
||||||
return self._delete(
|
warnings.warn(
|
||||||
"users/%s/playlists/%s/followers" % (user, playlist_id)
|
"You should use `current_user_unfollow_playlist(playlist_id)` instead",
|
||||||
|
DeprecationWarning,
|
||||||
)
|
)
|
||||||
|
return self.current_user_unfollow_playlist(playlist_id)
|
||||||
|
|
||||||
def user_playlist_add_tracks(
|
def user_playlist_add_tracks(
|
||||||
self, user, playlist_id, tracks, position=None
|
self, user, playlist_id, tracks, position=None
|
||||||
):
|
):
|
||||||
|
warnings.warn(
|
||||||
|
"You should use `playlist_add_items(playlist_id, tracks)` instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
""" Adds tracks to a playlist
|
""" Adds tracks to a playlist
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -770,13 +798,7 @@ class Spotify(object):
|
|||||||
- tracks - a list of track URIs, URLs or IDs
|
- tracks - a list of track URIs, URLs or IDs
|
||||||
- position - the position to add the tracks
|
- position - the position to add the tracks
|
||||||
"""
|
"""
|
||||||
plid = self._get_id("playlist", playlist_id)
|
return self.playlist_add_items(playlist_id, tracks, position)
|
||||||
ftracks = [self._get_uri("track", tid) for tid in tracks]
|
|
||||||
return self._post(
|
|
||||||
"users/%s/playlists/%s/tracks" % (user, plid),
|
|
||||||
payload=ftracks,
|
|
||||||
position=position,
|
|
||||||
)
|
|
||||||
|
|
||||||
def user_playlist_replace_tracks(self, user, playlist_id, tracks):
|
def user_playlist_replace_tracks(self, user, playlist_id, tracks):
|
||||||
""" Replace all tracks in a playlist
|
""" Replace all tracks in a playlist
|
||||||
@ -786,12 +808,11 @@ class Spotify(object):
|
|||||||
- playlist_id - the id of the playlist
|
- playlist_id - the id of the playlist
|
||||||
- tracks - the list of track ids to add to the playlist
|
- tracks - the list of track ids to add to the playlist
|
||||||
"""
|
"""
|
||||||
plid = self._get_id("playlist", playlist_id)
|
warnings.warn(
|
||||||
ftracks = [self._get_uri("track", tid) for tid in tracks]
|
"You should use `playlist_replace_items(playlist_id, tracks)` instead",
|
||||||
payload = {"uris": ftracks}
|
DeprecationWarning,
|
||||||
return self._put(
|
|
||||||
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
|
|
||||||
)
|
)
|
||||||
|
return self.playlist_replace_items(playlist_id, tracks)
|
||||||
|
|
||||||
def user_playlist_reorder_tracks(
|
def user_playlist_reorder_tracks(
|
||||||
self,
|
self,
|
||||||
@ -814,17 +835,13 @@ class Spotify(object):
|
|||||||
inserted
|
inserted
|
||||||
- snapshot_id - optional playlist's snapshot ID
|
- snapshot_id - optional playlist's snapshot ID
|
||||||
"""
|
"""
|
||||||
plid = self._get_id("playlist", playlist_id)
|
warnings.warn(
|
||||||
payload = {
|
"You should use `playlist_reorder_items(playlist_id, ...)` instead",
|
||||||
"range_start": range_start,
|
DeprecationWarning,
|
||||||
"range_length": range_length,
|
|
||||||
"insert_before": insert_before,
|
|
||||||
}
|
|
||||||
if snapshot_id:
|
|
||||||
payload["snapshot_id"] = snapshot_id
|
|
||||||
return self._put(
|
|
||||||
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
|
|
||||||
)
|
)
|
||||||
|
return self.playlist_reorder_items(playlist_id, range_start,
|
||||||
|
range_length, insert_before,
|
||||||
|
snapshot_id)
|
||||||
|
|
||||||
def user_playlist_remove_all_occurrences_of_tracks(
|
def user_playlist_remove_all_occurrences_of_tracks(
|
||||||
self, user, playlist_id, tracks, snapshot_id=None
|
self, user, playlist_id, tracks, snapshot_id=None
|
||||||
@ -838,15 +855,14 @@ class Spotify(object):
|
|||||||
- snapshot_id - optional id of the playlist snapshot
|
- snapshot_id - optional id of the playlist snapshot
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
plid = self._get_id("playlist", playlist_id)
|
"You should use `playlist_remove_all_occurrences_of_items"
|
||||||
ftracks = [self._get_uri("track", tid) for tid in tracks]
|
"(playlist_id, tracks)` instead",
|
||||||
payload = {"tracks": [{"uri": track} for track in ftracks]}
|
DeprecationWarning,
|
||||||
if snapshot_id:
|
|
||||||
payload["snapshot_id"] = snapshot_id
|
|
||||||
return self._delete(
|
|
||||||
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
|
|
||||||
)
|
)
|
||||||
|
return self.playlist_remove_all_occurrences_of_items(playlist_id,
|
||||||
|
tracks,
|
||||||
|
snapshot_id)
|
||||||
|
|
||||||
def user_playlist_remove_specific_occurrences_of_tracks(
|
def user_playlist_remove_specific_occurrences_of_tracks(
|
||||||
self, user, playlist_id, tracks, snapshot_id=None
|
self, user, playlist_id, tracks, snapshot_id=None
|
||||||
@ -863,7 +879,11 @@ class Spotify(object):
|
|||||||
{ "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
|
{ "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
|
||||||
- snapshot_id - optional id of the playlist snapshot
|
- snapshot_id - optional id of the playlist snapshot
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"You should use `playlist_remove_specific_occurrences_of_items"
|
||||||
|
"(playlist_id, tracks)` instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
plid = self._get_id("playlist", playlist_id)
|
plid = self._get_id("playlist", playlist_id)
|
||||||
ftracks = []
|
ftracks = []
|
||||||
for tr in tracks:
|
for tr in tracks:
|
||||||
@ -889,11 +909,11 @@ class Spotify(object):
|
|||||||
- playlist_id - the id of the playlist
|
- playlist_id - the id of the playlist
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self._put(
|
warnings.warn(
|
||||||
"users/{}/playlists/{}/followers".format(
|
"You should use `current_user_follow_playlist(playlist_id)` instead",
|
||||||
playlist_owner_id, playlist_id
|
DeprecationWarning,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
return self.current_user_follow_playlist(playlist_id)
|
||||||
|
|
||||||
def user_playlist_is_following(
|
def user_playlist_is_following(
|
||||||
self, playlist_owner_id, playlist_id, user_ids
|
self, playlist_owner_id, playlist_id, user_ids
|
||||||
@ -908,9 +928,196 @@ class Spotify(object):
|
|||||||
if they follow the playlist. Maximum: 5 ids.
|
if they follow the playlist. Maximum: 5 ids.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
endpoint = "users/{}/playlists/{}/followers/contains?ids={}"
|
warnings.warn(
|
||||||
|
"You should use `playlist_is_following(playlist_id, user_ids)` instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
return self.playlist_is_following(playlist_id, user_ids)
|
||||||
|
|
||||||
|
def playlist_change_details(
|
||||||
|
self,
|
||||||
|
playlist_id,
|
||||||
|
name=None,
|
||||||
|
public=None,
|
||||||
|
collaborative=None,
|
||||||
|
description=None,
|
||||||
|
):
|
||||||
|
""" Changes a playlist's name and/or public/private state
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- name - optional name of the playlist
|
||||||
|
- public - optional is the playlist public
|
||||||
|
- collaborative - optional is the playlist collaborative
|
||||||
|
- description - optional description of the playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
if isinstance(name, six.string_types):
|
||||||
|
data["name"] = name
|
||||||
|
if isinstance(public, bool):
|
||||||
|
data["public"] = public
|
||||||
|
if isinstance(collaborative, bool):
|
||||||
|
data["collaborative"] = collaborative
|
||||||
|
if isinstance(description, six.string_types):
|
||||||
|
data["description"] = description
|
||||||
|
return self._put(
|
||||||
|
"playlists/%s" % (playlist_id), payload=data
|
||||||
|
)
|
||||||
|
|
||||||
|
def current_user_unfollow_playlist(self, playlist_id):
|
||||||
|
""" Unfollows (deletes) a playlist for the current authenticated
|
||||||
|
user
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- name - the name of the playlist
|
||||||
|
"""
|
||||||
|
return self._delete(
|
||||||
|
"playlists/%s/followers" % (playlist_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_add_items(
|
||||||
|
self, playlist_id, items, position=None
|
||||||
|
):
|
||||||
|
""" Adds tracks/episodes to a playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- items - a list of track/episode URIs, URLs or IDs
|
||||||
|
- position - the position to add the tracks
|
||||||
|
"""
|
||||||
|
plid = self._get_id("playlist", playlist_id)
|
||||||
|
ftracks = [self._get_uri("track", tid) for tid in items]
|
||||||
|
return self._post(
|
||||||
|
"playlists/%s/tracks" % (plid),
|
||||||
|
payload=ftracks,
|
||||||
|
position=position,
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_replace_items(self, playlist_id, items):
|
||||||
|
""" Replace all tracks/episodes in a playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- items - list of track/episode ids to comprise playlist
|
||||||
|
"""
|
||||||
|
plid = self._get_id("playlist", playlist_id)
|
||||||
|
ftracks = [self._get_uri("track", tid) for tid in items]
|
||||||
|
payload = {"uris": ftracks}
|
||||||
|
return self._put(
|
||||||
|
"playlists/%s/tracks" % (plid), payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_reorder_items(
|
||||||
|
self,
|
||||||
|
playlist_id,
|
||||||
|
range_start,
|
||||||
|
insert_before,
|
||||||
|
range_length=1,
|
||||||
|
snapshot_id=None,
|
||||||
|
):
|
||||||
|
""" Reorder tracks in a playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- range_start - the position of the first track to be reordered
|
||||||
|
- range_length - optional the number of tracks to be reordered
|
||||||
|
(default: 1)
|
||||||
|
- insert_before - the position where the tracks should be
|
||||||
|
inserted
|
||||||
|
- snapshot_id - optional playlist's snapshot ID
|
||||||
|
"""
|
||||||
|
plid = self._get_id("playlist", playlist_id)
|
||||||
|
payload = {
|
||||||
|
"range_start": range_start,
|
||||||
|
"range_length": range_length,
|
||||||
|
"insert_before": insert_before,
|
||||||
|
}
|
||||||
|
if snapshot_id:
|
||||||
|
payload["snapshot_id"] = snapshot_id
|
||||||
|
return self._put(
|
||||||
|
"playlists/%s/tracks" % (plid), payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_remove_all_occurrences_of_items(
|
||||||
|
self, playlist_id, items, snapshot_id=None
|
||||||
|
):
|
||||||
|
""" Removes all occurrences of the given tracks from the given playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- items - list of track/episode ids to remove from the playlist
|
||||||
|
- snapshot_id - optional id of the playlist snapshot
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
plid = self._get_id("playlist", playlist_id)
|
||||||
|
ftracks = [self._get_uri("track", tid) for tid in items]
|
||||||
|
payload = {"tracks": [{"uri": track} for track in ftracks]}
|
||||||
|
if snapshot_id:
|
||||||
|
payload["snapshot_id"] = snapshot_id
|
||||||
|
return self._delete(
|
||||||
|
"playlists/%s/tracks" % (plid), payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_remove_specific_occurrences_of_items(
|
||||||
|
self, playlist_id, items, snapshot_id=None
|
||||||
|
):
|
||||||
|
""" Removes all occurrences of the given tracks from the given playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- items - an array of objects containing Spotify URIs of the
|
||||||
|
tracks/episodes to remove with their current positions in
|
||||||
|
the playlist. For example:
|
||||||
|
[ { "uri":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] },
|
||||||
|
{ "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
|
||||||
|
- snapshot_id - optional id of the playlist snapshot
|
||||||
|
"""
|
||||||
|
|
||||||
|
plid = self._get_id("playlist", playlist_id)
|
||||||
|
ftracks = []
|
||||||
|
for tr in items:
|
||||||
|
ftracks.append(
|
||||||
|
{
|
||||||
|
"uri": self._get_uri("track", tr["uri"]),
|
||||||
|
"positions": tr["positions"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
payload = {"tracks": ftracks}
|
||||||
|
if snapshot_id:
|
||||||
|
payload["snapshot_id"] = snapshot_id
|
||||||
|
return self._delete(
|
||||||
|
"playlists/%s/tracks" % (plid), payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
def current_user_follow_playlist(self, playlist_id):
|
||||||
|
"""
|
||||||
|
Add the current authenticated user as a follower of a playlist.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._put(
|
||||||
|
"playlists/{}/followers".format(playlist_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
def playlist_is_following(
|
||||||
|
self, playlist_id, user_ids
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Check to see if the given users are following the given playlist
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- playlist_id - the id of the playlist
|
||||||
|
- user_ids - the ids of the users that you want to check to see
|
||||||
|
if they follow the playlist. Maximum: 5 ids.
|
||||||
|
|
||||||
|
"""
|
||||||
|
endpoint = "playlists/{}/followers/contains?ids={}"
|
||||||
return self._get(
|
return self._get(
|
||||||
endpoint.format(playlist_owner_id, playlist_id, ",".join(user_ids))
|
endpoint.format(playlist_id, ",".join(user_ids))
|
||||||
)
|
)
|
||||||
|
|
||||||
def me(self):
|
def me(self):
|
||||||
@ -954,6 +1161,36 @@ class Spotify(object):
|
|||||||
"me/following", type="artist", limit=limit, after=after
|
"me/following", type="artist", limit=limit, after=after
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def current_user_following_artists(self, ids=None):
|
||||||
|
""" Check if the current user is following certain artists
|
||||||
|
|
||||||
|
Returns list of booleans respective to ids
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- ids - a list of artist URIs, URLs or IDs
|
||||||
|
"""
|
||||||
|
idlist = []
|
||||||
|
if ids is not None:
|
||||||
|
idlist = [self._get_id("artist", i) for i in ids]
|
||||||
|
return self._get(
|
||||||
|
"me/following/contains", ids=",".join(idlist), type="artist"
|
||||||
|
)
|
||||||
|
|
||||||
|
def current_user_following_users(self, ids=None):
|
||||||
|
""" Check if the current user is following certain artists
|
||||||
|
|
||||||
|
Returns list of booleans respective to ids
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- ids - a list of user URIs, URLs or IDs
|
||||||
|
"""
|
||||||
|
idlist = []
|
||||||
|
if ids is not None:
|
||||||
|
idlist = [self._get_id("user", i) for i in ids]
|
||||||
|
return self._get(
|
||||||
|
"me/following/contains", ids=",".join(idlist), type="user"
|
||||||
|
)
|
||||||
|
|
||||||
def current_user_saved_tracks_delete(self, tracks=None):
|
def current_user_saved_tracks_delete(self, tracks=None):
|
||||||
""" Remove one or more tracks from the current user's
|
""" Remove one or more tracks from the current user's
|
||||||
"Your Music" library.
|
"Your Music" library.
|
||||||
|
|||||||
@ -53,15 +53,14 @@ class SpotipyPlaylistApiTest(unittest.TestCase):
|
|||||||
self.assertTrue('items' in playlists)
|
self.assertTrue('items' in playlists)
|
||||||
self.assertGreaterEqual(len(playlists['items']), 1)
|
self.assertGreaterEqual(len(playlists['items']), 1)
|
||||||
|
|
||||||
def test_user_playlist_tracks(self):
|
def test_playlist_items(self):
|
||||||
playlists = self.spotify.user_playlists(self.username, limit=5)
|
playlists = self.spotify.user_playlists(self.username, limit=5)
|
||||||
self.assertTrue('items' in playlists)
|
self.assertTrue('items' in playlists)
|
||||||
for playlist in playlists['items']:
|
for playlist in playlists['items']:
|
||||||
if playlist['uri'] != self.new_playlist_uri:
|
if playlist['uri'] != self.new_playlist_uri:
|
||||||
continue
|
continue
|
||||||
user = playlist['owner']['id']
|
|
||||||
pid = playlist['id']
|
pid = playlist['id']
|
||||||
results = self.spotify.user_playlist_tracks(user, pid)
|
results = self.spotify.playlist_items(pid)
|
||||||
self.assertEqual(len(results['items']), 0)
|
self.assertEqual(len(results['items']), 0)
|
||||||
|
|
||||||
def test_current_user_playlists(self):
|
def test_current_user_playlists(self):
|
||||||
@ -70,65 +69,60 @@ class SpotipyPlaylistApiTest(unittest.TestCase):
|
|||||||
self.assertGreaterEqual(len(playlists['items']), 1)
|
self.assertGreaterEqual(len(playlists['items']), 1)
|
||||||
self.assertLessEqual(len(playlists['items']), 10)
|
self.assertLessEqual(len(playlists['items']), 10)
|
||||||
|
|
||||||
def test_user_playlist_follow(self):
|
def test_current_user_follow_playlist(self):
|
||||||
user_to_follow = 'plamere'
|
playlist_to_follow_id = '4erXB04MxwRAVqcUEpu30O'
|
||||||
user_to_follow_id = '4erXB04MxwRAVqcUEpu30O'
|
self.spotify.current_user_follow_playlist(playlist_to_follow_id)
|
||||||
self.spotify.user_playlist_follow_playlist(
|
follows = self.spotify.playlist_is_following(
|
||||||
user_to_follow, user_to_follow_id)
|
playlist_to_follow_id, [self.username])
|
||||||
follows = self.spotify.user_playlist_is_following(
|
|
||||||
user_to_follow, user_to_follow_id, [self.username])
|
|
||||||
|
|
||||||
self.assertTrue(len(follows) == 1, 'proper follows length')
|
self.assertTrue(len(follows) == 1, 'proper follows length')
|
||||||
self.assertTrue(follows[0], 'is following')
|
self.assertTrue(follows[0], 'is following')
|
||||||
self.spotify.user_playlist_unfollow(
|
self.spotify.current_user_unfollow_playlist(playlist_to_follow_id)
|
||||||
user_to_follow, user_to_follow_id)
|
|
||||||
|
|
||||||
follows = self.spotify.user_playlist_is_following(
|
follows = self.spotify.playlist_is_following(
|
||||||
user_to_follow, user_to_follow_id, [self.username])
|
playlist_to_follow_id, [self.username])
|
||||||
self.assertTrue(len(follows) == 1, 'proper follows length')
|
self.assertTrue(len(follows) == 1, 'proper follows length')
|
||||||
self.assertFalse(follows[0], 'is no longer following')
|
self.assertFalse(follows[0], 'is no longer following')
|
||||||
|
|
||||||
def test_user_playlist_replace_tracks(self):
|
def test_playlist_replace_items(self):
|
||||||
# add tracks to playlist
|
# add tracks to playlist
|
||||||
self.spotify.user_playlist_add_tracks(
|
self.spotify.playlist_add_items(
|
||||||
self.username, self.new_playlist['id'], self.four_tracks)
|
self.new_playlist['id'], self.four_tracks)
|
||||||
playlist = self.spotify.user_playlist(self.username, self.new_playlist['id'])
|
playlist = self.spotify.playlist(self.new_playlist['id'])
|
||||||
self.assertEqual(playlist['tracks']['total'], 4)
|
self.assertEqual(playlist['tracks']['total'], 4)
|
||||||
self.assertEqual(len(playlist['tracks']['items']), 4)
|
self.assertEqual(len(playlist['tracks']['items']), 4)
|
||||||
|
|
||||||
# replace with 3 other tracks
|
# replace with 3 other tracks
|
||||||
self.spotify.user_playlist_replace_tracks(self.username,
|
self.spotify.playlist_replace_items(self.new_playlist['id'],
|
||||||
self.new_playlist['id'],
|
|
||||||
self.other_tracks)
|
self.other_tracks)
|
||||||
playlist = self.spotify.user_playlist(self.username,
|
playlist = self.spotify.playlist(self.new_playlist['id'])
|
||||||
self.new_playlist['id'])
|
|
||||||
self.assertEqual(playlist['tracks']['total'], 3)
|
self.assertEqual(playlist['tracks']['total'], 3)
|
||||||
self.assertEqual(len(playlist['tracks']['items']), 3)
|
self.assertEqual(len(playlist['tracks']['items']), 3)
|
||||||
|
|
||||||
self.spotify.user_playlist_remove_all_occurrences_of_tracks(
|
self.spotify.playlist_remove_all_occurrences_of_items(
|
||||||
self.username, playlist['id'], self.other_tracks)
|
playlist['id'], self.other_tracks)
|
||||||
playlist = self.spotify.user_playlist(self.username, self.new_playlist['id'])
|
playlist = self.spotify.playlist(self.new_playlist['id'])
|
||||||
self.assertEqual(playlist["tracks"]["total"], 0)
|
self.assertEqual(playlist["tracks"]["total"], 0)
|
||||||
|
|
||||||
def test_get_playlist_by_id(self):
|
def test_get_playlist_by_id(self):
|
||||||
pl = self.spotify.playlist(self.new_playlist['id'])
|
pl = self.spotify.playlist(self.new_playlist['id'])
|
||||||
self.assertEqual(pl["tracks"]["total"], 0)
|
self.assertEqual(pl["tracks"]["total"], 0)
|
||||||
|
|
||||||
def test_playlist_add_tracks(self):
|
def test_playlist_add_items(self):
|
||||||
# add tracks to playlist
|
# add tracks to playlist
|
||||||
self.spotify.user_playlist_add_tracks(
|
self.spotify.playlist_add_items(
|
||||||
self.username, self.new_playlist['id'], self.other_tracks)
|
self.new_playlist['id'], self.other_tracks)
|
||||||
playlist = self.spotify.user_playlist(self.username, self.new_playlist['id'])
|
playlist = self.spotify.playlist_items(self.new_playlist['id'])
|
||||||
self.assertEqual(playlist['tracks']['total'], 3)
|
self.assertEqual(playlist['total'], 3)
|
||||||
self.assertEqual(len(playlist['tracks']['items']), 3)
|
self.assertEqual(len(playlist['items']), 3)
|
||||||
|
|
||||||
pl = self.spotify.playlist_tracks(self.new_playlist['id'], limit=2)
|
pl = self.spotify.playlist_items(self.new_playlist['id'], limit=2)
|
||||||
self.assertEqual(len(pl["items"]), 2)
|
self.assertEqual(len(pl["items"]), 2)
|
||||||
|
|
||||||
self.spotify.user_playlist_remove_all_occurrences_of_tracks(
|
self.spotify.playlist_remove_all_occurrences_of_items(
|
||||||
self.username, playlist['id'], self.other_tracks)
|
self.new_playlist['id'], self.other_tracks)
|
||||||
playlist = self.spotify.user_playlist(self.username, self.new_playlist['id'])
|
playlist = self.spotify.playlist_items(self.new_playlist['id'])
|
||||||
self.assertEqual(playlist["tracks"]["total"], 0)
|
self.assertEqual(playlist["total"], 0)
|
||||||
|
|
||||||
def test_playlist_cover_image(self):
|
def test_playlist_cover_image(self):
|
||||||
# Upload random dog image
|
# Upload random dog image
|
||||||
@ -332,23 +326,24 @@ class SpotipyFollowApiTests(unittest.TestCase):
|
|||||||
# Follow 2 more artists
|
# Follow 2 more artists
|
||||||
artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"]
|
artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"]
|
||||||
self.spotify.user_follow_artists(artists)
|
self.spotify.user_follow_artists(artists)
|
||||||
res = self.spotify.current_user_followed_artists()
|
self.assertTrue(all(self.spotify.current_user_following_artists(artists)))
|
||||||
self.assertEqual(res['artists']['total'], current_user_followed_artists + len(artists))
|
|
||||||
|
|
||||||
# Unfollow these 2 artists
|
# Unfollow these 2 artists
|
||||||
self.spotify.user_unfollow_artists(artists)
|
self.spotify.user_unfollow_artists(artists)
|
||||||
|
self.assertFalse(any(self.spotify.current_user_following_artists(artists)))
|
||||||
res = self.spotify.current_user_followed_artists()
|
res = self.spotify.current_user_followed_artists()
|
||||||
self.assertEqual(res['artists']['total'], current_user_followed_artists)
|
self.assertEqual(res['artists']['total'], current_user_followed_artists)
|
||||||
|
|
||||||
def test_user_follows_and_unfollows_user(self):
|
def test_user_follows_and_unfollows_user(self):
|
||||||
# TODO improve after implementing `me/following/contains`
|
|
||||||
users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"]
|
users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"]
|
||||||
|
|
||||||
# Follow 2 more users
|
# Follow 2 more users
|
||||||
self.spotify.user_follow_users(users)
|
self.spotify.user_follow_users(users)
|
||||||
|
self.assertTrue(all(self.spotify.current_user_following_users(users)))
|
||||||
|
|
||||||
# Unfollow these 2 users
|
# Unfollow these 2 users
|
||||||
self.spotify.user_unfollow_users(users)
|
self.spotify.user_unfollow_users(users)
|
||||||
|
self.assertFalse(any(self.spotify.current_user_following_users(users)))
|
||||||
|
|
||||||
|
|
||||||
class SpotipyPlayerApiTests(unittest.TestCase):
|
class SpotipyPlayerApiTests(unittest.TestCase):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user