Merge branch 'master' into master

This commit is contained in:
Stéphane Bruckert 2020-01-15 19:16:38 +00:00 committed by GitHub
commit 9b8c90dbe1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1113 additions and 770 deletions

23
.github/workflows/pythonapp.yml vendored Normal file
View File

@ -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

1
.gitignore vendored
View File

@ -52,6 +52,5 @@ coverage.xml
# Sphinx documentation
docs/_build/
.*
archive

View File

@ -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

126
CHANGELOG.md Normal file
View File

@ -0,0 +1,126 @@
# 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]
### Changed
- Allow session keepalive
- Bump requests to 2.20.0
## [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

View File

@ -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

View File

@ -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
[![Documentation Status](https://readthedocs.org/projects/spotipy/badge/?version=latest)](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).
@ -52,35 +53,3 @@ 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

19
deploy
View File

@ -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

View File

@ -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,16 +102,18 @@ 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
`My Applications
<https://developer.spotify.com/my-applications/#!/applications>`_.
<https://developer.spotify.com/my-applications/#!/applications>`_ and register the
redirect URI mentioned in the above paragragh.
*spotipy* supports two authorization flows:
@ -127,7 +132,7 @@ 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,
you can set environment variables like so::
@ -140,12 +145,13 @@ Call ``util.prompt_for_user_token`` method with the username and the
desired scope (see `Using
Scopes <https://developer.spotify.com/web-api/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
=======================
@ -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,9 +281,9 @@ 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']
print()
print(playlist['name'])
print (' total tracks', playlist['tracks']['total'])
results = sp.user_playlist(username, playlist['id'],
fields="tracks,next")
tracks = results['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
@ -356,10 +364,12 @@ Spotipy authored by Paul Lamere (plamere) with contributions by:
- 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

View File

@ -1,7 +1,6 @@
# Adds tracks to a playlist
import pprint
import sys
import spotipy

View File

@ -5,6 +5,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']
@ -13,6 +14,7 @@ def get_artist(name):
else:
return None
def show_artist_albums(artist):
albums = []
results = sp.artist_albums(artist['id'], album_type='album')
@ -20,14 +22,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__':
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

View File

@ -5,6 +5,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']
@ -13,6 +14,7 @@ def get_artist(name):
else:
return None
def show_album_tracks(album):
tracks = []
results = sp.album_tracks(album['id'])
@ -25,6 +27,7 @@ def show_album_tracks(album):
print()
print(track)
def show_artist_albums(id):
albums = []
results = sp.artist_albums(artist['id'], album_type='album')
@ -36,17 +39,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__':
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

View File

@ -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)

View File

@ -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,))

View File

@ -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,))

View File

@ -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,))

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -1,4 +1,3 @@
# Replaces all tracks in a playlist
import pprint

View File

@ -3,6 +3,7 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint

View File

@ -2,6 +2,7 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint

View File

@ -2,8 +2,8 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint
if len(sys.argv) > 1:
urn = sys.argv[1]

View File

@ -2,7 +2,6 @@
import spotipy
import sys
import pprint
import spotipy.util as util
if len(sys.argv) > 1:

View File

@ -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)

View File

@ -4,7 +4,6 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import pprint
if len(sys.argv) > 1:
artist_name = sys.argv[1]
@ -22,6 +21,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]")

View File

@ -2,6 +2,7 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import sys
import pprint

View File

@ -21,6 +21,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'])

View File

@ -16,4 +16,3 @@ sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
sp.trace = True
user = sp.user(username)
pprint.pprint(user)

View File

@ -1,18 +1,16 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
birdy_uri = 'spotify:artist:2WX2uTcsvV5OnS0inACecP'
client_credentials_manager = SpotifyClientCredentials()
spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
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']))

View File

@ -2,13 +2,12 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'
client_credentials_manager = SpotifyClientCredentials()
spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
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'])

View File

@ -1,18 +1,16 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
client_credentials_manager = SpotifyClientCredentials()
spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
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'])

View File

@ -1,7 +1,6 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import random
import simplejson as json
'''
generates a list of songs where the first word in each subsequent song
@ -18,14 +17,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
@ -41,27 +41,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())

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -1,2 +1,3 @@
requests==2.3.0
mock==2.0.0
requests==2.20.0
six==1.10.0

View File

@ -1,15 +1,18 @@
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/',
install_requires=[
'requests>=2.3.0',
'requests>=2.20.0',
'six>=1.10.0',
],
license='LICENSE.txt',
license='LICENSE.md',
packages=['spotipy'])

View File

@ -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

View File

@ -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
@ -107,36 +113,39 @@ class Spotify(object):
if self.trace_out:
print(url)
r = self._session.request(method, url, headers=headers, proxies=self.proxies, **args)
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(method, r.url)
if payload:
print("Data", json.dumps(payload))
try:
r.raise_for_status()
except BaseException:
try:
msg = r.json()['error']['message']
except BaseException:
msg = 'error'
raise SpotifyException(r.status_code,
-1, '%s:\n %s' % (r.url, msg),
headers=r.headers)
try:
results = r.json()
except BaseException:
results = None
if self.trace: # pragma: no cover
print('Response:', results)
print()
print ('headers', headers)
print ('http status', r.status_code)
print(method, r.url)
if payload:
print("DATA", json.dumps(payload))
try:
r.raise_for_status()
except:
if r.text and len(r.text) > 0 and r.text != 'null':
raise SpotifyException(r.status_code,
-1, '%s:\n %s' % (r.url, r.json()['error']['message']),
headers=r.headers)
else:
raise SpotifyException(r.status_code,
-1, '%s:\n %s' % (r.url, 'error'), headers=r.headers)
finally:
r.connection.close()
if r.text and len(r.text) > 0 and r.text != 'null':
results = r.json()
if self.trace: # pragma: no cover
print('RESP', results)
print()
return results
else:
return None
return results
def _get(self, url, args=None, payload=None, **kwargs):
if args:
@ -154,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:
@ -227,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:
@ -236,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
@ -339,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
@ -382,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.
@ -399,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
@ -411,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(
@ -447,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):
@ -487,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)
@ -507,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
"""
@ -527,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
"""
@ -555,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.
@ -586,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
@ -612,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,
@ -689,6 +713,27 @@ class Spotify(object):
'''
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 users 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.
@ -696,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
@ -713,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
@ -801,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_<attribute> - For the tuneable track attributes listed
in the documentation, these values provide filters and targeting on
results.
- min/max/target_<attribute> - For the tuneable track
attributes listed in the documentation, these values
provide filters and targeting on results.
"""
params = dict(limit=limit)
if seed_artists:
@ -869,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.
@ -914,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,
@ -945,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:
@ -955,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:
@ -963,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:
@ -981,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:
@ -993,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:
@ -1008,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:
@ -1021,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.
@ -1040,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):

View File

@ -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)

View File

@ -1,13 +1,31 @@
# -*- 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
constructor
@ -48,7 +66,7 @@ def prompt_for_user_token(username, scope=None, client_id = None,
cache_path = cache_path or ".cache-" + username
sp_oauth = oauth2.SpotifyOAuth(client_id, client_secret, redirect_uri,
scope=scope, cache_path=cache_path)
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()

View File

@ -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],))

View File

@ -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()

View File

@ -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()

312
tests/test_auth.py Normal file
View File

@ -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()

91
tests/test_auth2.py Normal file
View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -1,7 +1,9 @@
# -*- coding: latin-1 -*-
import spotipy
# -*- coding: utf-8 -*-
import os
import sys
import unittest
import pprint
import requests
from spotipy.client import SpotifyException
from spotipy.oauth2 import SpotifyClientCredentials
@ -9,6 +11,18 @@ from spotipy.oauth2 import SpotifyClientCredentials
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'
@ -20,9 +34,9 @@ class TestSpotipy(unittest.TestCase):
radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'
bad_id = 'BAD_ID'
def setUp(self):
client_credentials_manager = SpotifyClientCredentials()
self.spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
@ -49,14 +63,16 @@ class TestSpotipy(unittest.TestCase):
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)
@ -74,9 +90,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):
@ -125,18 +141,19 @@ class TestSpotipy(unittest.TestCase):
def test_search_timeout(self):
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager, requests_timeout=.1)
try:
results = sp.search(q='my*', type='track')
sp.search(q='my*', type='track')
self.assertTrue(False, 'unexpected search timeout')
except requests.exceptions.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')
@ -150,27 +167,20 @@ 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"
client_credentials_manager = SpotifyClientCredentials()
with_custom_session = spotipy.Spotify(client_credentials_manager=client_credentials_manager, requests_session=sess)
@ -184,7 +194,6 @@ class TestSpotipy(unittest.TestCase):
self.assertTrue(with_no_session.user(user="akx")["uri"] == "spotify:user:akx")
'''
Need tests for:

View File

@ -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