diff --git a/CHANGELOG.md b/CHANGELOG.md index bbd8107..1cb4f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + + - Support for `playlist_tracks` + +### Changed + + - `user_playlist_tracks` doesn't require a user anymore + +### Deprecated + + - Deprecated `user_playlist` and `user_playlist_tracks` + ## [2.6.3] - 2020-01-16 ### Fixed diff --git a/docs/index.rst b/docs/index.rst index f652dd2..0f3ba87 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -271,7 +271,7 @@ Shows the contents of every playlist owned by a user:: print() print(playlist['name']) print (' total tracks', playlist['tracks']['total']) - results = sp.user_playlist(username, playlist['id'], + results = sp.playlist(playlist['id'], fields="tracks,next") tracks = results['tracks'] show_tracks(tracks) diff --git a/examples/read_a_playlist.py b/examples/read_a_playlist.py index e38676f..06cad00 100644 --- a/examples/read_a_playlist.py +++ b/examples/read_a_playlist.py @@ -5,9 +5,6 @@ import json client_credentials_manager = SpotifyClientCredentials() sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) -uri = 'spotify:user:spotifycharts:playlist:37i9dQZEVXbJiZcmkrIHGU' -username = uri.split(':')[2] -playlist_id = uri.split(':')[4] - -results = sp.user_playlist(username, playlist_id) +playlist_id = 'spotify:user:spotifycharts:playlist:37i9dQZEVXbJiZcmkrIHGU' +results = sp.playlist(playlist_id) print(json.dumps(results, indent=4)) diff --git a/examples/user_playlists_contents.py b/examples/user_playlists_contents.py index b6cb7d2..a36006a 100644 --- a/examples/user_playlists_contents.py +++ b/examples/user_playlists_contents.py @@ -31,8 +31,7 @@ if __name__ == '__main__': print() print(playlist['name']) print(' total tracks', playlist['tracks']['total']) - results = sp.user_playlist( - username, playlist['id'], fields="tracks,next") + results = sp.playlist(playlist['id'], fields="tracks,next") tracks = results['tracks'] show_tracks(tracks) while tracks['next']: diff --git a/examples/user_starred_playlist.py b/examples/user_starred_playlist.py deleted file mode 100644 index 4d51456..0000000 --- a/examples/user_starred_playlist.py +++ /dev/null @@ -1,30 +0,0 @@ -# shows a user's starred playlist - -import sys -import spotipy -import spotipy.util as util - - -if len(sys.argv) > 1: - username = sys.argv[1] -else: - print("Whoops, need your username!") - print("usage: python user_playlists.py [username]") - sys.exit() - -token = util.prompt_for_user_token(username) - -if token: - sp = spotipy.Spotify(auth=token) - results = sp.user_playlist(username) - tracks = results['tracks'] - which = 1 - while tracks: - for item in tracks['items']: - track = item['track'] - print(which, track['name'], ' --', track['artists'][0]['name']) - which += 1 - tracks = sp.next(tracks) - -else: - print("Can't get token for", username) diff --git a/spotipy/client.py b/spotipy/client.py index e79bfca..1befef9 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -15,6 +15,7 @@ import time import requests import six +import warnings class SpotifyException(Exception): @@ -371,43 +372,57 @@ class Spotify(object): """ return self._get("me/playlists", limit=limit, offset=offset) - def user_playlists(self, user, limit=50, offset=0): - """ Gets playlists of a user + def playlist(self, playlist_id, fields=None, market=None): + """ Gets playlist by id. Parameters: - - user - the id of the usr - - limit - the number of items to return - - offset - the index of the first item to return + - playlist - the id of the playlist + - fields - which fields to return + - market - An ISO 3166-1 alpha-2 country code or the + string from_token. """ - return self._get("users/%s/playlists" % user, limit=limit, - offset=offset) + plid = self._get_id('playlist', playlist_id) + return self._get("playlists/%s" % (plid), fields=fields, market=market) + + def playlist_tracks(self, playlist_id, fields=None, + limit=100, offset=0, market=None): + """ Get full details of the tracks 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. + """ + plid = self._get_id('playlist', playlist_id) + return self._get("playlists/%s/tracks" % (plid), + limit=limit, offset=offset, fields=fields, + market=market) + + def user_playlist(self, user, playlist_id=None, + fields=None, market=None): + warnings.warn( + "You should use `playlist(playlist_id)` instead", + DeprecationWarning) - def user_playlist(self, user, playlist_id=None, fields=None): """ Gets playlist of a user + Parameters: - user - the id of the user - playlist_id - the id of the playlist - fields - which fields to return """ if playlist_id is None: - return self._get("users/%s/starred" % (user), fields=fields) - plid = self._get_id('playlist', playlist_id) - return self._get("users/%s/playlists/%s" % (user, plid), fields=fields) + return self._get("users/%s/starred" % user) + return self.playlist(playlist_id, fields=fields, market=market) - def playlist(self, playlist_id, fields=None, market=None): - """ Gets playlist by id - - Parameters: - - playlist - the id of the playlist - - fields - which fields to return - - market - An ISO 3166-1 alpha-2 country code or the string - from_token. - """ - plid = self._get_id('playlist', playlist_id) - return self._get("playlists/%s" % (plid), fields=fields) - - def user_playlist_tracks(self, user, playlist_id=None, fields=None, + def user_playlist_tracks(self, user=None, playlist_id=None, fields=None, limit=100, offset=0, market=None): + warnings.warn( + "You should use `playlist_tracks(playlist_id)` instead", + DeprecationWarning) + """ Get full details of the tracks of a playlist owned by a user. Parameters: @@ -418,10 +433,19 @@ class Spotify(object): - offset - the index of the first track to return - market - an ISO 3166-1 alpha-2 country code. """ - plid = self._get_id('playlist', playlist_id) - return self._get("users/%s/playlists/%s/tracks" % (user, plid), - limit=limit, offset=offset, fields=fields, - market=market) + return self.playlist_tracks(playlist_id, limit=limit, offset=offset, + fields=fields, market=market) + + def user_playlists(self, user, limit=50, offset=0): + """ Gets playlists of a user + + Parameters: + - user - the id of the usr + - limit - the number of items to return + - offset - the index of the first item to return + """ + return self._get("users/%s/playlists" % user, limit=limit, + offset=offset) def user_playlist_create(self, user, name, public=True, description=''): """ Creates a playlist for a user diff --git a/tests/test_auth.py b/tests/test_auth.py index a0b8237..0f6ef16 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -23,6 +23,7 @@ from spotipy import ( import os import sys import unittest +import warnings sys.path.insert(0, os.path.abspath(os.pardir)) @@ -61,6 +62,12 @@ class AuthTestSpotipy(unittest.TestCase): @classmethod def setUpClass(self): + if sys.version_info >= (3, 2): + # >= Python3.2 only + warnings.filterwarnings( + "ignore", + category=ResourceWarning, + message="unclosed.*") missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) @@ -119,13 +126,6 @@ class AuthTestSpotipy(unittest.TestCase): results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) - # known API issue currently causes this test to fail - # the issue is that the API doesn't currently respect the - # limit parameter - # def user_playlist_tracks(self, user, playlist_id=None, fields=None, - # limit=100, offset=0): - # self.assertTrue(len(playlists['items']) == 5) - def test_current_user_saved_albums(self): # List albums = self.spotify.current_user_saved_albums() @@ -281,6 +281,17 @@ class AuthTestSpotipy(unittest.TestCase): pl = self.spotify.playlist(self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) + def test_playlist_tracks(self): + # New playlist ID + pl = self.spotify.playlist_tracks(self.playlist_new_id, limit=2) + self.assertTrue(len(pl["items"]) == 2) + self.assertTrue(pl["total"] > 0) + + # Old playlist ID + pl = self.spotify.playlist_tracks(self.playlist, limit=2) + self.assertTrue(len(pl["items"]) == 2) + self.assertTrue(pl["total"] > 0) + def test_user_follows_and_unfollows_artist(self): # Initially follows 1 artist res = self.spotify.current_user_followed_artists() @@ -307,6 +318,24 @@ class AuthTestSpotipy(unittest.TestCase): # Unfollow these 2 users self.spotify.user_unfollow_users(users) + def test_deprecated_starred(self): + pl = self.spotify.user_playlist(self.username) + self.assertTrue(pl["tracks"] is None) + self.assertTrue(pl["owner"] is None) + + def test_deprecated_user_playlist(self): + # Test without user due to change from + # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ + pl = self.spotify.user_playlist(None, self.playlist) + self.assertTrue(pl["tracks"]["total"] > 0) + + def test_deprecated_user_playlis(self): + # Test without user due to change from + # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ + pl = self.spotify.user_playlist_tracks(None, self.playlist, limit=2) + self.assertTrue(len(pl["items"]) == 2) + self.assertTrue(pl["total"] > 0) + if __name__ == '__main__': unittest.main()