Merge pull request #54 from jsundram/master

Support for getting tracks from albums with more than 50 tracks
This commit is contained in:
Paul Lamere 2015-06-04 18:09:31 +02:00
commit 96edfc44ab
5 changed files with 60 additions and 43 deletions

View File

@ -2,14 +2,15 @@ v1.40, June 12, 2014 -- Initial public release.
v1.42, June 19, 2014 -- Removed dependency on simplejson v1.42, June 19, 2014 -- Removed dependency on simplejson
v1.43, June 27, 2014 -- Fixed JSON handling issue v1.43, June 27, 2014 -- Fixed JSON handling issue
v1.44, July 3, 2014 -- Added show_tracks.py exampole v1.44, July 3, 2014 -- Added show_tracks.py exampole
v1.45, July 7, 2014 -- Support for related artists endpoint. Don't used v1.45, July 7, 2014 -- Support for related artists endpoint. Don't use
cache auth codes when scope changes cache auth codes when scope changes
v1.50, August 14, 2014 -- Refactored util out of examples and into the main v1.50, August 14, 2014 -- Refactored util out of examples and into the main
package package
v2.301, August 19, 2014 -- Upgraded version number to take precedence over v2.301, August 19, 2014 -- Upgraded version number to take precedence over
previously botched release (sigh) previously botched release (sigh)
v2.310, August 20, 2014 -- Added playlist replace and remove methods. Added auth v2.310, August 20, 2014 -- Added playlist replace and remove methods. Added auth
tests. Improved API docs tests. Improved API docs
v2.310, January 05, 2015 -- Added session support v2.310, January 5, 2015 -- Added session support
v2.3.1, March 28, 2015 -- auto retry support v2.3.1, March 28, 2015 -- Auto retry support
v2.3.5, April 28, 2015 -- fixed bug in auto retry support v2.3.5, April 28, 2015 -- Fixed bug in auto retry support
v2.3.6, June 3, 2015 -- Support for offset/limit with album_tracks API

View File

@ -55,7 +55,7 @@ If you have suggestions, bugs or other issues specific to this library, file the
- v1.42, June 19, 2014 -- Removed dependency on simplejson - v1.42, June 19, 2014 -- Removed dependency on simplejson
- v1.43, June 27, 2014 -- Fixed JSON handling issue - v1.43, June 27, 2014 -- Fixed JSON handling issue
- v1.44, July 3, 2014 -- Added show tracks.py example - v1.44, July 3, 2014 -- Added show tracks.py example
- v1.45, July 7, 2014 -- Support for related artists endpoint. Don't used cache auth codes when scope changes - v1.45, July 7, 2014 -- Support for related artists endpoint. Don't use cache auth codes when scope changes
- v1.49, July 23, 2014 -- Support for "Your Music" tracks (add, delete, get), with examples - v1.49, July 23, 2014 -- Support for "Your Music" tracks (add, delete, get), with examples
- v1.50, August 14, 2014 -- Refactored util out of examples and into the main package - v1.50, August 14, 2014 -- Refactored util out of examples and into the main package
- v1.301, August 19, 2014 -- Upgraded version number to take precedence over previously botched release (sigh) - v1.301, August 19, 2014 -- Upgraded version number to take precedence over previously botched release (sigh)
@ -68,3 +68,4 @@ If you have suggestions, bugs or other issues specific to this library, file the
- v2.3.2 - March 31, 2015 -- Added auto retry logic - v2.3.2 - March 31, 2015 -- Added auto retry logic
- v2.3.3 - April 1, 2015 -- added client credential flow - v2.3.3 - April 1, 2015 -- added client credential flow
- v2.3.5 - April 28, 2015 -- Fixed bug in auto retry logic - v2.3.5 - April 28, 2015 -- Fixed bug in auto retry logic
- v2.3.6 - June 3, 2015 -- Support for offset/limit with album_tracks API

View File

