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:
IdmFoundInHim authored and GitHub committed 2020-07-11 11:23:18 +01:00
1 parent cc5e234a28
commit 88cf75b778
10 files changed
+361 -117

No files matched your search

+288 -51
View File
@@ -607,6 +607,34 @@ class Spotify(object):
- additional_types - list of item types to return.
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)
return self._get(
"playlists/%s/tracks" % (plid),
@@ -724,6 +752,10 @@ class Spotify(object):
collaborative=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
Parameters:
@@ -735,18 +767,8 @@ class Spotify(object):
- 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(
"users/%s/playlists/%s" % (user, playlist_id), payload=data
)
return self.playlist_change_details(playlist_id, name, public,
collaborative, description)
def user_playlist_unfollow(self, user, playlist_id):
""" Unfollows (deletes) a playlist for a user
@@ -755,13 +777,19 @@ class Spotify(object):
- user - the id of the user
- name - the name of the playlist
"""
return self._delete(
"users/%s/playlists/%s/followers" % (user, playlist_id)
warnings.warn(
"You should use `current_user_unfollow_playlist(playlist_id)` instead",
DeprecationWarning,
)
return self.current_user_unfollow_playlist(playlist_id)
def user_playlist_add_tracks(
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
Parameters:
@@ -770,13 +798,7 @@ class Spotify(object):
- tracks - a list of track 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 tracks]
return self._post(
"users/%s/playlists/%s/tracks" % (user, plid),
payload=ftracks,
position=position,
)
return self.playlist_add_items(playlist_id, tracks, position)
def user_playlist_replace_tracks(self, user, playlist_id, tracks):
""" Replace all tracks in a playlist
@@ -786,12 +808,11 @@ class Spotify(object):
- playlist_id - the id of the playlist
- tracks - the list of track ids to add to the playlist
"""
plid = self._get_id("playlist", playlist_id)
ftracks = [self._get_uri("track", tid) for tid in tracks]
payload = {"uris": ftracks}
return self._put(
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
warnings.warn(
"You should use `playlist_replace_items(playlist_id, tracks)` instead",
DeprecationWarning,
)
return self.playlist_replace_items(playlist_id, tracks)
def user_playlist_reorder_tracks(
self,
@@ -814,17 +835,13 @@ class Spotify(object):
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(
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
warnings.warn(
"You should use `playlist_reorder_items(playlist_id, ...)` instead",
DeprecationWarning,
)
return self.playlist_reorder_items(playlist_id, range_start,
range_length, insert_before,
snapshot_id)
def user_playlist_remove_all_occurrences_of_tracks(
self, user, playlist_id, tracks, snapshot_id=None
@@ -838,15 +855,14 @@ class Spotify(object):
- snapshot_id - optional id of the playlist snapshot
"""
plid = self._get_id("playlist", playlist_id)
ftracks = [self._get_uri("track", tid) for tid in tracks]
payload = {"tracks": [{"uri": track} for track in ftracks]}
if snapshot_id:
payload["snapshot_id"] = snapshot_id
return self._delete(
"users/%s/playlists/%s/tracks" % (user, plid), payload=payload
warnings.warn(
"You should use `playlist_remove_all_occurrences_of_items"
"(playlist_id, tracks)` instead",
DeprecationWarning,
)
return self.playlist_remove_all_occurrences_of_items(playlist_id,
tracks,
snapshot_id)
def user_playlist_remove_specific_occurrences_of_tracks(
self, user, playlist_id, tracks, snapshot_id=None
@@ -863,7 +879,11 @@ class Spotify(object):
{ "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
- 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)
ftracks = []
for tr in tracks:
@@ -889,11 +909,11 @@ class Spotify(object):
- playlist_id - the id of the playlist
"""
return self._put(
"users/{}/playlists/{}/followers".format(
playlist_owner_id, playlist_id
)
warnings.warn(
"You should use `current_user_follow_playlist(playlist_id)` instead",
DeprecationWarning,
)
return self.current_user_follow_playlist(playlist_id)
def user_playlist_is_following(
self, playlist_owner_id, playlist_id, user_ids
@@ -908,9 +928,196 @@ class Spotify(object):
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(
endpoint.format(playlist_owner_id, playlist_id, ",".join(user_ids))
endpoint.format(playlist_id, ",".join(user_ids))
)
def me(self):
@@ -954,6 +1161,36 @@ class Spotify(object):
"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):
""" Remove one or more tracks from the current user's
"Your Music" library.