mirror of
https://github.com/c0de-archive/spotipy.git
synced 2024-11-05 07:27:47 +00:00
Merge pull request #46 from fsahin/master
Implement client credentials flow
This commit is contained in:
commit
c8c44c1b28
10
examples/client_credentials_flow.py
Normal file
10
examples/client_credentials_flow.py
Normal file
@ -0,0 +1,10 @@
|
||||
from spotipy.oauth2 import SpotifyClientCredentials
|
||||
import spotipy
|
||||
import pprint
|
||||
|
||||
client_credentials_manager = SpotifyClientCredentials()
|
||||
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
|
||||
|
||||
search_str = 'Muse'
|
||||
result = sp.search(search_str)
|
||||
pprint.pprint(result)
|
@ -40,7 +40,8 @@ class Spotify(object):
|
||||
|
||||
trace = False # Enable tracing?
|
||||
|
||||
def __init__(self, auth=None, requests_session=True):
|
||||
def __init__(self, auth=None, requests_session=True,
|
||||
client_credentials_manager=None):
|
||||
'''
|
||||
Create a Spotify API object.
|
||||
|
||||
@ -50,10 +51,13 @@ class Spotify(object):
|
||||
A falsy value disables sessions.
|
||||
It should generally be a good idea to keep sessions enabled
|
||||
for performance reasons (connection pooling).
|
||||
:param client_credentials_manager:
|
||||
SpotifyClientCredentials object
|
||||
|
||||
'''
|
||||
self.prefix = 'https://api.spotify.com/v1/'
|
||||
self._auth = auth
|
||||
self.client_credentials_manager = client_credentials_manager
|
||||
|
||||
if isinstance(requests_session, requests.Session):
|
||||
self._session = requests_session
|
||||
@ -67,6 +71,9 @@ class Spotify(object):
|
||||
def _auth_headers(self):
|
||||
if self._auth:
|
||||
return {'Authorization': 'Bearer {0}'.format(self._auth)}
|
||||
elif self.client_credentials_manager:
|
||||
token = self.client_credentials_manager.get_access_token()
|
||||
return {'Authorization': 'Bearer {0}'.format(token)}
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
@ -7,9 +7,76 @@ import json
|
||||
import time
|
||||
import sys
|
||||
|
||||
|
||||
class SpotifyOauthError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SpotifyClientCredentials(object):
|
||||
OAUTH_TOKEN_URL = 'https://accounts.spotify.com/api/token'
|
||||
|
||||
def __init__(self, client_id=None, client_secret=None):
|
||||
"""
|
||||
You can either provid a client_id and client_secret to the
|
||||
constructor or set SPOTIPY_CLIENT_ID and SPOTIPY_CLIENT_SECRET
|
||||
environment variables
|
||||
"""
|
||||
if not client_id:
|
||||
client_id = os.getenv('SPOTIPY_CLIENT_ID')
|
||||
|
||||
if not client_secret:
|
||||
client_secret = os.getenv('SPOTIPY_CLIENT_SECRET')
|
||||
|
||||
if not client_id:
|
||||
raise SpotifyOauthError('No client id')
|
||||
|
||||
if not client_secret:
|
||||
raise SpotifyOauthError('No client secret')
|
||||
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.token_info = None
|
||||
|
||||
def get_access_token(self):
|
||||
"""
|
||||
If a valid access token is in memory, returns it
|
||||
Else feches a new token and returns it
|
||||
"""
|
||||
if self.token_info and not self._is_token_expired(self.token_info):
|
||||
return self.token_info['access_token']
|
||||
|
||||
token_info = self._request_access_token()
|
||||
token_info = self._add_custom_values_to_token_info(token_info)
|
||||
self.token_info = token_info
|
||||
return self.token_info['access_token']
|
||||
|
||||
def _request_access_token(self):
|
||||
"""Gets client credentials access token """
|
||||
payload = { 'grant_type': 'client_credentials'}
|
||||
|
||||
auth_header = base64.b64encode(self.client_id + ':' + self.client_secret)
|
||||
headers = {'Authorization': 'Basic %s' % auth_header}
|
||||
|
||||
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
|
||||
headers=headers, verify=True)
|
||||
if response.status_code is not 200:
|
||||
raise SpotifyOauthError(response.reason)
|
||||
token_info = response.json()
|
||||
return token_info
|
||||
|
||||
def _is_token_expired(self, token_info):
|
||||
now = int(time.time())
|
||||
return token_info['expires_at'] < now
|
||||
|
||||
def _add_custom_values_to_token_info(self, token_info):
|
||||
"""
|
||||
Store some values that aren't directly provided by a Web API
|
||||
response.
|
||||
"""
|
||||
token_info['expires_at'] = int(time.time()) + token_info['expires_in']
|
||||
return token_info
|
||||
|
||||
|
||||
class SpotifyOAuth(object):
|
||||
'''
|
||||
Implements Authorization Code Flow for Spotify's OAuth implementation.
|
||||
@ -18,7 +85,7 @@ class SpotifyOAuth(object):
|
||||
OAUTH_AUTHORIZE_URL = 'https://accounts.spotify.com/authorize'
|
||||
OAUTH_TOKEN_URL = 'https://accounts.spotify.com/api/token'
|
||||
|
||||
def __init__(self, client_id, client_secret, redirect_uri,
|
||||
def __init__(self, client_id, client_secret, redirect_uri,
|
||||
state=None, scope=None, cache_path=None):
|
||||
'''
|
||||
Creates a SpotifyOAuth object
|
||||
@ -38,7 +105,7 @@ class SpotifyOAuth(object):
|
||||
self.state=state
|
||||
self.cache_path = cache_path
|
||||
self.scope=self._normalize_scope(scope)
|
||||
|
||||
|
||||
def get_cached_token(self):
|
||||
''' Gets a cached auth token
|
||||
'''
|
||||
@ -75,7 +142,7 @@ class SpotifyOAuth(object):
|
||||
def _is_token_expired(self, token_info):
|
||||
now = int(time.time())
|
||||
return token_info['expires_at'] < now
|
||||
|
||||
|
||||
def get_authorize_url(self):
|
||||
""" Gets the URL to use to authorize this app
|
||||
"""
|
||||
@ -122,7 +189,7 @@ class SpotifyOAuth(object):
|
||||
headers = {'Authorization': 'Basic %s' % auth_header}
|
||||
|
||||
|
||||
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
|
||||
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
|
||||
headers=headers, verify=True)
|
||||
if response.status_code is not 200:
|
||||
raise SpotifyOauthError(response.reason)
|
||||
@ -146,7 +213,7 @@ class SpotifyOAuth(object):
|
||||
auth_header = base64.b64encode(self.client_id + ':' + self.client_secret)
|
||||
headers = {'Authorization': 'Basic %s' % auth_header}
|
||||
|
||||
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
|
||||
response = requests.post(self.OAUTH_TOKEN_URL, data=payload,
|
||||
headers=headers)
|
||||
if response.status_code != 200:
|
||||
if False: # debugging code
|
||||
@ -163,7 +230,7 @@ class SpotifyOAuth(object):
|
||||
return token_info
|
||||
|
||||
def _add_custom_values_to_token_info(self, token_info):
|
||||
'''
|
||||
'''
|
||||
Store some values that aren't directly provided by a Web API
|
||||
response.
|
||||
'''
|
||||
|
27
tests/client_credentials_tests.py
Normal file
27
tests/client_credentials_tests.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- 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'] == u'Muse')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
spotify_cc = SpotifyClientCredentials()
|
||||
spotify = spotipy.Spotify(client_credentials_manager=spotify_cc)
|
||||
spotify.trace = False
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user