@ -2,7 +2,7 @@ from setuptools import setup
setup( setup(
name='spotipy', name='spotipy',
version='2.3.5', version='2.3.6',
description='simple client for the Spotify Web API', description='simple client for the Spotify Web API',
author="@plamere", author="@plamere",
author_email="paul@echonest.com", author_email="paul@echonest.com",

View File

@ -100,7 +100,7 @@ class Spotify(object):
try: try:
r.raise_for_status() r.raise_for_status()
except: except:
raise SpotifyException(r.status_code, raise SpotifyException(r.status_code,
-1, u'%s:\n %s' % (r.url, r.json()['error']['message'])) -1, u'%s:\n %s' % (r.url, r.json()['error']['message']))
if len(r.text) > 0: if len(r.text) > 0:
results = r.json() results = r.json()
@ -153,7 +153,7 @@ class Spotify(object):
''' returns the next result given a paged result ''' returns the next result given a paged result
Parameters: Parameters:
- result - a previously returned paged result - result - a previously returned paged result
''' '''
if result['next']: if result['next']:
return self._get(result['next']) return self._get(result['next'])
@ -164,13 +164,13 @@ class Spotify(object):
''' returns the previous result given a paged result ''' returns the previous result given a paged result
Parameters: Parameters:
- result - a previously returned paged result - result - a previously returned paged result
''' '''
if result['previous']: if result['previous']:
return self._get(result['previous']) return self._get(result['previous'])
else: else:
return None return None
def _warn(self, msg): def _warn(self, msg):
print('warning:' + msg, file=sys.stderr) print('warning:' + msg, file=sys.stderr)
@ -215,7 +215,7 @@ class Spotify(object):
tlist = [self._get_id('artist', a) for a in artists] tlist = [self._get_id('artist', a) for a in artists]
return self._get('artists/?ids=' + ','.join(tlist)) return self._get('artists/?ids=' + ','.join(tlist))
def artist_albums(self, artist_id, album_type=None, country=None, def artist_albums(self, artist_id, album_type=None, country=None,
limit=20, offset=0): limit=20, offset=0):
''' Get Spotify catalog information about an artist's albums ''' Get Spotify catalog information about an artist's albums
@ -228,11 +228,11 @@ class Spotify(object):
''' '''
trid = self._get_id('artist', artist_id) trid = self._get_id('artist', artist_id)
return self._get('artists/' + trid + '/albums', album_type=album_type, return self._get('artists/' + trid + '/albums', album_type=album_type,
country=country, limit=limit, offset=offset) country=country, limit=limit, offset=offset)
def artist_top_tracks(self, artist_id, country='US'): def artist_top_tracks(self, artist_id, country='US'):
''' Get Spotify catalog information about an artist's top 10 tracks ''' Get Spotify catalog information about an artist's top 10 tracks
by country. by country.
Parameters: Parameters:
@ -244,8 +244,8 @@ class Spotify(object):
return self._get('artists/' + trid + '/top-tracks', country=country) return self._get('artists/' + trid + '/top-tracks', country=country)
def artist_related_artists(self, artist_id): def artist_related_artists(self, artist_id):
''' Get Spotify catalog information about artists similar to an ''' Get Spotify catalog information about artists similar to an
identified artist. Similarity is based on analysis of the identified artist. Similarity is based on analysis of the
Spotify community's listening history. Spotify community's listening history.
Parameters: Parameters:
@ -264,15 +264,17 @@ class Spotify(object):
trid = self._get_id('album', album_id) trid = self._get_id('album', album_id)
return self._get('albums/' + trid) return self._get('albums/' + trid)
def album_tracks(self, album_id): def album_tracks(self, album_id, limit=50, offset=0):
''' Get Spotify catalog information about an album's tracks ''' Get Spotify catalog information about an album's tracks
Parameters: Parameters:
- album_id - the album ID, URI or URL - album_id - the album ID, URI or URL
- limit - the number of items to return
- offset - the index of the first item to return
''' '''
trid = self._get_id('album', album_id) trid = self._get_id('album', album_id)
return self._get('albums/' + trid + '/tracks/') return self._get('albums/' + trid + '/tracks/', limit=limit, offset=offset)
def albums(self, albums): def albums(self, albums):
''' returns a list of albums given the album IDs, URIs, or URLs ''' returns a list of albums given the album IDs, URIs, or URLs
@ -303,7 +305,7 @@ class Spotify(object):
- user - the id of the usr - user - the id of the usr
''' '''
return self._get('users/' + user) return self._get('users/' + user)
def user_playlists(self, user, limit=50, offset=0): def user_playlists(self, user, limit=50, offset=0):
''' Gets playlists of a user ''' Gets playlists of a user
@ -326,7 +328,7 @@ class Spotify(object):
plid = self._get_id('playlist', playlist_id) plid = self._get_id('playlist', playlist_id)
return self._get("users/%s/playlists/%s" % (user, plid), fields=fields) return self._get("users/%s/playlists/%s" % (user, plid), fields=fields)
def user_playlist_tracks(self, user, playlist_id = None, fields=None, def user_playlist_tracks(self, user, playlist_id = None, fields=None,
limit=100, offset=0): limit=100, offset=0):
''' Get full details of the tracks of a playlist owned by a user. ''' Get full details of the tracks of a playlist owned by a user.
@ -338,7 +340,7 @@ class Spotify(object):
- offset - the index of the first track to return - offset - the index of the first track to return
''' '''
plid = self._get_id('playlist', playlist_id) plid = self._get_id('playlist', playlist_id)
return self._get("users/%s/playlists/%s/tracks" % (user, plid), return self._get("users/%s/playlists/%s/tracks" % (user, plid),
limit=limit, offset=offset, fields=fields) limit=limit, offset=offset, fields=fields)
def user_playlist_create(self, user, name, public=True): def user_playlist_create(self, user, name, public=True):
@ -352,7 +354,7 @@ class Spotify(object):
data = {'name':name, 'public':public } data = {'name':name, 'public':public }
return self._post("users/%s/playlists" % (user,), payload = data) return self._post("users/%s/playlists" % (user,), payload = data)
def user_playlist_add_tracks(self, user, playlist_id, tracks, def user_playlist_add_tracks(self, user, playlist_id, tracks,
position=None): position=None):
''' Adds tracks to a playlist ''' Adds tracks to a playlist
@ -364,7 +366,7 @@ class Spotify(object):
''' '''
plid = self._get_id('playlist', playlist_id) plid = self._get_id('playlist', playlist_id)
ftracks = [ self._get_uri('track', tid) for tid in tracks] ftracks = [ self._get_uri('track', tid) for tid in tracks]
return self._post("users/%s/playlists/%s/tracks" % (user,plid), return self._post("users/%s/playlists/%s/tracks" % (user,plid),
payload = ftracks, position=position) payload = ftracks, position=position)
def user_playlist_replace_tracks(self, user, playlist_id, tracks): def user_playlist_replace_tracks(self, user, playlist_id, tracks):
@ -378,7 +380,7 @@ class Spotify(object):
plid = self._get_id('playlist', playlist_id) plid = self._get_id('playlist', playlist_id)
ftracks = [ self._get_uri('track', tid) for tid in tracks] ftracks = [ self._get_uri('track', tid) for tid in tracks]
payload = { "uris": ftracks } payload = { "uris": ftracks }
return self._put("users/%s/playlists/%s/tracks" % (user,plid), return self._put("users/%s/playlists/%s/tracks" % (user,plid),
payload = payload) payload = payload)
def user_playlist_reorder_tracks(self, user, playlist_id, range_start, insert_before, def user_playlist_reorder_tracks(self, user, playlist_id, range_start, insert_before,
@ -402,7 +404,7 @@ class Spotify(object):
return self._put("users/%s/playlists/%s/tracks" % (user,plid), return self._put("users/%s/playlists/%s/tracks" % (user,plid),
payload = payload) payload = payload)
def user_playlist_remove_all_occurrences_of_tracks(self, user, playlist_id, def user_playlist_remove_all_occurrences_of_tracks(self, user, playlist_id,
tracks, snapshot_id=None): tracks, snapshot_id=None):
''' Removes all occurrences of the given tracks from the given playlist ''' Removes all occurrences of the given tracks from the given playlist
@ -419,19 +421,19 @@ class Spotify(object):
payload = { "tracks": [ {"uri": track} for track in ftracks] } payload = { "tracks": [ {"uri": track} for track in ftracks] }
if snapshot_id: if snapshot_id:
payload["snapshot_id"] = snapshot_id payload["snapshot_id"] = snapshot_id
return self._delete("users/%s/playlists/%s/tracks" % (user, plid), return self._delete("users/%s/playlists/%s/tracks" % (user, plid),
payload = payload) payload = payload)
def user_playlist_remove_specific_occurrences_of_tracks(self, user, def user_playlist_remove_specific_occurrences_of_tracks(self, user,
playlist_id, tracks, snapshot_id=None): playlist_id, tracks, snapshot_id=None):
''' Removes all occurrences of the given tracks from the given playlist ''' Removes all occurrences of the given tracks from the given playlist
Parameters: Parameters:
- user - the id of the user - user - the id of the user
- playlist_id - the id of the playlist - playlist_id - the id of the playlist
- tracks - an array of objects containing Spotify URIs of the tracks to remove with their current positions in the playlist. For example: - tracks - an array of objects containing Spotify URIs of the tracks to remove with their current positions in the playlist. For example:
[ { "uri":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] }, [ { "uri":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] },
{ "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ] { "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
- snapshot_id - optional id of the playlist snapshot - snapshot_id - optional id of the playlist snapshot
''' '''
@ -445,7 +447,7 @@ class Spotify(object):
payload = { "tracks": ftracks } payload = { "tracks": ftracks }
if snapshot_id: if snapshot_id:
payload["snapshot_id"] = snapshot_id payload["snapshot_id"] = snapshot_id
return self._delete("users/%s/playlists/%s/tracks" % (user, plid), return self._delete("users/%s/playlists/%s/tracks" % (user, plid),
payload = payload) payload = payload)
def me(self): def me(self):
@ -455,7 +457,7 @@ class Spotify(object):
return self._get('me/') return self._get('me/')
def current_user(self): def current_user(self):
''' Get detailed profile information about the current user. ''' Get detailed profile information about the current user.
An alias for the 'me' method. An alias for the 'me' method.
''' '''
return self.me() return self.me()
@ -472,7 +474,7 @@ class Spotify(object):
return self._get('me/tracks', limit=limit, offset=offset) return self._get('me/tracks', limit=limit, offset=offset)
def current_user_saved_tracks_delete(self, tracks=[]): def current_user_saved_tracks_delete(self, tracks=[]):
''' 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.
Parameters: Parameters:
@ -493,7 +495,7 @@ class Spotify(object):
def current_user_saved_tracks_add(self, tracks=[]): def current_user_saved_tracks_add(self, tracks=[]):
''' Add one or more tracks to the current user's ''' Add one or more tracks to the current user's
"Your Music" library. "Your Music" library.
Parameters: Parameters:
@ -503,16 +505,16 @@ class Spotify(object):
return self._put('me/tracks/?ids=' + ','.join(tlist)) return self._put('me/tracks/?ids=' + ','.join(tlist))
def featured_playlists(self, locale=None, country=None, def featured_playlists(self, locale=None, country=None,
timestamp=None, limit=20, offset = 0): timestamp=None, limit=20, offset = 0):
''' Get a list of Spotify featured playlists ''' Get a list of Spotify featured playlists
Parameters: Parameters:
- locale - The desired language, consisting of a lowercase ISO - locale - The desired language, consisting of a lowercase ISO
639 language code and an uppercase ISO 3166-1 alpha-2 country 639 language code and an uppercase ISO 3166-1 alpha-2 country
code, joined by an underscore. code, joined by an underscore.
- country - An ISO 3166-1 alpha-2 country code. - country - An ISO 3166-1 alpha-2 country code.
- timestamp - A timestamp in ISO 8601 format: - timestamp - A timestamp in ISO 8601 format:
yyyy-MM-ddTHH:mm:ss. Use this parameter to specify the user's yyyy-MM-ddTHH:mm:ss. Use this parameter to specify the user's
@ -524,25 +526,25 @@ class Spotify(object):
- offset - The index of the first item to return. Default: 0 - offset - The index of the first item to return. Default: 0
(the first object). Use with limit to get the next set of (the first object). Use with limit to get the next set of
items. items.
''' '''
return self._get('browse/featured-playlists', locale=locale, return self._get('browse/featured-playlists', locale=locale,
country=country, timestamp=timestamp, limit=limit, offset=offset) country=country, timestamp=timestamp, limit=limit, offset=offset)
def new_releases(self, country=None, limit=20, offset = 0): def new_releases(self, country=None, limit=20, offset = 0):
''' Get a list of new album releases featured in Spotify ''' Get a list of new album releases featured in Spotify
Parameters: Parameters:
- country - An ISO 3166-1 alpha-2 country code. - country - An ISO 3166-1 alpha-2 country code.
- limit - The maximum number of items to return. Default: 20. - limit - The maximum number of items to return. Default: 20.
Minimum: 1. Maximum: 50 Minimum: 1. Maximum: 50
- offset - The index of the first item to return. Default: 0 - offset - The index of the first item to return. Default: 0
(the first object). Use with limit to get the next set of (the first object). Use with limit to get the next set of
items. items.
''' '''
return self._get('browse/new-releases', country=country, return self._get('browse/new-releases', country=country,
limit=limit, offset=offset) limit=limit, offset=offset)
def _get_id(self, type, id): def _get_id(self, type, id):

View File

@ -15,6 +15,8 @@ class TestSpotipy(unittest.TestCase):
weezer_urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu' weezer_urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL' pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL'
radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb' radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'
bad_id = 'BAD_ID' bad_id = 'BAD_ID'
@ -38,6 +40,17 @@ class TestSpotipy(unittest.TestCase):
results = self.spotify.album_tracks(self.pinkerton_urn) results = self.spotify.album_tracks(self.pinkerton_urn)
self.assertTrue(len(results['items']) == 10) self.assertTrue(len(results['items']) == 10)
def test_album_tracks_many(self):
results = self.spotify.album_tracks(self.angeles_haydn_urn)
tracks = results['items']
total, received = results['total'], len(tracks)
while received < total:
results = self.spotify.album_tracks(self.angeles_haydn_urn, offset=received)
tracks.extend(results['items'])
received = len(tracks)
self.assertEqual(received, total)
def test_albums(self): def test_albums(self):
results = self.spotify.albums([self.pinkerton_urn, self.pablo_honey_urn]) results = self.spotify.albums([self.pinkerton_urn, self.pablo_honey_urn])
self.assertTrue('albums' in results) self.assertTrue('albums' in results)