From 7601aaab587f4fe77d1dcfbf15cf39dfe69592ba Mon Sep 17 00:00:00 2001 From: Paul Lamere Date: Wed, 20 Aug 2014 14:04:29 -0400 Subject: [PATCH] refacord to support playlist editing --- .../remove_specific_tracks_from_playlist.py | 32 ++ examples/remove_tracks_from_playlist.py | 28 ++ examples/replace_tracks_in_playlist.py | 28 ++ examples/show_artist.py | 2 + spotipy/__init__.py | 297 +----------- spotipy/oauth2.py | 9 +- spotipy/spotipy.py | 427 ++++++++++++++++++ spotipy/util.py | 15 +- tests/tests.py | 30 +- 9 files changed, 568 insertions(+), 300 deletions(-) create mode 100644 examples/remove_specific_tracks_from_playlist.py create mode 100644 examples/remove_tracks_from_playlist.py create mode 100644 examples/replace_tracks_in_playlist.py create mode 100644 spotipy/spotipy.py diff --git a/examples/remove_specific_tracks_from_playlist.py b/examples/remove_specific_tracks_from_playlist.py new file mode 100644 index 0000000..b7eb56d --- /dev/null +++ b/examples/remove_specific_tracks_from_playlist.py @@ -0,0 +1,32 @@ + +# Adds tracks to a playlist + +import pprint +import sys + +import spotipy +import spotipy.oauth2 as oauth2 +import spotipy.util as util + +if len(sys.argv) > 3: + username = sys.argv[1] + playlist_id = sys.argv[2] + track_ids_and_positions = sys.argv[3:] + track_ids = [ ] + for t_pos in sys.argv[3:]: + tid, pos = t_pos.split(',') + track_ids.append( { "uri" : tid, "positions": [ int(pos)] } ) +else: + print "Usage: %s username playlist_id track_id,pos track_id,pos ..." % (sys.argv[0],) + sys.exit() + +scope = 'playlist-modify-public' +token = util.prompt_for_user_token(username, scope) + +if token: + sp = spotipy.Spotify(auth=token) + sp.trace = False + results = sp.user_playlist_remove_specific_occurrences_of_tracks(username, playlist_id, track_ids) + pprint.pprint(results) +else: + print "Can't get token for", username diff --git a/examples/remove_tracks_from_playlist.py b/examples/remove_tracks_from_playlist.py new file mode 100644 index 0000000..0390df4 --- /dev/null +++ b/examples/remove_tracks_from_playlist.py @@ -0,0 +1,28 @@ + +# Adds tracks to a playlist + +import pprint +import sys + +import spotipy +import spotipy.oauth2 as oauth2 +import spotipy.util as util + +if len(sys.argv) > 3: + username = sys.argv[1] + playlist_id = sys.argv[2] + track_ids = sys.argv[3:] +else: + print "Usage: %s username playlist_id track_id ..." % (sys.argv[0],) + sys.exit() + +scope = 'playlist-modify-public' +token = util.prompt_for_user_token(username, scope) + +if token: + sp = spotipy.Spotify(auth=token) + sp.trace = False + results = sp.user_playlist_remove_all_occurrences_of_tracks(username, playlist_id, track_ids) + pprint.pprint(results) +else: + print "Can't get token for", username diff --git a/examples/replace_tracks_in_playlist.py b/examples/replace_tracks_in_playlist.py new file mode 100644 index 0000000..2ba58e0 --- /dev/null +++ b/examples/replace_tracks_in_playlist.py @@ -0,0 +1,28 @@ + +# Replaces all tracks in a playlist + +import pprint +import sys + +import spotipy +import spotipy.oauth2 as oauth2 +import spotipy.util as util + +if len(sys.argv) > 3: + username = sys.argv[1] + playlist_id = sys.argv[2] + track_ids = sys.argv[3:] +else: + print "Usage: %s username playlist_id track_id ..." % (sys.argv[0],) + sys.exit() + +scope = 'playlist-modify-public' +token = util.prompt_for_user_token(username, scope) + +if token: + sp = spotipy.Spotify(auth=token) + sp.trace = False + results = sp.user_playlist_replace_tracks(username, playlist_id, track_ids) + pprint.pprint(results) +else: + print "Can't get token for", username diff --git a/examples/show_artist.py b/examples/show_artist.py index bba54e7..0180b2a 100644 --- a/examples/show_artist.py +++ b/examples/show_artist.py @@ -10,5 +10,7 @@ else: urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu' sp = spotipy.Spotify() + artist = sp.artist(urn) + pprint.pprint(artist) diff --git a/spotipy/__init__.py b/spotipy/__init__.py index 297e92b..d77e12a 100644 --- a/spotipy/__init__.py +++ b/spotipy/__init__.py @@ -1,295 +1,2 @@ -# coding: utf-8 - -from __future__ import print_function - -import sys -import base64 -import requests -import json - - -__all__ = ['oauth2', 'util'] - -''' A simple and thin Python library for the Spotify Web API -''' - -class SpotifyException(Exception): - def __init__(self, http_status, code, msg): - self.http_status = http_status - self.code = code - self.msg = msg - - def __str__(self): - return u'http status: {0}, code:{1} - {2}'.format( - self.http_status, self.code, self.msg) - - -class Spotify(object): - ''' - Example usage: - - import spotipy - - urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu' - sp = spotipy.Spotify() - - sp.trace = True # turn on tracing - - artist = sp.artist(urn) - print(artist) - - user = sp.user('plamere') - print(user) - ''' - - trace = False - _auth = None - - def __init__(self, auth=None): - self.prefix = 'https://api.spotify.com/v1/' - self._auth = auth - - def _auth_headers(self): - if self._auth: - return {'Authorization': 'Bearer {0}'.format(self._auth)} - else: - return None - - def _internal_call(self, verb, method, params): - if not method.startswith('http'): - url = self.prefix + method - else: - url = method - args = dict(params=params) - headers = self._auth_headers() - r = requests.request(verb, url, headers=headers, **args) - if self.trace: - print() - print(verb, r.url) - if not (r.status_code >= 200 and r.status_code < 300): - raise SpotifyException(r.status_code, -1, u'the requested resource could not be found: ' + r.url) - if len(r.text) > 0: - results = r.json() - if self.trace: - print('RESP', results) - print() - return results - else: - return {} - - def get(self, method, args=None, **kwargs): - if args: - kwargs.update(args) - return self._internal_call('GET', method, kwargs) - - def delete(self, method, args=None, **kwargs): - if args: - kwargs.update(args) - return self._internal_call('DELETE', method, kwargs) - - def put(self, method, args=None, **kwargs): - if args: - kwargs.update(args) - return self._internal_call('PUT', method, kwargs) - - def post(self, method, payload=None, **kwargs): - args = dict(params=kwargs) - if not method.startswith('http'): - url = self.prefix + method - else: - url = method - headers = self._auth_headers() - headers['Content-Type'] = 'application/json' - if payload: - r = requests.post(url, headers=headers, data=json.dumps(payload), **args) - else: - r = requests.post(url, headers=headers, **args) - if self.trace: - print() - print("POST", r.url) - print("DATA", json.dumps(payload)) - if not (r.status_code >= 200 and r.status_code < 300): - raise SpotifyException(r.status_code, -1, u'the requested resource could not be found: ' + r.url) - try: - results = r.json() - if self.trace: - print('RESP', results) - print() - return results - except ValueError: - return None - - def next(self, result): - ''' returns the next result given a result - ''' - if result['next']: - return self.get(result['next']) - else: - return None - - def previous(self, result): - ''' returns the previous result given a result - ''' - if result['previous']: - return self.get(result['previous']) - else: - return None - - def _warn(self, msg): - print('warning:' + msg, file=sys.stderr) - - def track(self, track_id): - ''' returns a single track given the track's ID, URN or URL - ''' - - trid = self._get_id('track', track_id) - return self.get('tracks/' + trid) - - def tracks(self, tracks): - ''' returns a list of tracks given the track IDs, URNs, or URLs - ''' - - tlist = [self._get_id('track', t) for t in tracks] - return self.get('tracks/?ids=' + ','.join(tlist)) - - def artist(self, artist_id): - ''' returns a single artist given the artist's ID, URN or URL - ''' - - trid = self._get_id('artist', artist_id) - return self.get('artists/' + trid) - - - def artists(self, artists): - ''' returns a list of artists given the artist IDs, URNs, or URLs - ''' - - tlist = [self._get_id('artist', a) for a in artists] - return self.get('artists/?ids=' + ','.join(tlist)) - - def artist_albums(self, artist_id, album_type=None, country=None, limit=20, offset=0): - ''' Get Spotify catalog information about an artist’s albums - ''' - - trid = self._get_id('artist', artist_id) - return self.get('artists/' + trid + '/albums', album_type=album_type, country=country, limit=limit, offset=offset) - - def artist_top_tracks(self, artist_id, country='US'): - ''' Get Spotify catalog information about an artist’s top 10 tracks by country. - ''' - - trid = self._get_id('artist', artist_id) - return self.get('artists/' + trid + '/top-tracks', country=country) - - def artist_related_artists(self, artist_id): - ''' Get Spotify catalog information about artists similar to an identified artist. Similarity is based - on analysis of the Spotify community’s listening history. - ''' - trid = self._get_id('artist', artist_id) - return self.get('artists/' + trid + '/related-artists') - - def album(self, album_id): - ''' returns a single album given the album's ID, URN or URL - ''' - - trid = self._get_id('album', album_id) - return self.get('albums/' + trid) - - def album_tracks(self, album_id): - ''' Get Spotify catalog information about an album’s tracks - ''' - - trid = self._get_id('album', album_id) - return self.get('albums/' + trid + '/tracks/') - - def albums(self, albums): - ''' returns a list of albums given the album IDs, URNs, or URLs - ''' - - tlist = [self._get_id('album', a) for a in albums] - return self.get('albums/?ids=' + ','.join(tlist)) - - def search(self, q, limit=10, offset=0, type='track'): - ''' searches for an item - ''' - return self.get('search', q=q, limit=limit, offset=offset, type=type) - - def user(self, user_id): - ''' Gets basic profile information about a Spotify User - ''' - return self.get('users/' + user_id) - - def user_playlists(self, user): - ''' Gets playlists of a user - ''' - return self.get("users/%s/playlists" % user) - - def user_playlist(self, user, playlist_id = None, fields=None): - ''' Gets playlist of a user - ''' - if playlist_id == None: - return self.get("users/%s/starred" % (user), fields=fields) - return self.get("users/%s/playlists/%s" % (user, playlist_id), fields=fields) - - def user_playlist_create(self, user, name, public=True): - ''' Creates a playlist for a user - ''' - data = {'name':name, 'public':True } - return self.post("users/%s/playlists" % (user,), payload = data) - - def user_playlist_add_tracks(self, user, playlist_id, tracks, position=None): - ''' Adds tracks to a playlist - ''' - return self.post("users/%s/playlists/%s/tracks" % (user,playlist_id), - payload = tracks, position=position) - - def me(self): - ''' Get detailed profile information about the current user. - ''' - return self.get('me/') - - def current_user(self): - ''' Get detailed profile information about the current user. - ''' - return self.me() - - def current_user_saved_tracks(self, limit=20, offset=0): - ''' Gets a list of the tracks saved in the current authorized user's - "Your Music" library - ''' - return self.get('me/tracks', limit=limit, offset=offset) - - def current_user_saved_tracks_delete(self, ids=[]): - ''' Remove one or more tracks from the current user’s - “Your Music” library. - ''' - tlist = [self._get_id('track', t) for t in ids] - return self.delete('me/tracks/?ids=' + ','.join(tlist)) - - def current_user_saved_tracks_add(self, ids=[]): - ''' Add one or more tracks to the current user’s - “Your Music” library. - ''' - tlist = [self._get_id('track', t) for t in ids] - return self.put('me/tracks/?ids=' + ','.join(tlist)) - - def current_user_saved_tracks_contains(self, ids=[]): - ''' Check if one or more tracks is already saved in - the current Spotify user’s “Your Music” library. - ''' - tlist = [self._get_id('track', t) for t in ids] - return self.get('me/tracks/contains?ids=' + ','.join(tlist)) - - def _get_id(self, type, id): - fields = id.split(':') - if len(fields) == 3: - if type != fields[1]: - self._warn('expected id of type ' + type + ' but found type ' + fields[2] + " " + id) - return fields[2] - fields = id.split('/') - if len(fields) >= 3: - itype = fields[-2] - if type != itype: - self._warn('expected id of type ' + type + ' but found type ' + itype + " " + id) - return fields[-1] - return id +__all__ = ['spotipy'] +from spotipy import * diff --git a/spotipy/oauth2.py b/spotipy/oauth2.py index 97475df..57fad8a 100644 --- a/spotipy/oauth2.py +++ b/spotipy/oauth2.py @@ -17,7 +17,8 @@ class SpotifyOAuth(object): OAUTH_AUTHORIZE_URL = 'https://accounts.spotify.com/authorize' OAUTH_TOKEN_URL = 'https://accounts.spotify.com/api/token' - def __init__(self, client_id, client_secret, redirect_uri, state=None, scope=None, cache_path=None): + def __init__(self, client_id, client_secret, redirect_uri, + state=None, scope=None, cache_path=None): self.client_id = client_id self.client_secret = client_secret self.redirect_uri = redirect_uri @@ -87,7 +88,8 @@ class SpotifyOAuth(object): headers = {'Authorization': 'Basic %s' % auth_header} - response = requests.post(self.OAUTH_TOKEN_URL, data=payload, headers=headers, verify=True) + response = requests.post(self.OAUTH_TOKEN_URL, data=payload, + headers=headers, verify=True) if response.status_code is not 200: raise SpotifyOauthError(response.reason) token_info = response.json() @@ -109,7 +111,8 @@ class SpotifyOAuth(object): auth_header = base64.b64encode(self.client_id + ':' + self.client_secret) headers = {'Authorization': 'Basic %s' % auth_header} - response = requests.post(self.OAUTH_TOKEN_URL, data=payload, headers=headers, verify=True) + response = requests.post(self.OAUTH_TOKEN_URL, data=payload, + headers=headers, verify=True) if response.status_code is not 200: raise SpotifyOauthError(response.reason) token_info = response.json() diff --git a/spotipy/spotipy.py b/spotipy/spotipy.py new file mode 100644 index 0000000..a4d6405 --- /dev/null +++ b/spotipy/spotipy.py @@ -0,0 +1,427 @@ +# coding: utf-8 + +from __future__ import print_function + +import sys +import base64 +import requests +import json + +''' A simple and thin Python library for the Spotify Web API +''' + +class SpotifyException(Exception): + def __init__(self, http_status, code, msg): + self.http_status = http_status + self.code = code + self.msg = msg + + def __str__(self): + return u'http status: {0}, code:{1} - {2}'.format( + self.http_status, self.code, self.msg) + +class Spotify(object): + ''' + Example usage: + + import spotipy + + urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu' + sp = spotipy.Spotify() + + sp.trace = True # turn on tracing + + artist = sp.artist(urn) + print(artist) + + user = sp.user('plamere') + print(user) + ''' + + trace = False + _auth = None + + def __init__(self, auth=None): + ''' + creates a spotify object + + Parameters: + - auth - the optional authorization token + ''' + self.prefix = 'https://api.spotify.com/v1/' + self._auth = auth + + def _auth_headers(self): + if self._auth: + return {'Authorization': 'Bearer {0}'.format(self._auth)} + else: + return {} + + def _internal_call(self, method, url, payload, params): + args = dict(params=params) + if not url.startswith('http'): + url = self.prefix + url + headers = self._auth_headers() + headers['Content-Type'] = 'application/json' + + if payload: + r = requests.request(method, url, headers=headers, + data=json.dumps(payload), **args) + else: + r = requests.request(method, url, headers=headers, **args) + + if self.trace: + print() + print(method, r.url) + if payload: + print("DATA", json.dumps(payload)) + + try: + r.raise_for_status() + except: + raise SpotifyException(r.status_code, + -1, u'%s:\n %s' % (r.url, r.json()['error']['message'])) + if len(r.text) > 0: + results = r.json() + if self.trace: + print('RESP', results) + print() + return results + else: + return None + + def _get(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call('GET', url, payload, kwargs) + + def _post(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call('POST', url, payload, kwargs) + + def _delete(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call('DELETE', url, payload, kwargs) + + def _put(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call('PUT', url, payload, kwargs) + + def next(self, result): + ''' returns the next result given a paged result + + Parameters: + - result - a previously returned paged result + ''' + if result['next']: + return self._get(result['next']) + else: + return None + + def previous(self, result): + ''' returns the previous result given a paged result + + Parameters: + - result - a previously returned paged result + ''' + if result['previous']: + return self._get(result['previous']) + else: + return None + + def _warn(self, msg): + print('warning:' + msg, file=sys.stderr) + + def track(self, track_id): + ''' returns a single track given the track's ID, URI or URL + + Parameters: + - track_id - a spotify URI, URL or ID + ''' + + trid = self._get_id('track', track_id) + return self._get('tracks/' + trid) + + def tracks(self, tracks): + ''' returns a list of tracks given a list of track IDs, URIs, or URLs + + Parameters: + - tracks - a list of spotify URIs, URLs or IDs + ''' + + tlist = [self._get_id('track', t) for t in tracks] + return self._get('tracks/?ids=' + ','.join(tlist)) + + def artist(self, artist_id): + ''' returns a single artist given the artist's ID, URI or URL + + Parameters: + - artist_id - an artist ID, URI or URL + ''' + + trid = self._get_id('artist', artist_id) + return self._get('artists/' + trid) + + + def artists(self, artists): + ''' returns a list of artists given the artist IDs, URIs, or URLs + + Parameters: + - artists - a list of artist IDs, URIs or URLs + ''' + + tlist = [self._get_id('artist', a) for a in artists] + return self._get('artists/?ids=' + ','.join(tlist)) + + def artist_albums(self, artist_id, album_type=None, country=None, + limit=20, offset=0): + ''' Get Spotify catalog information about an artist's albums + + Parameters: + - artist_id - the artist ID, URI or URL + - album_type - 'album', 'single', 'appears_on', 'compilation' + - country - limit the response to one particular country. + - limit - the number of albums to return + - offset - the index of the first album to return + ''' + + trid = self._get_id('artist', artist_id) + return self._get('artists/' + trid + '/albums', album_type=album_type, + country=country, limit=limit, offset=offset) + + def artist_top_tracks(self, artist_id, country='US'): + ''' Get Spotify catalog information about an artist's top 10 tracks + by country. + + Parameters: + - artist_id - the artist ID, URI or URL + - country - limit the response to one particular country. + ''' + + trid = self._get_id('artist', artist_id) + return self._get('artists/' + trid + '/top-tracks', country=country) + + def artist_related_artists(self, artist_id): + ''' Get Spotify catalog information about artists similar to an + identified artist. Similarity is based on analysis of the + Spotify community's listening history. + + Parameters: + - artist_id - the artist ID, URI or URL + ''' + trid = self._get_id('artist', artist_id) + return self._get('artists/' + trid + '/related-artists') + + def album(self, album_id): + ''' returns a single album given the album's ID, URIs or URL + + Parameters: + - album_id - the album ID, URI or URL + ''' + + trid = self._get_id('album', album_id) + return self._get('albums/' + trid) + + def album_tracks(self, album_id): + ''' Get Spotify catalog information about an album's tracks + + Parameters: + - album_id - the album ID, URI or URL + ''' + + trid = self._get_id('album', album_id) + return self._get('albums/' + trid + '/tracks/') + + def albums(self, albums): + ''' returns a list of albums given the album IDs, URIs, or URLs + + Parameters: + - albums - a list of album IDs, URIs or URLs + ''' + + tlist = [self._get_id('album', a) for a in albums] + return self._get('albums/?ids=' + ','.join(tlist)) + + def search(self, q, limit=10, offset=0, type='track'): + ''' searches for an item + + Parameters: + - q - the search query + - limit - the number of items to return + - offset - the index of the first item to return + - type - the type of item to return. One of 'artist', 'album' + or 'track' + ''' + return self._get('search', q=q, limit=limit, offset=offset, type=type) + + def user(self, user): + ''' Gets basic profile information about a Spotify User + + Parameters: + - user - the id of the usr + ''' + return self._get('users/' + user) + + def user_playlists(self, user): + ''' Gets playlists of a user + + Parameters: + - user - the id of the usr + ''' + return self._get("users/%s/playlists" % user) + + 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 == None: + return self._get("users/%s/starred" % (user), fields=fields) + return self._get("users/%s/playlists/%s" % (user, playlist_id), + fields=fields) + + def user_playlist_create(self, user, name, public=True): + ''' Creates a playlist for a user + + Parameters: + - user - the id of the user + - name - the name of the playlist + - public - is the created playlist public + ''' + data = {'name':name, 'public':True } + return self._post("users/%s/playlists" % (user,), payload = data) + + def user_playlist_add_tracks(self, user, playlist_id, tracks, + position=None): + ''' Adds tracks to a playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - 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) + + def user_playlist_replace_tracks(self, user, playlist_id, tracks): + ''' Replace all tracks in a playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - the list of track URIs, URLs or 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) + + def user_playlist_remove_all_occurrences_of_tracks(self, user, playlist_id, + tracks): + ''' Removes all occurrences of the given tracks from the given playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - the list of track URIs, URLs or IDs to add + to the playlist + ''' + 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] } + return self._delete("users/%s/playlists/%s/tracks" % (user, plid), + payload = payload) + + def user_playlist_remove_specific_occurrences_of_tracks(self, user, + playlist_id, tracks): + ''' Removes all occurrences of the given tracks from the given playlist + + Parameters: + - user - the id of the user + - 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: + [ + {"uri": "spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "positions": [2] }, + {"uri": "spotify:track:1301WleyT98MSxVHPZCA6M", "positions": [7] } + ] + ''' + plid = self._get_id('playlist', playlist_id) + ftracks = [ self._get_uri('track', tid) for tid in tracks] + payload = { "tracks": ftracks } + return self._delete("users/%s/playlists/%s/tracks" % (user, plid), + payload = payload) + + def me(self): + ''' Get detailed profile information about the current user. + An alias for the 'current_user' method. + ''' + return self._get('me/') + + def current_user(self): + ''' Get detailed profile information about the current user. + An alias for the 'me' method. + ''' + return self.me() + + def current_user_saved_tracks(self, limit=20, offset=0): + ''' Gets a list of the tracks saved in the current authorized user's + "Your Music" library + + Parameters: + - limit - the number of tracks to return + - offset - the index of the first track to return + + ''' + return self._get('me/tracks', limit=limit, offset=offset) + + def current_user_saved_tracks_delete(self, tracks=[]): + ''' Remove one or more tracks from the current user's + "Your Music" library. + + Parameters: + - tracks - a list of track URIs, URLs or IDs + ''' + tlist = [self._get_id('track', t) for t in tracks] + return self._delete('me/tracks/?ids=' + ','.join(tlist)) + + def current_user_saved_tracks_add(self, tracks=[]): + ''' Add one or more tracks to the current user's + "Your Music" library. + + Parameters: + - tracks - a list of track URIs, URLs or IDs + ''' + tlist = [self._get_id('track', t) for t in tracks] + return self._put('me/tracks/?ids=' + ','.join(tlist)) + + def _get_id(self, type, id): + fields = id.split(':') + if len(fields) >= 3: + if type != fields[-2]: + self._warn('expected id of type ' + type + ' but found type ' \ + + fields[2] + " " + id) + return fields[-1] + fields = id.split('/') + if len(fields) >= 3: + itype = fields[-2] + if type != itype: + self._warn('expected id of type ' + type + ' but found type ' \ + + itype + " " + id) + return fields[-1] + return id + + def _get_uri(self, type, id): + return 'spotify:' + type + ":" + self._get_id(type, id) diff --git a/spotipy/util.py b/spotipy/util.py index 14e068d..6e4b17c 100644 --- a/spotipy/util.py +++ b/spotipy/util.py @@ -26,7 +26,8 @@ def prompt_for_user_token(username, scope=None): export CLIENT_SECRET='your-spotify-client-secret' export REDIRECT_URI='your-app-redirect-url' - Get your credentials at https://developer.spotify.com/my-applications + Get your credentials at + https://developer.spotify.com/my-applications ''' sys.exit(1) @@ -40,12 +41,24 @@ def prompt_for_user_token(username, scope=None): token_info = sp_oauth.get_cached_token() if not token_info: + print ''' + + User authentication requires interaction with your + web browser. Once you enter your credentials and + give authorization, you will be redirected to + a url. Paste that url you were directed to to + complete the authorization. + + ''' auth_url = sp_oauth.get_authorize_url() try: subprocess.call(["open", auth_url]) print "Opening %s in your browser" % auth_url except: print "Please navigate here: %s" % auth_url + + print + print response = raw_input("Enter the URL you were redirected to: ") code = sp_oauth.parse_response_code(response) token_info = sp_oauth.get_access_token(code) diff --git a/tests/tests.py b/tests/tests.py index 35c6676..87779a3 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -64,6 +64,15 @@ class TestSpotipy(unittest.TestCase): self.assertTrue('tracks' in results) self.assertTrue(len(results['tracks']) == 10) + def test_artist_related_artists(self): + results = self.spotify.artist_related_artists(self.weezer_urn) + self.assertTrue('artists' in results) + self.assertTrue(len(results['artists']) == 20) + for artist in results['artists']: + if artist['name'] == 'Rivers Cuomo': + found = True + self.assertTrue(found) + def test_artist_search(self): results = self.spotify.search(q='weezer', type='artist') self.assertTrue('artists' in results) @@ -95,7 +104,7 @@ class TestSpotipy(unittest.TestCase): self.assertTrue(results['tracks']['items'][0]['name'] == 'El Scorcho') def test_user(self): - user = self.spotify.user(user_id='plamere') + user = self.spotify.user(user='plamere') self.assertTrue(user['uri'] == 'spotify:user:plamere') def test_track_bad_id(self): @@ -105,5 +114,24 @@ class TestSpotipy(unittest.TestCase): except spotipy.SpotifyException: self.assertTrue(True) +''' + Need tests for: + + - next + - previous + - artist_related_artists + - user_playlists + - user_playlist + - user_playlist_create + - user_playlist_add_tracks + - user_playlist_replace_tracks + - user_playlist_remove_all_occurrences_of_tracks + - user_playlist_remove_specific_occurrences_of_tracks + - me + - current_user + - current_user_saved_tracks_delete + - current_user_saved_tracks_add +''' + if __name__ == '__main__': unittest.main()