diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml
new file mode 100644
index 0000000..43354b0
--- /dev/null
+++ b/.github/workflows/pythonapp.yml
@@ -0,0 +1,23 @@
+name: Python application
+
+on: [push, pull_request]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python 3.7
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.7
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ - name: Lint with flake8
+ run: |
+ pip install flake8
+ flake8 . --count --show-source --statistics
diff --git a/.gitignore b/.gitignore
index 286ddd2..137335b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,5 @@ coverage.xml
# Sphinx documentation
docs/_build/
-
.*
-archive
+archive
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 241902d..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-language: python
-
-python:
- - 2.7
- - 3.6
- - 3.5
- - 3.4
- - pypy
-
-install:
- - pip install coveralls
-
-script:
- - python setup.py install
- - coverage run --include=*spotipy* tests/tests.py
- - for file in examples/*.py; do python $file; done
-
-after_success:
- - coverage report
- - coveralls
- - pip install pep8 pyflakes
- - pep8 *.py tests/*.py
- - pyflakes *.py tests/*.py
-
-matrix:
- allow_failures:
- - python: 3.6
- - python: 3.5
- - python: 3.4
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..28addea
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,121 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [2.6.1] - 2020-01-13
+
+### Fixed
+ - Fixed inconsistent behaviour with some API methods when
+ a full HTTP URL is passed.
+ - Fixed invalid calls to logging warn method
+
+### Removed
+ - `mock` no longer needed for install. Only used in `tox`.
+
+## [2.6.0] - 2020-01-12
+
+### Added
+ - Support for `playlist` to get a playlist without specifying a user
+ - Support for `current_user_saved_albums_delete`
+ - Support for `current_user_saved_albums_contains`
+ - Support for `user_unfollow_artists`
+ - Support for `user_unfollow_users`
+ - Lint with flake8 using Github action
+
+### Changed
+ - Fix typos in doc
+ - Start following [SemVer](https://semver.org) properly
+
+## [2.5.0] - 2020-01-11
+Added follow and player endpoints
+
+## [2.4.4] - 2017-01-04
+Python 3 fix
+
+## [2.4.3] - 2017-01-02
+Fixed proxy issue in standard auth flow
+
+## [2.4.2] - 2017-01-02
+Support getting audio features for a single track
+
+## [2.4.1] - 2017-01-02
+Incorporated proxy support
+
+## [2.4.0] - 2016-12-31
+Incorporated a number of PRs
+
+## [2.3.8] - 2016-03-31
+Added recs, audio features, user top lists
+
+## [2.3.7] - 2015-08-10
+Added current_user_followed_artists
+
+## [2.3.6] - 2015-06-03
+Support for offset/limit with album_tracks API
+
+## [2.3.5] - 2015-04-28
+Fixed bug in auto retry logic
+
+## [2.3.3] - 2015-04-01
+Aadded client credential flow
+
+## [2.3.2] - 2015-03-31
+Added auto retry logic
+
+## [2.3.0] - 2015-01-05
+Added session support added by akx.
+
+## [2.2.0] - 2014-11-15
+Added support for user_playlist_tracks
+
+## [2.1.0] - 2014-10-25
+Added support for new_releases and featured_playlists
+
+## [2.0.2] - 2014-08-25
+Moved to spotipy at pypi
+
+## [1.2.0] - 2014-08-22
+Upgraded APIs and docs to make it be a real library
+
+## [1.310.0] - 2014-08-20
+Added playlist replace and remove methods. Added auth tests. Improved API docs
+
+## [1.301.0] - 2014-08-19
+Upgraded version number to take precedence over previously botched release (sigh)
+
+## [1.50.0] - 2014-08-14
+Refactored util out of examples and into the main package
+
+## [1.49.0] - 2014-07-23
+Support for "Your Music" tracks (add, delete, get), with examples
+
+## [1.45.0] - 2014-07-07
+Support for related artists endpoint. Don't use cache auth codes when scope changes
+
+## [1.44.0] - 2014-07-03
+Added show tracks.py example
+
+## [1.43.0] - 2014-06-27
+Fixed JSON handling issue
+
+## [1.42.0] - 2014-06-19
+Removed dependency on simplejson
+
+## [1.40.0] - 2014-06-12
+Initial public release.
+
+## [1.4.2] - 2014-06-21
+Added support for retrieving starred playlists
+
+## [1.1.0] - 2014-06-17
+Updates to match released API
+
+## [1.1.0] - 2014-05-18
+Repackaged for saner imports
+
+## [1.0.0] - 2017-04-05
+Initial release
diff --git a/CHANGES.txt b/CHANGES.txt
deleted file mode 100644
index b46e2c9..0000000
--- a/CHANGES.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-v1.40, June 12, 2014 -- Initial public release.
-v1.42, June 19, 2014 -- Removed dependency on simplejson
-v1.43, June 27, 2014 -- Fixed JSON handling issue
-v1.44, July 3, 2014 -- Added show_tracks.py exampole
-v1.45, July 7, 2014 -- Support for related artists endpoint. Don't use
- cache auth codes when scope changes
-v1.50, August 14, 2014 -- Refactored util out of examples and into the main
- package
-v2.301, August 19, 2014 -- Upgraded version number to take precedence over
- previously botched release (sigh)
-v2.310, August 20, 2014 -- Added playlist replace and remove methods. Added auth
- tests. Improved API docs
-v2.310, January 5, 2015 -- Added session support
-v2.3.1, March 28, 2015 -- 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
diff --git a/LICENSE.txt b/LICENSE.md
similarity index 100%
rename from LICENSE.txt
rename to LICENSE.md
diff --git a/README.md b/README.md
index 09d3a1f..8a52a79 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
-# Spotipy - a Python client for The Spotify Web API
+# Spotipy
-## Description
+##### A light weight Python library for the Spotify Web API
+
+[](https://spotipy.readthedocs.io/en/latest/?badge=latest)
-Spotipy is a thin client library for the Spotify Web API.
## Documentation
@@ -43,7 +44,7 @@ sp = spotipy.Spotify()
results = sp.search(q='weezer', limit=20)
for i, t in enumerate(results['tracks']['items']):
- print ' ', i, t['name']
+ print(' ', i, t['name'])
```
A full set of examples can be found in the [online documentation](http://spotipy.readthedocs.org/) and in the [Spotipy examples directory](https://github.com/plamere/spotipy/tree/master/examples).
@@ -51,36 +52,4 @@ A full set of examples can be found in the [online documentation](http://spotipy
## Reporting Issues
-If you have suggestions, bugs or other issues specific to this library, file them [here](https://github.com/plamere/spotipy/issues). Or just send me a pull request.
-
-## Version
-
-- 1.0 - 04/05/2014 - Initial release
-- 1.1 - 05/18/2014 - Repackaged for saner imports
-- 1.4.1 - 06/17/2014 - Updates to match released API
-- 1.4.2 - 06/21/2014 - Added support for retrieving starred playlists
-- v1.40, June 12, 2014 -- Initial public release.
-- v1.42, June 19, 2014 -- Removed dependency on simplejson
-- v1.43, June 27, 2014 -- Fixed JSON handling issue
-- v1.44, July 3, 2014 -- Added show tracks.py example
-- 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.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.310, August 20, 2014 -- Added playlist replace and remove methods. Added auth tests. Improved API docs
-- v2.0 - August 22, 2014 -- Upgraded APIs and docs to make it be a real library
-- v2.0.2 - August 25, 2014 -- Moved to spotipy at pypi
-- v2.1.0 - October 25, 2014 -- Added support for new_releases and featured_playlists
-- v2.2.0 - November 15, 2014 -- Added support for user_playlist_tracks
-- v2.3.0 - January 5, 2015 -- Added session support added by akx.
-- v2.3.2 - March 31, 2015 -- Added auto retry logic
-- v2.3.3 - April 1, 2015 -- added client credential flow
-- 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
-- v2.3.7 - August 10, 2015 -- Added current_user_followed_artists
-- v2.3.8 - March 30, 2016 -- Added recs, audio features, user top lists
-- v2.4.0 - December 31, 2016 -- Incorporated a number of PRs
-- v2.4.1 - January 2, 2017 -- Incorporated proxy support
-- v2.4.2 - January 2, 2017 -- support getting audio features for a single track
-- v2.4.3 - January 2, 2017 -- fixed proxy issue in standard auth flow
-- v2.4.4 - January 4, 2017 -- python 3 fix
+If you have suggestions, bugs or other issues specific to this library, file them [here](https://github.com/plamere/spotipy/issues). Or just send me a pull request.
\ No newline at end of file
diff --git a/deploy b/deploy
deleted file mode 100755
index f18302a..0000000
--- a/deploy
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# sudo python setup.py develop --uninstall
-# sudo python setup.py install
-
-# How do deploy
-# - run tests
-# - push to github
-# - Adjust version number in setup.py
-# - Update README.md with updated version info
-# - leave development mode
-# sudo python setup.py develop --uninstall
-# sudo python setup.py install
-# - upload dist
-# sudo python setup.py sdist upload
-# docs should automatically be updated. verify them at
-# http://spotipy.readthedocs.org/en/latest/
-
-sudo python setup.py sdist upload
-
diff --git a/docs/index.rst b/docs/index.rst
index 6283ed8..efb52cf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,7 +5,7 @@ Welcome to Spotipy!
===================================
*Spotipy* is a lightweight Python library for the `Spotify Web API
`_. With *Spotipy*
-you get full access to all of the music data provided by the Spotify platform.
+you get full access to all of the music data provided by the Spotify platform.
Here's a quick example of using *Spotipy* to list the names of all the albums
released by the artist 'Birdy'::
@@ -27,6 +27,7 @@ released by the artist 'Birdy'::
Here's another example showing how to get 30 second samples and cover art
for the top 10 tracks for Led Zeppelin::
+ from __future__ import print_function
import spotipy
lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'
@@ -35,14 +36,15 @@ for the top 10 tracks for Led Zeppelin::
results = spotify.artist_top_tracks(lz_uri)
for track in results['tracks'][:10]:
- print 'track : ' + track['name']
- print 'audio : ' + track['preview_url']
- print 'cover art: ' + track['album']['images'][0]['url']
- print
+ print('track : ' + track['name'])
+ print('audio : ' + track['preview_url'])
+ print('cover art: ' + track['album']['images'][0]['url'])
+ print()
Finally, here's an example that will get the URL for an artist image given the
artist's name::
+ from __future__ import print_function
import spotipy
import sys
@@ -57,7 +59,7 @@ artist's name::
items = results['artists']['items']
if len(items) > 0:
artist = items[0]
- print artist['name'], artist['images'][0]['url']
+ print(artist['name'], artist['images'][0]['url'])
Features
@@ -87,10 +89,11 @@ Non-Authorized requests
For methods that do not require authorization, simply create a Spotify object
and start making method calls like so::
+ from __future__ import print_function
import spotipy
spotify = spotipy.Spotify()
results = spotify.search(q='artist:' + name, type='artist')
- print results
+ print(results)
Authorized requests
=======================
@@ -99,21 +102,23 @@ generate an authorization token that indicates that the user has granted
permission for your application to perform the given task. You will need to
register your app to get the credentials necessary to make authorized calls.
-Even if your script does not have an accessible URL you need to specify one
-when registering your application where the spotify authentication API will
-redirect to after successful login. The URL doesn't need to work or be
-accessible, you can specify "http://localhost/", after successful login you
-just need to copy the "http://localhost/?code=..." URL from your browser
-and paste it to the console where your script is running.
+Even if your script does not have an accessible URL you will need to specify one
+when registering your application which the Spotify authentication server will
+redirect to after successful login. The URL doesn't need to be publicly
+accessible, so you can specify "http://localhost/", and after succesfully
+authenticating your app, you can simply copy the
+"http://localhost/?code=..." URL from your browser and paste it to the
+console where your script is running.
-Register your app at
+Register your app at
`My Applications
-`_.
+`_ and register the
+redirect URI mentioned in the above paragragh.
*spotipy* supports two authorization flows:
- - The **Authorization Code flow** This method is suitable for long-running applications
+ - The **Authorization Code flow** This method is suitable for long-running applications
which the user logs into once. It provides an access token that can be refreshed.
- The **Client Credentials flow** The method makes it possible
@@ -127,25 +132,26 @@ To support the **Authorization Code Flow** *Spotipy* provides a
utility method ``util.prompt_for_user_token`` that will attempt to authorize the
user. You can pass your app credentials directly into the method as arguments::
- util.prompt_for_user_token(username,scope,client_id='your-app-redirect-url',client_secret='your-app-redirect-url',redirect_uri='your-app-redirect-url')
+ util.prompt_for_user_token(username,scope,client_id='your-spotify-client-id',client_secret='your-spotify-client-secret',redirect_uri='your-app-redirect-url')
-or if you are reluctant to immortalize your app credentials in your source code,
+or if you are reluctant to immortalize your app credentials in your source code,
you can set environment variables like so::
export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
-Call ``util.prompt_for_user_token`` method with the username and the
+Call ``util.prompt_for_user_token`` method with the username and the
desired scope (see `Using
Scopes `_ for information
about scopes) and credentials. This will coordinate the user authorization via
-your web browser and ask for the SPOTIPY_REDIRECT_URI you were redirected to
+your web browser and callback to the SPOTIPY_REDIRECT_URI you were redirected to
with the authorization token appended. The credentials are cached locally and
are used to automatically re-authorized expired tokens.
Here's an example of getting user authorization to read a user's saved tracks::
+ from __future__ import print_function
import sys
import spotipy
import spotipy.util as util
@@ -155,7 +161,7 @@ Here's an example of getting user authorization to read a user's saved tracks::
if len(sys.argv) > 1:
username = sys.argv[1]
else:
- print "Usage: %s username" % (sys.argv[0],)
+ print("Usage: %s username" % (sys.argv[0],))
sys.exit()
token = util.prompt_for_user_token(username, scope)
@@ -165,9 +171,9 @@ Here's an example of getting user authorization to read a user's saved tracks::
results = sp.current_user_saved_tracks()
for item in results['items']:
track = item['track']
- print track['name'] + ' - ' + track['artists'][0]['name']
+ print(track['name'] + ' - ' + track['artists'][0]['name'])
else:
- print "Can't get token for", username
+ print("Can't get token for", username)
Client Credentials Flow
=======================
@@ -191,8 +197,8 @@ class SpotifyClientCredentials that can be used to authenticate requests like so
playlists = None
Client credentials flow is appropriate for requests that do not require access to a
-user's private data. Even if you are only making calls that do not require
-authorization, using this flow yields the benefit of a higher rate limit
+user's private data. Even if you are only making calls that do not require
+authorization, using this flow yields the benefit of a higher rate limit
IDs URIs and URLs
=======================
@@ -217,6 +223,7 @@ Here are a few more examples of using *Spotipy*.
Add tracks to a playlist::
+ from __future__ import print_function
import pprint
import sys
@@ -228,7 +235,7 @@ Add tracks to a playlist::
playlist_id = sys.argv[2]
track_ids = sys.argv[3:]
else:
- print "Usage: %s username playlist_id track_id ..." % (sys.argv[0],)
+ print("Usage: %s username playlist_id track_id ..." % (sys.argv[0],))
sys.exit()
scope = 'playlist-modify-public'
@@ -238,15 +245,16 @@ Add tracks to a playlist::
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.user_playlist_add_tracks(username, playlist_id, track_ids)
- print results
+ print(results)
else:
- print "Can't get token for", username
+ print("Can't get token for", username)
Shows the contents of every playlist owned by a user::
# shows a user's playlists (need to be authenticated via oauth)
+ from __future__ import print_function
import sys
import spotipy
import spotipy.util as util
@@ -254,16 +262,16 @@ Shows the contents of every playlist owned by a user::
def show_tracks(tracks):
for i, item in enumerate(tracks['items']):
track = item['track']
- print " %d %32.32s %s" % (i, track['artists'][0]['name'],
- track['name'])
+ print(" %d %32.32s %s" % (i, track['artists'][0]['name'],
+ track['name']))
if __name__ == '__main__':
if len(sys.argv) > 1:
username = sys.argv[1]
else:
- print "Whoops, need your username!"
- print "usage: python user_playlists.py [username]"
+ print("Whoops, need your username!")
+ print("usage: python user_playlists.py [username]")
sys.exit()
token = util.prompt_for_user_token(username)
@@ -273,10 +281,10 @@ Shows the contents of every playlist owned by a user::
playlists = sp.user_playlists(username)
for playlist in playlists['items']:
if playlist['owner']['id'] == username:
- print
- print playlist['name']
- print ' total tracks', playlist['tracks']['total']
- results = sp.user_playlist(username, playlist['id'],
+ print()
+ print(playlist['name'])
+ print (' total tracks', playlist['tracks']['total'])
+ results = sp.user_playlist(username, playlist['id'],
fields="tracks,next")
tracks = results['tracks']
show_tracks(tracks)
@@ -284,7 +292,7 @@ Shows the contents of every playlist owned by a user::
tracks = sp.next(tracks)
show_tracks(tracks)
else:
- print "Can't get token for", username
+ print("Can't get token for", username)
More Examples
@@ -292,7 +300,7 @@ More Examples
There are many more examples of how to use *Spotipy* in the `Examples
Directory `_ on Github
-API Reference
+API Reference
==============
:mod:`client` Module
@@ -330,7 +338,7 @@ You can ask questions about Spotipy on Stack Overflow. Don’t forget to add t
http://stackoverflow.com/questions/ask
-If you think you've found a bug, let us know at
+If you think you've found a bug, let us know at
`Spotify Issues `_
@@ -338,28 +346,30 @@ Contribute
==========
Spotipy authored by Paul Lamere (plamere) with contributions by:
- - Daniel Beaudry // danbeaudry
- - Faruk Emre Sahin // fsahin
- - George // rogueleaderr
- - Henry Greville // sethaurus
- - Hugo // hugovk
- - José Manuel Pérez // JMPerez
- - Lucas Nunno // lnunno
- - Lynn Root // econchick
- - Matt Dennewitz // mattdennewitz
- - Matthew Duck // mattduck
- - Michael Thelin // thelinmichael
- - Ryan Choi // ryankicks
- - Simon Metson // drsm79
+ - Daniel Beaudry // danbeaudry
+ - Faruk Emre Sahin // fsahin
+ - George // rogueleaderr
+ - Henry Greville // sethaurus
+ - Hugo // hugovk
+ - José Manuel Pérez // JMPerez
+ - Lucas Nunno // lnunno
+ - Lynn Root // econchick
+ - Matt Dennewitz // mattdennewitz
+ - Matthew Duck // mattduck
+ - Michael Thelin // thelinmichael
+ - Ryan Choi // ryankicks
+ - Simon Metson // drsm79
- Steve Winton // swinton
- - Tim Balzer // timbalzer
- - corycorycory // corycorycory
+ - Tim Balzer // timbalzer
+ - corycorycory // corycorycory
- Nathan Coleman // nathancoleman
- Michael Birtwell // mbirtwell
+ - Harrison Hayes // Harrison97
+ - Stephane Bruckert // stephanebruckert
License
=======
-https://github.com/plamere/spotipy/blob/master/LICENSE.txt
+https://github.com/plamere/spotipy/blob/master/LICENSE.md
Indices and tables
diff --git a/examples/add_tracks_to_playlist.py b/examples/add_tracks_to_playlist.py
index a5297da..d0df161 100644
--- a/examples/add_tracks_to_playlist.py
+++ b/examples/add_tracks_to_playlist.py
@@ -1,7 +1,6 @@
# Adds tracks to a playlist
-import pprint
import sys
import spotipy
diff --git a/examples/artist_albums.py b/examples/artist_albums.py
index 6b9f6ce..8cb53db 100644
--- a/examples/artist_albums.py
+++ b/examples/artist_albums.py
@@ -4,6 +4,7 @@ import spotipy
''' shows the albums and tracks for a given artist.
'''
+
def get_artist(name):
results = sp.search(q='artist:' + name, type='artist')
items = results['artists']['items']
@@ -12,6 +13,7 @@ def get_artist(name):
else:
return None
+
def show_artist_albums(artist):
albums = []
results = sp.artist_albums(artist['id'], album_type='album')
@@ -19,14 +21,15 @@ def show_artist_albums(artist):
while results['next']:
results = sp.next(results)
albums.extend(results['items'])
- seen = set() # to avoid dups
- albums.sort(key=lambda album:album['name'].lower())
+ seen = set() # to avoid dups
+ albums.sort(key=lambda album: album['name'].lower())
for album in albums:
name = album['name']
if name not in seen:
print((' ' + name))
seen.add(name)
+
if __name__ == '__main__':
sp = spotipy.Spotify()
diff --git a/examples/artist_discography.py b/examples/artist_discography.py
index 34114bb..1f9c837 100644
--- a/examples/artist_discography.py
+++ b/examples/artist_discography.py
@@ -4,6 +4,7 @@ import spotipy
''' shows the albums and tracks for a given artist.
'''
+
def get_artist(name):
results = sp.search(q='artist:' + name, type='artist')
items = results['artists']['items']
@@ -12,6 +13,7 @@ def get_artist(name):
else:
return None
+
def show_album_tracks(album):
tracks = []
results = sp.album_tracks(album['id'])
@@ -24,6 +26,7 @@ def show_album_tracks(album):
print()
print(track)
+
def show_artist_albums(id):
albums = []
results = sp.artist_albums(artist['id'], album_type='album')
@@ -35,17 +38,19 @@ def show_artist_albums(id):
unique = set() # skip duplicate albums
for album in albums:
name = album['name'].lower()
- if not name in unique:
+ if name not in unique:
print(name)
unique.add(name)
show_album_tracks(album)
+
def show_artist(artist):
print('====', artist['name'], '====')
print('Popularity: ', artist['popularity'])
if len(artist['genres']) > 0:
print('Genres: ', ','.join(artist['genres']))
+
if __name__ == '__main__':
sp = spotipy.Spotify()
sp.trace = False
diff --git a/examples/artist_recommendations.py b/examples/artist_recommendations.py
index 6efbc1e..ad0355c 100644
--- a/examples/artist_recommendations.py
+++ b/examples/artist_recommendations.py
@@ -7,7 +7,8 @@ import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-sp.trace=False
+sp.trace = False
+
def get_artist(name):
results = sp.search(q='artist:' + name, type='artist')
@@ -17,11 +18,12 @@ def get_artist(name):
else:
return None
+
def show_recommendations_for_artist(artist):
- albums = []
- results = sp.recommendations(seed_artists = [artist['id']])
+ results = sp.recommendations(seed_artists=[artist['id']])
for track in results['tracks']:
- print track['name'], '-', track['artists'][0]['name']
+ print(track['name'], '-', track['artists'][0]['name'])
+
if __name__ == '__main__':
if len(sys.argv) < 2:
@@ -32,4 +34,4 @@ if __name__ == '__main__':
if artist:
show_recommendations_for_artist(artist)
else:
- print "Can't find that artist", name
+ print("Can't find that artist", name)
diff --git a/examples/audio_analysis_for_track.py b/examples/audio_analysis_for_track.py
index 39e8347..1f728a5 100644
--- a/examples/audio_analysis_for_track.py
+++ b/examples/audio_analysis_for_track.py
@@ -20,4 +20,4 @@ start = time.time()
analysis = sp.audio_analysis(tid)
delta = time.time() - start
print(json.dumps(analysis, indent=4))
-print ("analysis retrieved in %.2f seconds" % (delta,))
+print("analysis retrieved in %.2f seconds" % (delta,))
diff --git a/examples/audio_features.py b/examples/audio_features.py
index 079f4f9..30caddb 100644
--- a/examples/audio_features.py
+++ b/examples/audio_features.py
@@ -11,7 +11,7 @@ import sys
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-sp.trace=False
+sp.trace = False
if len(sys.argv) > 1:
artist_name = ' '.join(sys.argv[1:])
@@ -33,4 +33,4 @@ for feature in features:
analysis = sp._get(feature['analysis_url'])
print(json.dumps(analysis, indent=4))
print()
-print ("features retrieved in %.2f seconds" % (delta,))
+print("features retrieved in %.2f seconds" % (delta,))
diff --git a/examples/audio_features_for_track.py b/examples/audio_features_for_track.py
index cdc980a..9e156d3 100644
--- a/examples/audio_features_for_track.py
+++ b/examples/audio_features_for_track.py
@@ -12,7 +12,7 @@ import sys
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-sp.trace=True
+sp.trace = True
if len(sys.argv) > 1:
tids = sys.argv[1:]
@@ -22,4 +22,4 @@ if len(sys.argv) > 1:
features = sp.audio_features(tids)
delta = time.time() - start
print(json.dumps(features, indent=4))
- print ("features retrieved in %.2f seconds" % (delta,))
+ print("features retrieved in %.2f seconds" % (delta,))
diff --git a/examples/change_playlist_details.py b/examples/change_playlist_details.py
index 621f5d0..b150213 100644
--- a/examples/change_playlist_details.py
+++ b/examples/change_playlist_details.py
@@ -24,8 +24,8 @@ if len(sys.argv) > 3:
description = sys.argv[6]
else:
- print ("Usage: %s username playlist_id name [public collaborative "
- "description]" % (sys.argv[0]))
+ print("Usage: %s username playlist_id name [public collaborative "
+ "description]" % (sys.argv[0]))
sys.exit()
scope = 'playlist-modify-public playlist-modify-private'
@@ -37,6 +37,6 @@ if token:
results = sp.user_playlist_change_details(
username, playlist_id, name=name, public=public,
collaborative=collaborative, description=description)
- print results
+ print(results)
else:
- print "Can't get token for", username
+ print("Can't get token for"), username
diff --git a/examples/contains_a_saved_track.py b/examples/contains_a_saved_track.py
index 2dac2f5..bd130b4 100644
--- a/examples/contains_a_saved_track.py
+++ b/examples/contains_a_saved_track.py
@@ -10,7 +10,7 @@ if len(sys.argv) > 2:
username = sys.argv[1]
tids = sys.argv[2:]
else:
- print("Usage: %s username track-id ..." % (sys.argv[0],))
+ print("Usage: %s username track-id ..." % (sys.argv[0],))
sys.exit()
token = util.prompt_for_user_token(username, scope)
diff --git a/examples/create_playlist.py b/examples/create_playlist.py
index efa334d..0c4caf8 100644
--- a/examples/create_playlist.py
+++ b/examples/create_playlist.py
@@ -2,8 +2,6 @@
import pprint
import sys
-import os
-import subprocess
import spotipy
import spotipy.util as util
@@ -14,16 +12,19 @@ if len(sys.argv) > 2:
playlist_name = sys.argv[2]
playlist_description = sys.argv[3]
else:
- print("Usage: %s username playlist-name playlist-description" % (sys.argv[0],))
+ print(
+ "Usage: %s username playlist-name playlist-description" %
+ (sys.argv[0],))
sys.exit()
-token = util.prompt_for_user_token(username)
+scope = "playlist-modify-public"
+token = util.prompt_for_user_token(username, scope)
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
playlists = sp.user_playlist_create(username, playlist_name,
- playlist_description)
+ description=playlist_description)
pprint.pprint(playlists)
else:
print("Can't get token for", username)
diff --git a/examples/errors.py b/examples/errors.py
index 570600f..217c4e7 100644
--- a/examples/errors.py
+++ b/examples/errors.py
@@ -2,22 +2,18 @@
from __future__ import print_function # (at top of module)
from spotipy.oauth2 import SpotifyClientCredentials
-import json
import spotipy
-import time
-import sys
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-sp.trace=True
+sp.trace = True
try:
- print ('bad call 0')
+ print('bad call 0')
bad_artist_call = sp.artist('spotify:artist:12341234')
except spotipy.client.SpotifyException:
- print ('bad call 0 exception' )
+ print('bad call 0 exception')
-print ('bad call 1')
+print('bad call 1')
bad_artist_call = sp.artist('spotify:artist:12341234')
-print ('bad artist', bad_artist_call)
-
+print('bad artist', bad_artist_call)
diff --git a/examples/my_playlists.py b/examples/my_playlists.py
index 58e02df..bd04b91 100644
--- a/examples/my_playlists.py
+++ b/examples/my_playlists.py
@@ -1,11 +1,9 @@
-# Shows the top artists for a user
+# Shows a user's playlists
-import pprint
import sys
import spotipy
import spotipy.util as util
-import simplejson as json
if len(sys.argv) > 1:
username = sys.argv[1]
@@ -21,6 +19,6 @@ if token:
sp.trace = False
results = sp.current_user_playlists(limit=50)
for i, item in enumerate(results['items']):
- print("%d %s" %(i, item['name']))
+ print("%d %s" % (i, item['name']))
else:
print("Can't get token for", username)
diff --git a/examples/my_top_artists.py b/examples/my_top_artists.py
index 91cbfe2..dc8b873 100644
--- a/examples/my_top_artists.py
+++ b/examples/my_top_artists.py
@@ -1,11 +1,9 @@
# Shows the top artists for a user
-import pprint
import sys
import spotipy
import spotipy.util as util
-import simplejson as json
if len(sys.argv) > 1:
username = sys.argv[1]
@@ -21,10 +19,10 @@ if token:
sp.trace = False
ranges = ['short_term', 'medium_term', 'long_term']
for range in ranges:
- print "range:", range
+ print("range:", range)
results = sp.current_user_top_artists(time_range=range, limit=50)
for i, item in enumerate(results['items']):
- print i, item['name']
- print
+ print(i, item['name'])
+ print()
else:
print("Can't get token for", username)
diff --git a/examples/my_top_tracks.py b/examples/my_top_tracks.py
index 6bd72ec..29782c8 100644
--- a/examples/my_top_tracks.py
+++ b/examples/my_top_tracks.py
@@ -1,11 +1,9 @@
-# Adds tracks to a playlist
+# Shows the top tracks for a user
-import pprint
import sys
import spotipy
import spotipy.util as util
-import simplejson as json
if len(sys.argv) > 1:
username = sys.argv[1]
@@ -21,11 +19,11 @@ if token:
sp.trace = False
ranges = ['short_term', 'medium_term', 'long_term']
for range in ranges:
- print "range:", range
+ print("range:", range)
results = sp.current_user_top_tracks(time_range=range, limit=50)
for i, item in enumerate(results['items']):
- print i, item['name'], '//', item['artists'][0]['name']
- print
-
+ print(i, item['name'], '//', item['artists'][0]['name'])
+ print()
+
else:
print("Can't get token for", username)
diff --git a/examples/read_a_playlist.py b/examples/read_a_playlist.py
index dbcf984..e38676f 100644
--- a/examples/read_a_playlist.py
+++ b/examples/read_a_playlist.py
@@ -10,4 +10,4 @@ username = uri.split(':')[2]
playlist_id = uri.split(':')[4]
results = sp.user_playlist(username, playlist_id)
-print json.dumps(results, indent=4)
+print(json.dumps(results, indent=4))
diff --git a/examples/remove_specific_tracks_from_playlist.py b/examples/remove_specific_tracks_from_playlist.py
index a99dc94..b2fce66 100644
--- a/examples/remove_specific_tracks_from_playlist.py
+++ b/examples/remove_specific_tracks_from_playlist.py
@@ -1,5 +1,4 @@
-
-# Adds tracks to a playlist
+# removes tracks from a playlist
import pprint
import sys
@@ -11,12 +10,14 @@ if len(sys.argv) > 3:
username = sys.argv[1]
playlist_id = sys.argv[2]
track_ids_and_positions = sys.argv[3:]
- track_ids = [ ]
+ track_ids = []
for t_pos in sys.argv[3:]:
tid, pos = t_pos.split(',')
- track_ids.append( { "uri" : tid, "positions": [ int(pos)] } )
+ track_ids.append({"uri": tid, "positions": [int(pos)]})
else:
- print("Usage: %s username playlist_id track_id,pos track_id,pos ..." % (sys.argv[0],))
+ print(
+ "Usage: %s username playlist_id track_id,pos track_id,pos ..." %
+ (sys.argv[0],))
sys.exit()
scope = 'playlist-modify-public'
@@ -25,7 +26,8 @@ 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)
+ 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
index e9fca7b..c4efa1b 100644
--- a/examples/remove_tracks_from_playlist.py
+++ b/examples/remove_tracks_from_playlist.py
@@ -1,5 +1,4 @@
-
-# Adds tracks to a playlist
+# removes tracks to a playlist
import pprint
import sys
@@ -21,7 +20,8 @@ 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)
+ 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
index 452d328..d7236cc 100644
--- a/examples/replace_tracks_in_playlist.py
+++ b/examples/replace_tracks_in_playlist.py
@@ -1,4 +1,3 @@
-
# Replaces all tracks in a playlist
import pprint
diff --git a/examples/show_album.py b/examples/show_album.py
index e492b3c..acd310f 100644
--- a/examples/show_album.py
+++ b/examples/show_album.py
@@ -2,6 +2,7 @@
# shows album info for a URN or URL
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint
@@ -11,6 +12,7 @@ else:
urn = 'spotify:album:5yTx83u3qerZF7GRJu7eFk'
-sp = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
album = sp.album(urn)
pprint.pprint(album)
diff --git a/examples/show_artist.py b/examples/show_artist.py
index 0180b2a..c0a9834 100644
--- a/examples/show_artist.py
+++ b/examples/show_artist.py
@@ -1,6 +1,7 @@
# shows artist info for a URN or URL
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint
@@ -9,7 +10,8 @@ if len(sys.argv) > 1:
else:
urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
-sp = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
artist = sp.artist(urn)
diff --git a/examples/show_artist_top_tracks.py b/examples/show_artist_top_tracks.py
index a80beb8..dbca651 100644
--- a/examples/show_artist_top_tracks.py
+++ b/examples/show_artist_top_tracks.py
@@ -1,15 +1,16 @@
# shows artist info for a URN or URL
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
-import pprint
if len(sys.argv) > 1:
urn = sys.argv[1]
else:
urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
-sp = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
response = sp.artist_top_tracks(urn)
for track in response['tracks']:
diff --git a/examples/show_featured_playlists.py b/examples/show_featured_playlists.py
index 2c7f8d9..769037c 100644
--- a/examples/show_featured_playlists.py
+++ b/examples/show_featured_playlists.py
@@ -2,7 +2,6 @@
import spotipy
import sys
-import pprint
import spotipy.util as util
if len(sys.argv) > 1:
diff --git a/examples/show_new_releases.py b/examples/show_new_releases.py
index 5205245..d4bd5fa 100644
--- a/examples/show_new_releases.py
+++ b/examples/show_new_releases.py
@@ -2,7 +2,6 @@
import spotipy
import sys
-import pprint
import spotipy.util as util
if len(sys.argv) > 1:
@@ -22,7 +21,7 @@ if token:
while response:
albums = response['albums']
for i, item in enumerate(albums['items']):
- print(albums['offset'] + i,item['name'])
+ print(albums['offset'] + i, item['name'])
if albums['next']:
response = sp.next(albums)
diff --git a/examples/show_related.py b/examples/show_related.py
index b78e1dc..f8e9a65 100644
--- a/examples/show_related.py
+++ b/examples/show_related.py
@@ -2,15 +2,17 @@
# shows related artists for the given seed artist
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
-import pprint
if len(sys.argv) > 1:
artist_name = sys.argv[1]
else:
artist_name = 'weezer'
-sp = spotipy.Spotify()
+
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
result = sp.search(q='artist:' + artist_name, type='artist')
try:
name = result['artists']['items'][0]['name']
@@ -20,6 +22,5 @@ try:
print('Related artists for', name)
for artist in related['artists']:
print(' ', artist['name'])
-except:
+except BaseException:
print("usage show_related.py [artist-name]")
-
diff --git a/examples/show_track_info.py b/examples/show_track_info.py
index b286c0d..def5a9a 100644
--- a/examples/show_track_info.py
+++ b/examples/show_track_info.py
@@ -1,6 +1,7 @@
# shows track info for a URN or URL
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint
@@ -9,6 +10,7 @@ if len(sys.argv) > 1:
else:
urn = 'spotify:track:0Svkvt5I79wficMFgaqEQJ'
-sp = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
track = sp.track(urn)
pprint.pprint(track)
diff --git a/examples/show_tracks.py b/examples/show_tracks.py
index 79a5302..db9c4c5 100644
--- a/examples/show_tracks.py
+++ b/examples/show_tracks.py
@@ -19,6 +19,3 @@ if __name__ == '__main__':
results = sp.tracks(tids[start: start + max_tracks_per_call])
for track in results['tracks']:
print(track['name'] + ' - ' + track['artists'][0]['name'])
-
-
-
diff --git a/examples/show_user.py b/examples/show_user.py
index 07b102f..8ce6fe9 100644
--- a/examples/show_user.py
+++ b/examples/show_user.py
@@ -14,4 +14,3 @@ sp = spotipy.Spotify()
sp.trace = True
user = sp.user(username)
pprint.pprint(user)
-
diff --git a/examples/simple0.py b/examples/simple0.py
index 52540bb..c1ea7d7 100644
--- a/examples/simple0.py
+++ b/examples/simple0.py
@@ -1,5 +1,8 @@
import spotipy
-sp = spotipy.Spotify()
+from spotipy.oauth2 import SpotifyClientCredentials
+
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
results = sp.search(q='weezer', limit=20)
for i, t in enumerate(results['tracks']['items']):
diff --git a/examples/simple1.py b/examples/simple1.py
index 1748b24..92cf482 100644
--- a/examples/simple1.py
+++ b/examples/simple1.py
@@ -1,16 +1,16 @@
import spotipy
-
+from spotipy.oauth2 import SpotifyClientCredentials
birdy_uri = 'spotify:artist:2WX2uTcsvV5OnS0inACecP'
-spotify = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-results = spotify.artist_albums(birdy_uri, album_type='album')
+results = sp.artist_albums(birdy_uri, album_type='album')
albums = results['items']
while results['next']:
- results = spotify.next(results)
+ results = sp.next(results)
albums.extend(results['items'])
for album in albums:
print((album['name']))
-
diff --git a/examples/simple2.py b/examples/simple2.py
index 3b06ddc..79a67e1 100644
--- a/examples/simple2.py
+++ b/examples/simple2.py
@@ -1,12 +1,14 @@
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'
-spotify = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-results = spotify.artist_top_tracks(lz_uri)
+results = sp.artist_top_tracks(lz_uri)
for track in results['tracks'][:10]:
print('track : ' + track['name'])
diff --git a/examples/simple3.py b/examples/simple3.py
index 96f300a..08777f2 100644
--- a/examples/simple3.py
+++ b/examples/simple3.py
@@ -1,16 +1,17 @@
import spotipy
+from spotipy.oauth2 import SpotifyClientCredentials
import sys
-spotify = spotipy.Spotify()
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
if len(sys.argv) > 1:
name = ' '.join(sys.argv[1:])
else:
name = 'Radiohead'
-results = spotify.search(q='artist:' + name, type='artist')
+results = sp.search(q='artist:' + name, type='artist')
items = results['artists']['items']
if len(items) > 0:
artist = items[0]
print(artist['name'], artist['images'][0]['url'])
-
diff --git a/examples/title_chain.py b/examples/title_chain.py
index 67a55c0..6251382 100644
--- a/examples/title_chain.py
+++ b/examples/title_chain.py
@@ -1,6 +1,5 @@
import spotipy
import random
-import simplejson as json
'''
generates a list of songs where the first word in each subsequent song
@@ -14,14 +13,15 @@ skiplist = set(['dm', 'remix'])
max_offset = 500
seen = set()
+
def find_songs_that_start_with_word(word):
max_titles = 20
max_offset = 200
offset = 0
out = []
- while offset < max_offset and len(out) < max_titles:
- results = sp.search(q=word, type = 'track', limit=50, offset = offset)
+ while offset < max_offset and len(out) < max_titles:
+ results = sp.search(q=word, type='track', limit=50, offset=offset)
if len(results['tracks']['items']) == 0:
break
@@ -37,27 +37,29 @@ def find_songs_that_start_with_word(word):
if '/' in name:
continue
words = name.split()
- if len(words) > 1 and words[0] == word and words[-1] not in skiplist:
- #print " ", name, len(out)
+ if len(words) > 1 and words[0] == word \
+ and words[-1] not in skiplist:
+ # print " ", name, len(out)
out.append(item)
offset += 50
- #print "found", len(out), "matches"
+ # print "found", len(out), "matches"
return out
+
def make_chain(word):
which = 1
while True:
songs = find_songs_that_start_with_word(word)
if len(songs) > 0:
song = random.choice(songs)
- print which, song['name'] + " by " + song['artists'][0]['name']
+ print(which, song['name'] + " by " + song['artists'][0]['name'])
which += 1
word = song['name'].lower().split()[-1]
else:
break
+
if __name__ == '__main__':
import sys
title = ' '.join(sys.argv[1:])
make_chain(sys.argv[1].lower())
-
diff --git a/examples/user_playlists.py b/examples/user_playlists.py
index 7bcc1f5..e94e810 100644
--- a/examples/user_playlists.py
+++ b/examples/user_playlists.py
@@ -1,9 +1,6 @@
# shows a user's playlists (need to be authenticated via oauth)
-import pprint
import sys
-import os
-import subprocess
import spotipy
diff --git a/examples/user_playlists_contents.py b/examples/user_playlists_contents.py
index 5d49bb9..b6cb7d2 100644
--- a/examples/user_playlists_contents.py
+++ b/examples/user_playlists_contents.py
@@ -4,10 +4,13 @@ import sys
import spotipy
import spotipy.util as util
+
def show_tracks(results):
for i, item in enumerate(results['items']):
track = item['track']
- print(" %d %32.32s %s" % (i, track['artists'][0]['name'], track['name']))
+ print(
+ " %d %32.32s %s" %
+ (i, track['artists'][0]['name'], track['name']))
if __name__ == '__main__':
@@ -28,7 +31,8 @@ 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.user_playlist(
+ username, playlist['id'], fields="tracks,next")
tracks = results['tracks']
show_tracks(tracks)
while tracks['next']:
@@ -36,4 +40,3 @@ if __name__ == '__main__':
show_tracks(tracks)
else:
print("Can't get token for", username)
-
diff --git a/examples/user_public_playlists.py b/examples/user_public_playlists.py
index 3557fc3..2d7f64f 100644
--- a/examples/user_public_playlists.py
+++ b/examples/user_public_playlists.py
@@ -1,6 +1,6 @@
# Gets all the public playlists for the given
# user. Uses Client Credentials flow
-#
+#
import sys
import spotipy
@@ -18,7 +18,13 @@ playlists = sp.user_playlists(user)
while playlists:
for i, playlist in enumerate(playlists['items']):
- print("%4d %s %s" % (i + 1 + playlists['offset'], playlist['uri'], playlist['name']))
+ print(
+ "%4d %s %s" %
+ (i +
+ 1 +
+ playlists['offset'],
+ playlist['uri'],
+ playlist['name']))
if playlists['next']:
playlists = sp.next(playlists)
else:
diff --git a/examples/user_saved_albums_delete.py b/examples/user_saved_albums_delete.py
new file mode 100644
index 0000000..783c33e
--- /dev/null
+++ b/examples/user_saved_albums_delete.py
@@ -0,0 +1,28 @@
+"""
+ Deletes user saved album
+
+"""
+
+import sys
+import spotipy
+import spotipy.util as util
+
+if len(sys.argv) > 1:
+ username = sys.argv[1]
+else:
+ print("Usage: %s username" % (sys.argv[0],))
+ sys.exit()
+
+scope = 'user-library-modify'
+token = util.prompt_for_user_token(username, scope)
+
+if token:
+ sp = spotipy.Spotify(auth=token)
+ sp.trace = False
+ uris = input("input a list of album URIs, URLs or IDs: ")
+ uris = list(map(str, uris.split()))
+ deleted = sp.current_user_saved_albums_delete(uris)
+ print("Deletion successful.")
+
+else:
+ print("Can't get token for", username)
diff --git a/examples/user_starred_playlist.py b/examples/user_starred_playlist.py
index 914344a..4d51456 100644
--- a/examples/user_starred_playlist.py
+++ b/examples/user_starred_playlist.py
@@ -5,7 +5,6 @@ import spotipy
import spotipy.util as util
-
if len(sys.argv) > 1:
username = sys.argv[1]
else:
@@ -23,7 +22,7 @@ if token:
while tracks:
for item in tracks['items']:
track = item['track']
- print(which, track['name' ], ' --', track['artists'][0]['name'])
+ print(which, track['name'], ' --', track['artists'][0]['name'])
which += 1
tracks = sp.next(tracks)
diff --git a/redirect_page.md b/redirect_page.md
deleted file mode 100644
index 54d38f8..0000000
--- a/redirect_page.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Spotipy Authorization Page
-===========================
-
-If you are here, you are probably running a Spotipy test app that has been asked to authenticate the user.
-Now you just need to cut/paste the URL into the terminal at the prompt asking:
-
- Enter the URL you were redirected to:
-
-
-And you should be good to go
diff --git a/requirements.txt b/requirements.txt
index 47f25d8..708dccd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
+mock==2.0.0
requests==2.3.0
six==1.10.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 7b81605..b1929c7 100644
--- a/setup.py
+++ b/setup.py
@@ -1,9 +1,12 @@
from setuptools import setup
+desc = """### A light weight Python library for the Spotify Web API"""
+
setup(
name='spotipy',
- version='2.4.4',
- description='simple client for the Spotify Web API',
+ version='2.6.1',
+ long_description=desc,
+ long_description_content_type='text/markdown',
author="@plamere",
author_email="paul@echonest.com",
url='http://spotipy.readthedocs.org/',
@@ -11,5 +14,5 @@ setup(
'requests>=2.3.0',
'six>=1.10.0',
],
- license='LICENSE.txt',
+ license='LICENSE.md',
packages=['spotipy'])
diff --git a/spotipy/__init__.py b/spotipy/__init__.py
index 9be1dce..dd9eb13 100644
--- a/spotipy/__init__.py
+++ b/spotipy/__init__.py
@@ -1,2 +1,3 @@
-VERSION='2.0.1'
-from .client import Spotify, SpotifyException
+from .client import * # noqa
+from .oauth2 import * # noqa
+from .util import * # noqa
diff --git a/spotipy/client.py b/spotipy/client.py
index 06858b9..e79bfca 100644
--- a/spotipy/client.py
+++ b/spotipy/client.py
@@ -1,17 +1,21 @@
-# coding: utf-8
+# -*- coding: utf-8 -*-
+""" A simple and thin Python library for the Spotify Web API """
from __future__ import print_function
-import sys
-import requests
+
+__all__ = [
+ 'Spotify',
+ 'SpotifyException'
+]
+
import json
+import sys
import time
+import requests
import six
-""" A simple and thin Python library for the Spotify Web API
-"""
-
class SpotifyException(Exception):
def __init__(self, http_status, code, msg, headers=None):
@@ -53,9 +57,10 @@ class Spotify(object):
max_get_retries = 10
def __init__(self, auth=None, requests_session=True,
- client_credentials_manager=None, proxies=None, requests_timeout=None):
+ client_credentials_manager=None, proxies=None,
+ requests_timeout=None):
"""
- Create a Spotify API object.
+ Creates a Spotify API client.
:param auth: An authorization token (optional)
:param requests_session:
@@ -68,7 +73,8 @@ class Spotify(object):
:param proxies:
Definition of proxies (optional)
:param requests_timeout:
- Tell Requests to stop waiting for a response after a given number of seconds
+ Tell Requests to stop waiting for a response after a given
+ number of seconds
"""
self.prefix = 'https://api.spotify.com/v1/'
self._auth = auth
@@ -108,34 +114,36 @@ class Spotify(object):
if self.trace_out:
print(url)
- with self._session.request(method, url, headers=headers, proxies=self.proxies, **args) as r:
+ with self._session.request(method, url, headers=headers,
+ proxies=self.proxies, **args) as r:
if self.trace: # pragma: no cover
print()
- print ('request headers', headers)
- print ('response headers', r.headers)
- print ('http status', r.status_code)
+ print('Request headers:', headers)
+ print('Response headers:', r.headers)
+ print('HTTP status', r.status_code)
print(method, r.url)
if payload:
- print("DATA", json.dumps(payload))
+ print("Data", json.dumps(payload))
try:
r.raise_for_status()
- except:
+ except BaseException:
try:
msg = r.json()['error']['message']
- except:
+ except BaseException:
msg = 'error'
raise SpotifyException(r.status_code,
- -1, '%s:\n %s' % (r.url, msg), headers=r.headers)
+ -1, '%s:\n %s' % (r.url, msg),
+ headers=r.headers)
try:
results = r.json()
- except:
+ except BaseException:
results = None
if self.trace: # pragma: no cover
- print('RESP', results)
+ print('Response:', results)
print()
return results
@@ -155,21 +163,22 @@ class Spotify(object):
if retries < 0:
raise
else:
- sleep_seconds = int(e.headers.get('Retry-After', delay))
- print ('retrying ...' + str(sleep_seconds) + 'secs')
+ sleep_seconds = int(
+ e.headers.get('Retry-After', delay))
+ print('retrying ...' + str(sleep_seconds) + 'secs')
time.sleep(sleep_seconds + 1)
delay += 1
else:
raise
except Exception as e:
raise
- print ('exception', str(e))
+ print('exception', str(e))
# some other exception. Requests have
# been know to throw a BadStatusLine exception
retries -= 1
if retries >= 0:
sleep_seconds = int(e.headers.get('Retry-After', delay))
- print ('retrying ...' + str(delay) + 'secs')
+ print('retrying ...' + str(delay) + 'secs')
time.sleep(sleep_seconds + 1)
delay += 1
else:
@@ -228,7 +237,7 @@ class Spotify(object):
trid = self._get_id('track', track_id)
return self._get('tracks/' + trid)
- def tracks(self, tracks, market = None):
+ def tracks(self, tracks, market=None):
""" returns a list of tracks given a list of track IDs, URIs, or URLs
Parameters:
@@ -237,7 +246,7 @@ class Spotify(object):
"""
tlist = [self._get_id('track', t) for t in tracks]
- return self._get('tracks/?ids=' + ','.join(tlist), market = market)
+ return self._get('tracks/?ids=' + ','.join(tlist), market=market)
def artist(self, artist_id):
""" returns a single artist given the artist's ID, URI or URL
@@ -340,9 +349,11 @@ class Spotify(object):
- offset - the index of the first item to return
- type - the type of item to return. One of 'artist', 'album',
'track' or 'playlist'
- - market - An ISO 3166-1 alpha-2 country code or the string from_token.
+ - market - An ISO 3166-1 alpha-2 country code or the string
+ from_token.
"""
- return self._get('search', q=q, limit=limit, offset=offset, type=type, market=market)
+ return self._get('search', q=q, limit=limit,
+ offset=offset, type=type, market=market)
def user(self, user):
""" Gets basic profile information about a Spotify User
@@ -383,6 +394,18 @@ class Spotify(object):
plid = self._get_id('playlist', playlist_id)
return self._get("users/%s/playlists/%s" % (user, plid), fields=fields)
+ 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,
limit=100, offset=0, market=None):
""" Get full details of the tracks of a playlist owned by a user.
@@ -400,7 +423,6 @@ class Spotify(object):
limit=limit, offset=offset, fields=fields,
market=market)
-
def user_playlist_create(self, user, name, public=True, description=''):
""" Creates a playlist for a user
@@ -412,7 +434,6 @@ class Spotify(object):
"""
data = {'name': name, 'public': public, 'description': description}
-
return self._post("users/%s/playlists" % (user,), payload=data)
def user_playlist_change_details(
@@ -448,7 +469,8 @@ 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))
+ return self._delete("users/%s/playlists/%s/followers" %
+ (user, playlist_id))
def user_playlist_add_tracks(self, user, playlist_id, tracks,
position=None):
@@ -488,8 +510,10 @@ class Spotify(object):
- user - the id of the user
- 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
+ - 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)
@@ -508,7 +532,7 @@ class Spotify(object):
Parameters:
- user - the id of the user
- 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 remove from the playlist
- snapshot_id - optional id of the playlist snapshot
"""
@@ -528,9 +552,11 @@ class Spotify(object):
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":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] },
- { "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
+ - 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":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ]
- snapshot_id - optional id of the playlist snapshot
"""
@@ -556,19 +582,26 @@ class Spotify(object):
- playlist_id - the id of the playlist
"""
- return self._put("users/{}/playlists/{}/followers".format(playlist_owner_id, playlist_id))
+ return self._put(
+ "users/{}/playlists/{}/followers".format(playlist_owner_id,
+ playlist_id))
- def user_playlist_is_following(self, playlist_owner_id, playlist_id, user_ids):
+ def user_playlist_is_following(
+ self, playlist_owner_id, playlist_id, user_ids):
"""
Check to see if the given users are following the given playlist
Parameters:
- playlist_owner_id - the user id of the playlist owner
- 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.
+ - user_ids - the ids of the users that you want to check to see
+ if they follow the playlist. Maximum: 5 ids.
"""
- return self._get("users/{}/playlists/{}/followers/contains?ids={}".format(playlist_owner_id, playlist_id, ','.join(user_ids)))
+ endpoint = "users/{}/playlists/{}/followers/contains?ids={}"
+ return self._get(endpoint.format(playlist_owner_id,
+ playlist_id,
+ ','.join(user_ids)))
def me(self):
""" Get detailed profile information about the current user.
@@ -587,17 +620,6 @@ class Spotify(object):
'''
return self._get('me/player/currently-playing')
- def current_user_saved_albums(self, limit=20, offset=0):
- """ Gets a list of the albums saved in the current authorized user's
- "Your Music" library
-
- Parameters:
- - limit - the number of albums to return
- - offset - the index of the first album to return
-
- """
- return self._get('me/albums', limit=limit, offset=offset)
-
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
@@ -613,8 +635,9 @@ class Spotify(object):
""" Gets a list of the artists followed by the current authorized user
Parameters:
- - limit - the number of tracks to return
- - after - ghe last artist ID retrieved from the previous request
+ - limit - the number of artists to return
+ - after - the last artist ID retrieved from the previous
+ request
"""
return self._get('me/following', type='artist', limit=limit,
@@ -687,9 +710,30 @@ class Spotify(object):
Parameters:
- limit - the number of entities to return
- '''
+ '''
return self._get('me/player/recently-played', limit=limit)
+ def current_user_saved_albums(self, limit=20, offset=0):
+ """ Gets a list of the albums saved in the current authorized user's
+ "Your Music" library
+
+ Parameters:
+ - limit - the number of albums to return
+ - offset - the index of the first album to return
+
+ """
+ return self._get('me/albums', limit=limit, offset=offset)
+
+ def current_user_saved_albums_contains(self, albums=[]):
+ """ Check if one or more albums is already saved in
+ the current Spotify user’s “Your Music” library.
+
+ Parameters:
+ - albums - a list of album URIs, URLs or IDs
+ """
+ alist = [self._get_id('album', a) for a in albums]
+ return self._get('me/albums/contains?ids=' + ','.join(alist))
+
def current_user_saved_albums_add(self, albums=[]):
""" Add one or more albums to the current user's
"Your Music" library.
@@ -697,8 +741,17 @@ class Spotify(object):
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id('album', a) for a in albums]
- r = self._put('me/albums?ids=' + ','.join(alist))
- return r
+ return self._put('me/albums?ids=' + ','.join(alist))
+
+ def current_user_saved_albums_delete(self, albums=[]):
+ """ Remove one or more albums from the current user's
+ "Your Music" library.
+
+ Parameters:
+ - albums - a list of album URIs, URLs or IDs
+ """
+ alist = [self._get_id('album', a) for a in albums]
+ return self._delete('me/albums/?ids=' + ','.join(alist))
def user_follow_artists(self, ids=[]):
''' Follow one or more artists
@@ -714,6 +767,20 @@ class Spotify(object):
'''
return self._put('me/following?type=user&ids=' + ','.join(ids))
+ def user_unfollow_artists(self, ids=[]):
+ ''' Unfollow one or more artists
+ Parameters:
+ - ids - a list of artist IDs
+ '''
+ return self._delete('me/following?type=artist&ids=' + ','.join(ids))
+
+ def user_unfollow_users(self, ids=[]):
+ ''' Unfollow one or more users
+ Parameters:
+ - ids - a list of user IDs
+ '''
+ return self._delete('me/following?type=user&ids=' + ','.join(ids))
+
def featured_playlists(self, locale=None, country=None, timestamp=None,
limit=20, offset=0):
""" Get a list of Spotify featured playlists
@@ -802,20 +869,21 @@ class Spotify(object):
Parameters:
- seed_artists - a list of artist IDs, URIs or URLs
- - seed_tracks - a list of artist IDs, URIs or URLs
+ - seed_tracks - a list of track IDs, URIs or URLs
- seed_genres - a list of genre names. Available genres for
- recommendations can be found by calling recommendation_genre_seeds
+ recommendations can be found by calling
+ recommendation_genre_seeds
- - country - An ISO 3166-1 alpha-2 country code. If provided, all
- results will be playable in this country.
+ - country - An ISO 3166-1 alpha-2 country code. If provided,
+ all results will be playable in this country.
- limit - The maximum number of items to return. Default: 20.
- Minimum: 1. Maximum: 100
+ Minimum: 1. Maximum: 100
- - min/max/target_ - For the tuneable track attributes listed
- in the documentation, these values provide filters and targeting on
- results.
+ - min/max/target_ - For the tuneable track
+ attributes listed in the documentation, these values
+ provide filters and targeting on results.
"""
params = dict(limit=limit)
if seed_artists:
@@ -870,36 +938,28 @@ class Spotify(object):
else:
return results
- def audio_analysis(self, id):
- """ Get audio analysis for a track based upon its Spotify ID
- Parameters:
- - id - a track URIs, URLs or IDs
- """
- id = self._get_id('track', id)
- return self._get('audio-analysis/'+id)
-
def devices(self):
''' Get a list of user's available devices.
'''
return self._get("me/player/devices")
- def current_playback(self, market = None):
+ def current_playback(self, market=None):
''' Get information about user's current playback.
Parameters:
- market - an ISO 3166-1 alpha-2 country code.
'''
- return self._get("me/player", market = market)
+ return self._get("me/player", market=market)
- def currently_playing(self, market = None):
+ def currently_playing(self, market=None):
''' Get user's currently playing track.
Parameters:
- market - an ISO 3166-1 alpha-2 country code.
'''
- return self._get("me/player/currently-playing", market = market)
+ return self._get("me/player/currently-playing", market=market)
- def transfer_playback(self, device_id, force_play = True):
+ def transfer_playback(self, device_id, force_play=True):
''' Transfer playback to another device.
Note that the API accepts a list of device ids, but only
actually supports one.
@@ -915,7 +975,8 @@ class Spotify(object):
}
return self._put("me/player", payload=data)
- def start_playback(self, device_id = None, context_uri = None, uris = None, offset = None):
+ def start_playback(self, device_id=None,
+ context_uri=None, uris=None, offset=None):
''' Start or resume user's playback.
Provide a `context_uri` to start playback or a album,
@@ -946,9 +1007,10 @@ class Spotify(object):
data['uris'] = uris
if offset is not None:
data['offset'] = offset
- return self._put(self._append_device_id("me/player/play", device_id), payload=data)
+ return self._put(self._append_device_id(
+ "me/player/play", device_id), payload=data)
- def pause_playback(self, device_id = None):
+ def pause_playback(self, device_id=None):
''' Pause user's playback.
Parameters:
@@ -956,7 +1018,7 @@ class Spotify(object):
'''
return self._put(self._append_device_id("me/player/pause", device_id))
- def next_track(self, device_id = None):
+ def next_track(self, device_id=None):
''' Skip user's playback to next track.
Parameters:
@@ -964,15 +1026,16 @@ class Spotify(object):
'''
return self._post(self._append_device_id("me/player/next", device_id))
- def previous_track(self, device_id = None):
+ def previous_track(self, device_id=None):
''' Skip user's playback to previous track.
Parameters:
- device_id - device target for playback
'''
- return self._post(self._append_device_id("me/player/previous", device_id))
+ return self._post(self._append_device_id(
+ "me/player/previous", device_id))
- def seek_track(self, position_ms, device_id = None):
+ def seek_track(self, position_ms, device_id=None):
''' Seek to position in current track.
Parameters:
@@ -982,9 +1045,10 @@ class Spotify(object):
if not isinstance(position_ms, int):
self._warn('position_ms must be an integer')
return
- return self._put(self._append_device_id("me/player/seek?position_ms=%s" % position_ms, device_id))
+ return self._put(self._append_device_id(
+ "me/player/seek?position_ms=%s" % position_ms, device_id))
- def repeat(self, state, device_id = None):
+ def repeat(self, state, device_id=None):
''' Set repeat mode for playback.
Parameters:
@@ -994,9 +1058,12 @@ class Spotify(object):
if state not in ['track', 'context', 'off']:
self._warn('invalid state')
return
- self._put(self._append_device_id("me/player/repeat?state=%s" % state, device_id))
+ self._put(
+ self._append_device_id(
+ "me/player/repeat?state=%s" %
+ state, device_id))
- def volume(self, volume_percent, device_id = None):
+ def volume(self, volume_percent, device_id=None):
''' Set playback volume.
Parameters:
@@ -1009,9 +1076,12 @@ class Spotify(object):
if volume_percent < 0 or volume_percent > 100:
self._warn('volume must be between 0 and 100, inclusive')
return
- self._put(self._append_device_id("me/player/volume?volume_percent=%s" % volume_percent, device_id))
+ self._put(
+ self._append_device_id(
+ "me/player/volume?volume_percent=%s" %
+ volume_percent, device_id))
- def shuffle(self, state, device_id = None):
+ def shuffle(self, state, device_id=None):
''' Toggle playback shuffling.
Parameters:
@@ -1022,7 +1092,10 @@ class Spotify(object):
self._warn('state must be a boolean')
return
state = str(state).lower()
- self._put(self._append_device_id("me/player/shuffle?state=%s" % state, device_id))
+ self._put(
+ self._append_device_id(
+ "me/player/shuffle?state=%s" %
+ state, device_id))
def _append_device_id(self, path, device_id):
''' Append device ID to API path.
@@ -1041,16 +1114,16 @@ class Spotify(object):
fields = id.split(':')
if len(fields) >= 3:
if type != fields[-2]:
- self._warn('expected id of type %s but found type %s %s',
- type, fields[-2], id)
+ self._warn('expected id of type %s but found type %s %s' %
+ (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 %s but found type %s %s',
- type, itype, id)
- return fields[-1]
+ self._warn('expected id of type %s but found type %s %s' %
+ (type, itype, id))
+ return fields[-1].split('?')[0]
return id
def _get_uri(self, type, id):
diff --git a/spotipy/oauth2.py b/spotipy/oauth2.py
index cef7908..f749974 100644
--- a/spotipy/oauth2.py
+++ b/spotipy/oauth2.py
@@ -1,11 +1,21 @@
+# -*- coding: utf-8 -*-
from __future__ import print_function
+
+__all__ = [
+ 'is_token_expired',
+ 'SpotifyClientCredentials',
+ 'SpotifyOAuth',
+ 'SpotifyOauthError'
+]
+
import base64
-import requests
-import os
import json
-import time
+import os
import sys
+import time
+
+import requests
# Workaround to support both python 2 & 3
import six
@@ -17,7 +27,11 @@ class SpotifyOauthError(Exception):
def _make_authorization_headers(client_id, client_secret):
- auth_header = base64.b64encode(six.text_type(client_id + ':' + client_secret).encode('ascii'))
+ auth_header = base64.b64encode(
+ six.text_type(
+ client_id +
+ ':' +
+ client_secret).encode('ascii'))
return {'Authorization': 'Basic %s' % auth_header.decode('ascii')}
@@ -31,7 +45,7 @@ class SpotifyClientCredentials(object):
def __init__(self, client_id=None, client_secret=None, proxies=None):
"""
- You can either provid a client_id and client_secret to the
+ You can either provide a client_id and client_secret to the
constructor or set SPOTIPY_CLIENT_ID and SPOTIPY_CLIENT_SECRET
environment variables
"""
@@ -67,12 +81,14 @@ class SpotifyClientCredentials(object):
def _request_access_token(self):
"""Gets client credentials access token """
- payload = { 'grant_type': 'client_credentials'}
+ payload = {'grant_type': 'client_credentials'}
- headers = _make_authorization_headers(self.client_id, self.client_secret)
+ headers = _make_authorization_headers(
+ self.client_id, self.client_secret)
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
- headers=headers, verify=True, proxies=self.proxies)
+ headers=headers, verify=True,
+ proxies=self.proxies)
if response.status_code != 200:
raise SpotifyOauthError(response.reason)
token_info = response.json()
@@ -99,7 +115,7 @@ class SpotifyOAuth(object):
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, proxies=None):
+ state=None, scope=None, cache_path=None, proxies=None):
'''
Creates a SpotifyOAuth object
@@ -115,9 +131,9 @@ class SpotifyOAuth(object):
self.client_id = client_id
self.client_secret = client_secret
self.redirect_uri = redirect_uri
- self.state=state
+ self.state = state
self.cache_path = cache_path
- self.scope=self._normalize_scope(scope)
+ self.scope = self._normalize_scope(scope)
self.proxies = proxies
def get_cached_token(self):
@@ -132,11 +148,13 @@ class SpotifyOAuth(object):
token_info = json.loads(token_info_string)
# if scopes don't match, then bail
- if 'scope' not in token_info or not self._is_scope_subset(self.scope, token_info['scope']):
+ if 'scope' not in token_info or not self._is_scope_subset(
+ self.scope, token_info['scope']):
return None
if self.is_token_expired(token_info):
- token_info = self.refresh_access_token(token_info['refresh_token'])
+ token_info = self.refresh_access_token(
+ token_info['refresh_token'])
except IOError:
pass
@@ -154,7 +172,8 @@ class SpotifyOAuth(object):
def _is_scope_subset(self, needle_scope, haystack_scope):
needle_scope = set(needle_scope.split()) if needle_scope else set()
- haystack_scope = set(haystack_scope.split()) if haystack_scope else set()
+ haystack_scope = set(
+ haystack_scope.split()) if haystack_scope else set()
return needle_scope <= haystack_scope
def is_token_expired(self, token_info):
@@ -212,7 +231,8 @@ class SpotifyOAuth(object):
headers = self._make_authorization_headers()
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
- headers=headers, verify=True, proxies=self.proxies)
+ headers=headers, verify=True,
+ proxies=self.proxies)
if response.status_code != 200:
raise SpotifyOauthError(response.reason)
token_info = response.json()
@@ -222,30 +242,29 @@ class SpotifyOAuth(object):
def _normalize_scope(self, scope):
if scope:
- scopes = scope.split()
- scopes.sort()
+ scopes = sorted(scope.split())
return ' '.join(scopes)
else:
return None
def refresh_access_token(self, refresh_token):
- payload = { 'refresh_token': refresh_token,
+ payload = {'refresh_token': refresh_token,
'grant_type': 'refresh_token'}
headers = self._make_authorization_headers()
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
- headers=headers, proxies=self.proxies)
+ headers=headers, proxies=self.proxies)
if response.status_code != 200:
if False: # debugging code
print('headers', headers)
print('request', response.url)
- self._warn("couldn't refresh token: code:%d reason:%s" \
- % (response.status_code, response.reason))
+ self._warn("couldn't refresh token: code:%d reason:%s"
+ % (response.status_code, response.reason))
return None
token_info = response.json()
token_info = self._add_custom_values_to_token_info(token_info)
- if not 'refresh_token' in token_info:
+ if 'refresh_token' not in token_info:
token_info['refresh_token'] = refresh_token
self._save_token_info(token_info)
return token_info
@@ -261,4 +280,3 @@ class SpotifyOAuth(object):
def _warn(self, msg):
print('warning:' + msg, file=sys.stderr)
-
diff --git a/spotipy/util.py b/spotipy/util.py
index 0dd39a9..d8b7585 100644
--- a/spotipy/util.py
+++ b/spotipy/util.py
@@ -1,15 +1,33 @@
+# -*- coding: utf-8 -*-
-# shows a user's playlists (need to be authenticated via oauth)
+""" Shows a user's playlists (need to be authenticated via oauth) """
from __future__ import print_function
+
+__all__ = [
+ 'CLIENT_CREDS_ENV_VARS',
+ 'prompt_for_user_token'
+]
+
import os
+
from . import oauth2
+
import spotipy
-def prompt_for_user_token(username, scope=None, client_id = None,
- client_secret = None, redirect_uri = None, cache_path = None):
+CLIENT_CREDS_ENV_VARS = {
+ 'client_id': 'SPOTIPY_CLIENT_ID',
+ 'client_secret': 'SPOTIPY_CLIENT_SECRET',
+ 'client_username': 'SPOTIPY_CLIENT_USERNAME',
+ 'redirect_uri': 'SPOTIPY_REDIRECT_URI'
+}
+
+
+def prompt_for_user_token(username, scope=None, client_id=None,
+ client_secret=None, redirect_uri=None,
+ cache_path=None):
''' prompts the user to login if necessary and returns
- the user token suitable for use with the spotipy.Spotify
+ the user token suitable for use with the spotipy.Spotify
constructor
Parameters:
@@ -41,14 +59,14 @@ def prompt_for_user_token(username, scope=None, client_id = None,
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
- Get your credentials at
+ Get your credentials at
https://developer.spotify.com/my-applications
''')
raise spotipy.SpotifyException(550, -1, 'no credentials set')
cache_path = cache_path or ".cache-" + username
- sp_oauth = oauth2.SpotifyOAuth(client_id, client_secret, redirect_uri,
- scope=scope, cache_path=cache_path)
+ sp_oauth = oauth2.SpotifyOAuth(client_id, client_secret, redirect_uri,
+ scope=scope, cache_path=cache_path)
# try to get a valid token for this user, from the cache,
# if not in the cache, the create a new (this will send
@@ -71,7 +89,7 @@ def prompt_for_user_token(username, scope=None, client_id = None,
import webbrowser
webbrowser.open(auth_url)
print("Opened %s in your browser" % auth_url)
- except:
+ except BaseException:
print("Please navigate here: %s" % auth_url)
print()
@@ -82,7 +100,7 @@ def prompt_for_user_token(username, scope=None, client_id = None,
response = input("Enter the URL you were redirected to: ")
print()
- print()
+ print()
code = sp_oauth.parse_response_code(response)
token_info = sp_oauth.get_access_token(code)
diff --git a/tests/authtests.py b/tests/authtests.py
deleted file mode 100644
index 13504a6..0000000
--- a/tests/authtests.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# -*- coding: latin-1 -*-
-
-import spotipy
-from spotipy import util
-import unittest
-import pprint
-import sys
-import simplejson as json
-
-'''
- Since these tests require authentication they are maintained
- separately from the other tests.
-
- These tests try to be benign and leave your collection and
- playlists in a relatively stable state.
-'''
-
-class AuthTestSpotipy(unittest.TestCase):
- '''
- These tests require user authentication
- '''
-
- playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
- four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
- "spotify:track:7IHOIqZUUInxjVkko181PB",
- "4VrWlk8IQxevMvERoX08iC",
- "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"]
-
- two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
- "spotify:track:7IHOIqZUUInxjVkko181PB"]
-
- other_tracks=["spotify:track:2wySlB6vMzCbQrRnNGOYKa",
- "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
- "spotify:track:1PB7gRWcvefzu7t3LJLUlf"]
-
- bad_id = 'BAD_ID'
-
- def test_track_bad_id(self):
- try:
- track = spotify.track(self.bad_id)
- self.assertTrue(False)
- except spotipy.SpotifyException:
- self.assertTrue(True)
-
-
- def test_basic_user_profile(self):
- user = spotify.user(username)
- self.assertTrue(user['id'] == username)
-
- def test_current_user(self):
- user = spotify.current_user()
- self.assertTrue(user['id'] == username)
-
- def test_me(self):
- user = spotify.me()
- self.assertTrue(user['id'] == username)
-
- def test_user_playlists(self):
- playlists = spotify.user_playlists(username, limit=5)
- self.assertTrue('items' in playlists)
- self.assertTrue(len(playlists['items']) == 5)
-
- def test_user_playlist_tracks(self):
- playlists = spotify.user_playlists(username, limit=5)
- self.assertTrue('items' in playlists)
- for playlist in playlists['items']:
- user = playlist['owner']['id']
- pid = playlist['id']
- results = spotify.user_playlist_tracks(user, pid)
- self.assertTrue(len(results['items']) >= 0)
-
- def user_playlist_tracks(self, user, playlist_id = None, fields=None,
- limit=100, offset=0):
-
- # known API issue currently causes this test to fail
- # the issue is that the API doesn't currently respect the
- # limit paramter
-
- self.assertTrue(len(playlists['items']) == 5)
-
- def test_current_user_saved_tracks(self):
- tracks = spotify.current_user_saved_tracks()
- self.assertTrue(len(tracks['items']) > 0)
-
- def test_current_user_saved_albums(self):
- albums = spotify.current_user_saved_albums()
- self.assertTrue(len(albums['items']) > 0)
-
- def test_current_user_playlists(self):
- playlists = spotify.current_user_playlists(limit=10)
- self.assertTrue('items' in playlists)
- self.assertTrue(len(playlists['items']) == 10)
-
- def test_user_playlist_follow(self):
- spotify.user_playlist_follow_playlist('plamere', '4erXB04MxwRAVqcUEpu30O')
- follows = spotify.user_playlist_is_following('plamere', '4erXB04MxwRAVqcUEpu30O', ['plamere'])
-
- self.assertTrue(len(follows) == 1, 'proper follows length')
- self.assertTrue(follows[0], 'is following')
- spotify.user_playlist_unfollow('plamere', '4erXB04MxwRAVqcUEpu30O')
-
- follows = spotify.user_playlist_is_following('plamere', '4erXB04MxwRAVqcUEpu30O', ['plamere'])
- self.assertTrue(len(follows) == 1, 'proper follows length')
- self.assertFalse(follows[0], 'is no longer following')
-
-
- def test_current_user_save_and_unsave_tracks(self):
- tracks = spotify.current_user_saved_tracks()
- total = tracks['total']
-
- spotify.current_user_saved_tracks_add(self.four_tracks)
-
- tracks = spotify.current_user_saved_tracks()
- new_total = tracks['total']
- self.assertTrue(new_total - total == len(self.four_tracks))
-
- tracks = spotify.current_user_saved_tracks_delete(self.four_tracks)
- tracks = spotify.current_user_saved_tracks()
- new_total = tracks['total']
- self.assertTrue(new_total == total)
-
-
- def test_categories(self):
- response = spotify.categories()
- self.assertTrue(len(response['categories']) > 0)
-
- def test_category_playlists(self):
- response = spotify.categories()
- for cat in response['categories']['items']:
- cat_id = cat['id']
- response = spotify.category_playlists(category_id=cat_id)
- self.assertTrue(len(response['playlists']["items"]) > 0)
-
- def test_new_releases(self):
- response = spotify.new_releases()
- self.assertTrue(len(response['albums']) > 0)
-
- def test_featured_releases(self):
- response = spotify.featured_playlists()
- self.assertTrue(len(response['playlists']) > 0)
-
- def test_current_user_follows(self):
- response = spotify.current_user_followed_artists()
- artists = response['artists']
- self.assertTrue(len(artists['items']) > 0)
-
- def test_current_user_top_tracks(self):
- response = spotify.current_user_top_tracks()
- items = response['items']
- self.assertTrue(len(items) > 0)
-
- def test_current_user_top_artists(self):
- response = spotify.current_user_top_artists()
- items = response['items']
- self.assertTrue(len(items) > 0)
-
- def get_or_create_spotify_playlist(self, username, playlist_name):
- playlists = spotify.user_playlists(username)
- while playlists:
- for item in playlists['items']:
- if item['name'] == playlist_name:
- return item['id']
- playlists = spotify.next(playlists)
- playlist = spotify.user_playlist_create(username, playlist_name)
- playlist_id = playlist['uri']
- return playlist_id
-
- def test_user_playlist_ops(self):
- # create empty playlist
- playlist_id = self.get_or_create_spotify_playlist(username,
- 'spotipy-testing-playlist-1')
-
- # remove all tracks from it
-
- spotify.user_playlist_replace_tracks(username, playlist_id,[])
-
- playlist = spotify.user_playlist(username, playlist_id)
- self.assertTrue(playlist['tracks']['total'] == 0)
- self.assertTrue(len(playlist['tracks']['items']) == 0)
-
- # add tracks to it
-
- spotify.user_playlist_add_tracks(username, playlist_id, self.four_tracks)
- playlist = spotify.user_playlist(username, playlist_id)
- self.assertTrue(playlist['tracks']['total'] == 4)
- self.assertTrue(len(playlist['tracks']['items']) == 4)
-
- # remove two tracks from it
-
- spotify.user_playlist_remove_all_occurrences_of_tracks (username,
- playlist_id, self.two_tracks)
-
- playlist = spotify.user_playlist(username, playlist_id)
- self.assertTrue(playlist['tracks']['total'] == 2)
- self.assertTrue(len(playlist['tracks']['items']) == 2)
-
- # replace with 3 other tracks
- spotify.user_playlist_replace_tracks(username,
- playlist_id, self.other_tracks)
-
- playlist = spotify.user_playlist(username, playlist_id)
- self.assertTrue(playlist['tracks']['total'] == 3)
- self.assertTrue(len(playlist['tracks']['items']) == 3)
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- username = sys.argv[1]
- del sys.argv[1]
-
- scope = 'playlist-modify-public '
- scope += 'user-library-read '
- scope += 'user-follow-read '
- scope += 'user-library-modify '
- scope += 'user-read-private '
- scope += 'user-top-read'
-
- token = util.prompt_for_user_token(username, scope)
- spotify = spotipy.Spotify(auth=token)
- spotify.trace = False
- unittest.main()
- else:
- print("Usage: %s username" % (sys.argv[0],))
diff --git a/tests/authtests2.py b/tests/authtests2.py
deleted file mode 100644
index 670e1c8..0000000
--- a/tests/authtests2.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: latin-1 -*-
-
-import spotipy
-from spotipy import util
-import unittest
-import pprint
-import sys
-import simplejson as json
-from spotipy.oauth2 import SpotifyClientCredentials
-
-'''
- Since these tests require authentication they are maintained
- separately from the other tests.
-
- These tests try to be benign and leave your collection and
- playlists in a relatively stable state.
-'''
-
-class AuthTestSpotipy(unittest.TestCase):
- '''
- These tests require user authentication
- '''
-
- playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
- four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
- "spotify:track:7IHOIqZUUInxjVkko181PB",
- "4VrWlk8IQxevMvERoX08iC",
- "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"]
-
- two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
- "spotify:track:7IHOIqZUUInxjVkko181PB"]
-
- other_tracks=["spotify:track:2wySlB6vMzCbQrRnNGOYKa",
- "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
- "spotify:track:1PB7gRWcvefzu7t3LJLUlf"]
-
- bad_id = 'BAD_ID'
-
-
- def test_audio_analysis(self):
- result = spotify.audio_analysis(self.four_tracks[0])
- assert('beats' in result)
-
- def test_audio_features(self):
- results = spotify.audio_features(self.four_tracks)
- self.assertTrue(len(results) == len(self.four_tracks))
- for track in results:
- assert('speechiness' in track)
-
- def test_audio_features_with_bad_track(self):
- bad_tracks = []
- bad_tracks = ['spotify:track:bad']
- input = self.four_tracks + bad_tracks
- results = spotify.audio_features(input)
- self.assertTrue(len(results) == len(input))
- for track in results[:-1]:
- if track != None:
- assert('speechiness' in track)
- self.assertTrue(results[-1] == None)
-
- def test_recommendations(self):
- results = spotify.recommendations(seed_tracks=self.four_tracks, min_danceability=0, max_loudness=0, target_popularity=50)
- self.assertTrue(len(results['tracks']) == 20)
-
-
-if __name__ == '__main__':
- client_credentials_manager = SpotifyClientCredentials()
- spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
- spotify.trace = False
- unittest.main()
diff --git a/tests/client_credentials_tests.py b/tests/client_credentials_tests.py
deleted file mode 100644
index 4905062..0000000
--- a/tests/client_credentials_tests.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: latin-1 -*-
-
-import spotipy
-from spotipy.oauth2 import SpotifyClientCredentials
-import unittest
-
-'''
- Client Credentials Requests Tests
-'''
-
-class ClientCredentialsTestSpotipy(unittest.TestCase):
- '''
- These tests require user authentication
- '''
-
- muse_urn = 'spotify:artist:12Chz98pHFMPJEknJQMWvI'
-
- def test_request_with_token(self):
- artist = spotify.artist(self.muse_urn)
- self.assertTrue(artist['name'] == 'Muse')
-
-
-if __name__ == '__main__':
- spotify_cc = SpotifyClientCredentials()
- spotify = spotipy.Spotify(client_credentials_manager=spotify_cc)
- spotify.trace = False
- unittest.main()
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 0000000..327898a
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,312 @@
+# -*- coding: utf-8 -*-
+
+"""
+These tests require user authentication - provide client credentials using the
+following environment variables
+
+::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+"""
+
+from __future__ import print_function
+
+from spotipy import (
+ CLIENT_CREDS_ENV_VARS as CCEV,
+ prompt_for_user_token,
+ Spotify,
+ SpotifyException,
+)
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.abspath(os.pardir))
+
+
+class AuthTestSpotipy(unittest.TestCase):
+ """
+ These tests require user authentication - provide client credentials using
+ the following environment variables
+
+ ::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+ """
+
+ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
+ playlist_new_id = "spotify:playlist:7GlxpQjjxRjmbb3RP2rDqI"
+ four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
+ "spotify:track:7IHOIqZUUInxjVkko181PB",
+ "4VrWlk8IQxevMvERoX08iC",
+ "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"]
+
+ two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
+ "spotify:track:7IHOIqZUUInxjVkko181PB"]
+
+ other_tracks = ["spotify:track:2wySlB6vMzCbQrRnNGOYKa",
+ "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
+ "spotify:track:1PB7gRWcvefzu7t3LJLUlf"]
+
+ album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU",
+ "spotify:album:6RTzC0rDbvagTSJLlY7AKl"]
+
+ bad_id = 'BAD_ID'
+
+ @classmethod
+ def setUpClass(self):
+
+ missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV))
+
+ if missing:
+ raise Exception(
+ ('Please set the client credentials for the test application'
+ ' using the following environment variables: {}').format(
+ CCEV.values()))
+
+ self.username = os.getenv(CCEV['client_username'])
+
+ self.scope = (
+ 'playlist-modify-public '
+ 'user-library-read '
+ 'user-follow-read '
+ 'user-library-modify '
+ 'user-read-private '
+ 'user-top-read '
+ 'user-follow-modify'
+ )
+
+ self.token = prompt_for_user_token(self.username, scope=self.scope)
+
+ self.spotify = Spotify(auth=self.token)
+
+ def test_track_bad_id(self):
+ try:
+ self.spotify.track(self.bad_id)
+ self.assertTrue(False)
+ except SpotifyException:
+ self.assertTrue(True)
+
+ def test_basic_user_profile(self):
+ user = self.spotify.user(self.username)
+ self.assertTrue(user['id'] == self.username.lower())
+
+ def test_current_user(self):
+ user = self.spotify.current_user()
+ self.assertTrue(user['id'] == self.username.lower())
+
+ def test_me(self):
+ user = self.spotify.me()
+ self.assertTrue(user['id'] == self.username.lower())
+
+ def test_user_playlists(self):
+ playlists = self.spotify.user_playlists(self.username, limit=5)
+ self.assertTrue('items' in playlists)
+ self.assertTrue(len(playlists['items']) == 5)
+
+ def test_user_playlist_tracks(self):
+ playlists = self.spotify.user_playlists(self.username, limit=5)
+ self.assertTrue('items' in playlists)
+ for playlist in playlists['items']:
+ user = playlist['owner']['id']
+ pid = playlist['id']
+ 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()
+ self.assertTrue(len(albums['items']) == 1)
+
+ # Add
+ self.spotify.current_user_saved_albums_add(self.album_ids)
+
+ # Contains
+ self.assertTrue(
+ self.spotify.current_user_saved_albums_contains(
+ self.album_ids) == [
+ True, True])
+
+ # Remove
+ self.spotify.current_user_saved_albums_delete(self.album_ids)
+ albums = self.spotify.current_user_saved_albums()
+ self.assertTrue(len(albums['items']) == 1)
+
+ def test_current_user_playlists(self):
+ playlists = self.spotify.current_user_playlists(limit=10)
+ self.assertTrue('items' in playlists)
+ self.assertTrue(len(playlists['items']) == 10)
+
+ def test_user_playlist_follow(self):
+ self.spotify.user_playlist_follow_playlist(
+ 'plamere', '4erXB04MxwRAVqcUEpu30O')
+ follows = self.spotify.user_playlist_is_following(
+ 'plamere', '4erXB04MxwRAVqcUEpu30O', [
+ self.spotify.current_user()['id']])
+
+ self.assertTrue(len(follows) == 1, 'proper follows length')
+ self.assertTrue(follows[0], 'is following')
+ self.spotify.user_playlist_unfollow(
+ 'plamere', '4erXB04MxwRAVqcUEpu30O')
+
+ follows = self.spotify.user_playlist_is_following(
+ 'plamere', '4erXB04MxwRAVqcUEpu30O', [
+ self.spotify.current_user()['id']])
+ self.assertTrue(len(follows) == 1, 'proper follows length')
+ self.assertFalse(follows[0], 'is no longer following')
+
+ def test_current_user_saved_tracks(self):
+ tracks = self.spotify.current_user_saved_tracks()
+ self.assertTrue(len(tracks['items']) > 0)
+
+ def test_current_user_save_and_unsave_tracks(self):
+ tracks = self.spotify.current_user_saved_tracks()
+ total = tracks['total']
+ self.spotify.current_user_saved_tracks_add(self.four_tracks)
+
+ tracks = self.spotify.current_user_saved_tracks()
+ new_total = tracks['total']
+ self.assertTrue(new_total - total == len(self.four_tracks))
+
+ tracks = self.spotify.current_user_saved_tracks_delete(
+ self.four_tracks)
+ tracks = self.spotify.current_user_saved_tracks()
+ new_total = tracks['total']
+ self.assertTrue(new_total == total)
+
+ def test_categories(self):
+ response = self.spotify.categories()
+ self.assertTrue(len(response['categories']) > 0)
+
+ def test_category_playlists(self):
+ response = self.spotify.categories()
+ for cat in response['categories']['items']:
+ cat_id = cat['id']
+ response = self.spotify.category_playlists(category_id=cat_id)
+ if len(response['playlists']["items"]) > 0:
+ break
+ self.assertTrue(True)
+
+ def test_new_releases(self):
+ response = self.spotify.new_releases()
+ self.assertTrue(len(response['albums']) > 0)
+
+ def test_featured_releases(self):
+ response = self.spotify.featured_playlists()
+ self.assertTrue(len(response['playlists']) > 0)
+
+ def test_current_user_follows(self):
+ response = self.spotify.current_user_followed_artists()
+ artists = response['artists']
+ self.assertTrue(len(artists['items']) > 0)
+
+ def test_current_user_top_tracks(self):
+ response = self.spotify.current_user_top_tracks()
+ items = response['items']
+ self.assertTrue(len(items) > 0)
+
+ def test_current_user_top_artists(self):
+ response = self.spotify.current_user_top_artists()
+ items = response['items']
+ self.assertTrue(len(items) > 0)
+
+ def get_or_create_spotify_playlist(self, playlist_name):
+ playlists = self.spotify.user_playlists(self.username)
+ while playlists:
+ for item in playlists['items']:
+ if item['name'] == playlist_name:
+ return item['id']
+ playlists = self.spotify.next(playlists)
+ playlist = self.spotify.user_playlist_create(
+ self.username, playlist_name)
+ playlist_id = playlist['uri']
+ return playlist_id
+
+ def test_user_playlist_ops(self):
+ sp = self.spotify
+ # create empty playlist
+ playlist_id = self.get_or_create_spotify_playlist(
+ 'spotipy-testing-playlist-1')
+
+ # remove all tracks from it
+ sp.user_playlist_replace_tracks(
+ self.username, playlist_id, [])
+ playlist = sp.user_playlist(self.username, playlist_id)
+ self.assertTrue(playlist['tracks']['total'] == 0)
+ self.assertTrue(len(playlist['tracks']['items']) == 0)
+
+ # add tracks to it
+ sp.user_playlist_add_tracks(
+ self.username, playlist_id, self.four_tracks)
+ playlist = sp.user_playlist(self.username, playlist_id)
+ self.assertTrue(playlist['tracks']['total'] == 4)
+ self.assertTrue(len(playlist['tracks']['items']) == 4)
+
+ # remove two tracks from it
+
+ sp.user_playlist_remove_all_occurrences_of_tracks(self.username,
+ playlist_id,
+ self.two_tracks)
+ playlist = sp.user_playlist(self.username, playlist_id)
+ self.assertTrue(playlist['tracks']['total'] == 2)
+ self.assertTrue(len(playlist['tracks']['items']) == 2)
+
+ # replace with 3 other tracks
+ sp.user_playlist_replace_tracks(self.username,
+ playlist_id,
+ self.other_tracks)
+ playlist = sp.user_playlist(self.username, playlist_id)
+ self.assertTrue(playlist['tracks']['total'] == 3)
+ self.assertTrue(len(playlist['tracks']['items']) == 3)
+
+ def test_playlist(self):
+ # New playlist ID
+ pl = self.spotify.playlist(self.playlist_new_id)
+ self.assertTrue(pl["tracks"]["total"] > 0)
+
+ # Old playlist ID
+ pl = self.spotify.playlist(self.playlist)
+ self.assertTrue(pl["tracks"]["total"] > 0)
+
+ def test_user_follows_and_unfollows_artist(self):
+ # Initially follows 1 artist
+ res = self.spotify.current_user_followed_artists()
+ self.assertTrue(res['artists']['total'] == 1)
+
+ # Follow 2 more artists
+ artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"]
+ self.spotify.user_follow_artists(artists)
+ res = self.spotify.current_user_followed_artists()
+ self.assertTrue(res['artists']['total'] == 3)
+
+ # Unfollow these 2 artists
+ self.spotify.user_unfollow_artists(artists)
+ res = self.spotify.current_user_followed_artists()
+ self.assertTrue(res['artists']['total'] == 1)
+
+ def test_user_follows_and_unfollows_user(self):
+ # TODO improve after implementing `me/following/contains`
+ users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"]
+
+ # Follow 2 more users
+ self.spotify.user_follow_users(users)
+
+ # Unfollow these 2 users
+ self.spotify.user_unfollow_users(users)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_auth2.py b/tests/test_auth2.py
new file mode 100644
index 0000000..47cf798
--- /dev/null
+++ b/tests/test_auth2.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+"""
+These tests require user authentication - provide client credentials using the
+following environment variables
+
+::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+"""
+
+from spotipy import (
+ Spotify,
+ SpotifyClientCredentials,
+)
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.abspath(os.pardir))
+
+
+class AuthTestSpotipy(unittest.TestCase):
+ """
+ These tests require user authentication - provide client credentials using
+ the following environment variables
+
+ ::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+ """
+
+ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
+ four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
+ "spotify:track:7IHOIqZUUInxjVkko181PB",
+ "4VrWlk8IQxevMvERoX08iC",
+ "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"]
+
+ two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp",
+ "spotify:track:7IHOIqZUUInxjVkko181PB"]
+
+ other_tracks = ["spotify:track:2wySlB6vMzCbQrRnNGOYKa",
+ "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
+ "spotify:track:1PB7gRWcvefzu7t3LJLUlf"]
+
+ bad_id = 'BAD_ID'
+
+ @classmethod
+ def setUpClass(self):
+ self.spotify = Spotify(
+ client_credentials_manager=SpotifyClientCredentials())
+ self.spotify.trace = False
+
+ def test_audio_analysis(self):
+ result = self.spotify.audio_analysis(self.four_tracks[0])
+ assert('beats' in result)
+
+ def test_audio_features(self):
+ results = self.spotify.audio_features(self.four_tracks)
+ self.assertTrue(len(results) == len(self.four_tracks))
+ for track in results:
+ assert('speechiness' in track)
+
+ def test_audio_features_with_bad_track(self):
+ bad_tracks = []
+ bad_tracks = ['spotify:track:bad']
+ input = self.four_tracks + bad_tracks
+ results = self.spotify.audio_features(input)
+ self.assertTrue(len(results) == len(input))
+ for track in results[:-1]:
+ if track is not None:
+ assert('speechiness' in track)
+ self.assertTrue(results[-1] is None)
+
+ def test_recommendations(self):
+ results = self.spotify.recommendations(
+ seed_tracks=self.four_tracks,
+ min_danceability=0,
+ max_loudness=0,
+ target_popularity=50)
+ self.assertTrue(len(results['tracks']) == 20)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_client_credentials.py b/tests/test_client_credentials.py
new file mode 100644
index 0000000..acbda5c
--- /dev/null
+++ b/tests/test_client_credentials.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+""" Client Credentials Requests Tests """
+
+from spotipy import (
+ Spotify,
+ SpotifyClientCredentials,
+)
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.abspath(os.pardir))
+
+
+class ClientCredentialsTestSpotipy(unittest.TestCase):
+ """
+ These tests require user authentication - provide client credentials using
+ the following environment variables
+
+ ::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+ """
+
+ @classmethod
+ def setUpClass(self):
+ self.spotify = Spotify(
+ client_credentials_manager=SpotifyClientCredentials())
+ self.spotify.trace = False
+
+ muse_urn = 'spotify:artist:12Chz98pHFMPJEknJQMWvI'
+
+ def test_request_with_token(self):
+ artist = self.spotify.artist(self.muse_urn)
+ self.assertTrue(artist['name'] == 'Muse')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 1871b60..9b51eb5 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -1,13 +1,20 @@
-from spotipy.oauth2 import SpotifyOAuth
-import json
+# -*- coding: utf-8 -*-
+
+import six.moves.urllib.parse as urllibparse
+from spotipy import SpotifyOAuth
import io
+import json
+import os
+import sys
import unittest
+sys.path.insert(0, os.path.abspath(os.pardir))
+
+
try:
import unittest.mock as mock
except ImportError:
import mock
-import six.moves.urllib.parse as urllibparse
patch = mock.patch
DEFAULT = mock.DEFAULT
@@ -166,4 +173,5 @@ class TestSpotifyOAuth(unittest.TestCase):
if __name__ == '__main__':
+
unittest.main()
diff --git a/tests/tests.py b/tests/tests.py
index 760b91c..5d9a4a4 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -1,13 +1,34 @@
-# -*- coding: latin-1 -*-
-import spotipy
+# -*- coding: utf-8 -*-
+
+from spotipy import (
+ CLIENT_CREDS_ENV_VARS as CCEV,
+ prompt_for_user_token,
+ Spotify,
+ SpotifyException,
+)
+import os
+import sys
import unittest
-import pprint
+
import requests
-from spotipy.client import SpotifyException
+
+sys.path.insert(0, os.path.abspath(os.pardir))
class TestSpotipy(unittest.TestCase):
+ """
+ These tests require user authentication - provide client credentials using
+ the following environment variables
+
+ ::
+
+ 'SPOTIPY_CLIENT_USERNAME'
+ 'SPOTIPY_CLIENT_ID'
+ 'SPOTIPY_CLIENT_SECRET'
+ 'SPOTIPY_REDIRECT_URI'
+ """
+
creep_urn = 'spotify:track:3HfB5hBU0dmBt8T0iCmH42'
creep_id = '3HfB5hBU0dmBt8T0iCmH42'
creep_url = 'http://open.spotify.com/track/3HfB5hBU0dmBt8T0iCmH42'
@@ -19,11 +40,25 @@ class TestSpotipy(unittest.TestCase):
radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'
-
bad_id = 'BAD_ID'
- def setUp(self):
- self.spotify = spotipy.Spotify()
+ @classmethod
+ def setUpClass(self):
+ missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV))
+
+ if missing:
+ raise Exception(
+ ('Please set the client credentials for the test '
+ 'the following environment variables: {}').format(
+ CCEV.values()))
+
+ self.username = os.getenv(CCEV['client_username'])
+
+ self.scope = 'user-library-read'
+
+ self.token = prompt_for_user_token(self.username, scope=self.scope)
+
+ self.spotify = Spotify(auth=self.token)
def test_artist_urn(self):
artist = self.spotify.artist(self.radiohead_urn)
@@ -41,20 +76,22 @@ class TestSpotipy(unittest.TestCase):
def test_album_tracks(self):
results = self.spotify.album_tracks(self.pinkerton_urn)
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)
+ 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):
- 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(len(results['albums']) == 2)
@@ -72,9 +109,9 @@ class TestSpotipy(unittest.TestCase):
def test_track_bad_urn(self):
try:
- track = self.spotify.track(self.el_scorcho_bad_urn)
+ self.spotify.track(self.el_scorcho_bad_urn)
self.assertTrue(False)
- except spotipy.SpotifyException:
+ except SpotifyException:
self.assertTrue(True)
def test_tracks(self):
@@ -121,19 +158,19 @@ class TestSpotipy(unittest.TestCase):
self.assertTrue(found)
def test_search_timeout(self):
- sp = spotipy.Spotify(requests_timeout=.1)
+ sp = Spotify(auth=self.token, requests_timeout=.01)
try:
- results = sp.search(q='my*', type='track')
+ sp.search(q='my*', type='track')
self.assertTrue(False, 'unexpected search timeout')
- except requests.ReadTimeout:
+ except requests.Timeout:
self.assertTrue(True, 'expected search timeout')
-
def test_album_search(self):
results = self.spotify.search(q='weezer pinkerton', type='album')
self.assertTrue('albums' in results)
self.assertTrue(len(results['albums']['items']) > 0)
- self.assertTrue(results['albums']['items'][0]['name'].find('Pinkerton') >= 0)
+ self.assertTrue(results['albums']['items'][0]
+ ['name'].find('Pinkerton') >= 0)
def test_track_search(self):
results = self.spotify.search(q='el scorcho weezer', type='track')
@@ -147,37 +184,34 @@ class TestSpotipy(unittest.TestCase):
def test_track_bad_id(self):
try:
- track = self.spotify.track(self.bad_id)
+ self.spotify.track(self.bad_id)
self.assertTrue(False)
- except spotipy.SpotifyException:
- self.assertTrue(True)
-
- def test_track_bad_id(self):
- try:
- track = self.spotify.track(self.bad_id)
- self.assertTrue(False)
- except spotipy.SpotifyException:
+ except SpotifyException:
self.assertTrue(True)
def test_unauthenticated_post_fails(self):
with self.assertRaises(SpotifyException) as cm:
- self.spotify.user_playlist_create("spotify", "Best hits of the 90s")
+ self.spotify.user_playlist_create(
+ "spotify", "Best hits of the 90s")
self.assertTrue(cm.exception.http_status == 401 or
- cm.exception.http_status == 403)
+ cm.exception.http_status == 403)
def test_custom_requests_session(self):
- from requests import Session
- sess = Session()
+ sess = requests.Session()
sess.headers["user-agent"] = "spotipy-test"
- with_custom_session = spotipy.Spotify(requests_session=sess)
- self.assertTrue(with_custom_session.user(user="akx")["uri"] == "spotify:user:akx")
+ with_custom_session = Spotify(auth=self.token, requests_session=sess)
+ self.assertTrue(
+ with_custom_session.user(
+ user="akx")["uri"] == "spotify:user:akx")
def test_force_no_requests_session(self):
- from requests import Session
- with_no_session = spotipy.Spotify(requests_session=False)
- self.assertFalse(isinstance(with_no_session._session, Session))
- self.assertTrue(with_no_session.user(user="akx")["uri"] == "spotify:user:akx")
-
+ with_no_session = Spotify(auth=self.token, requests_session=False)
+ self.assertFalse(
+ isinstance(
+ with_no_session._session,
+ requests.Session))
+ self.assertTrue(with_no_session.user(user="akx")
+ ["uri"] == "spotify:user:akx")
'''
diff --git a/tox.ini b/tox.ini
index e3af7ee..5d4b140 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,4 +5,9 @@ deps=
requests
six
py27: mock
-commands=python -m unittest discover tests
+commands=python -m unittest discover -v tests
+[flake8]
+exclude=
+ .git,
+ dist,
+ docs
\ No newline at end of file