Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FlaskSessionCacheHandler #833

Merged
merged 1 commit into from Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added `market` parameter to `album` and `albums` to address ([#753](https://github.com/plamere/spotipy/issues/753)
* Added 'show_featured_artists.py' to 'examples'.
* Expanded contribution and license sections of the documentation.
* Added `FlaskSessionCacheHandler`, a cache handler that stores the token info in a flask session.

### Fixed

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Expand Up @@ -235,6 +235,7 @@ The following handlers are available and defined in the URL above.
- ``CacheFileHandler``
- ``MemoryCacheHandler``
- ``DjangoSessionCacheHandler``
- ``FlaskSessionCacheHandler``
- ``RedisCacheHandler``

Feel free to contribute new cache handlers to the repo.
Expand Down
38 changes: 12 additions & 26 deletions examples/app.py
Expand Up @@ -27,65 +27,51 @@
from flask import Flask, session, request, redirect
from flask_session import Session
import spotipy
import uuid

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(64)
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = './.flask_session/'
Session(app)

caches_folder = './.spotify_caches/'
if not os.path.exists(caches_folder):
os.makedirs(caches_folder)

def session_cache_path():
return caches_folder + session.get('uuid')

@app.route('/')
def index():
if not session.get('uuid'):
# Step 1. Visitor is unknown, give random ID
session['uuid'] = str(uuid.uuid4())

cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path=session_cache_path())
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(scope='user-read-currently-playing playlist-modify-private',
cache_handler=cache_handler,
show_dialog=True)
cache_handler=cache_handler,
show_dialog=True)

if request.args.get("code"):
# Step 3. Being redirected from Spotify auth page
# Step 2. Being redirected from Spotify auth page
auth_manager.get_access_token(request.args.get("code"))
return redirect('/')

if not auth_manager.validate_token(cache_handler.get_cached_token()):
# Step 2. Display sign in link when no token
# Step 1. Display sign in link when no token
auth_url = auth_manager.get_authorize_url()
return f'<h2><a href="{auth_url}">Sign in</a></h2>'

# Step 4. Signed in, display data
# Step 3. Signed in, display data
spotify = spotipy.Spotify(auth_manager=auth_manager)
return f'<h2>Hi {spotify.me()["display_name"]}, ' \
f'<small><a href="/sign_out">[sign out]<a/></small></h2>' \
f'<a href="/playlists">my playlists</a> | ' \
f'<a href="/currently_playing">currently playing</a> | ' \
f'<a href="/current_user">me</a>' \
f'<a href="/current_user">me</a>' \



@app.route('/sign_out')
def sign_out():
try:
# Remove the CACHE file (.cache-test) so that a new user can authorize.
os.remove(session_cache_path())
session.clear()
except OSError as e:
print ("Error: %s - %s." % (e.filename, e.strerror))
session.pop("token_info", None)
return redirect('/')


@app.route('/playlists')
def playlists():
cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path=session_cache_path())
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler)
if not auth_manager.validate_token(cache_handler.get_cached_token()):
return redirect('/')
Expand All @@ -96,7 +82,7 @@ def playlists():

@app.route('/currently_playing')
def currently_playing():
cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path=session_cache_path())
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler)
if not auth_manager.validate_token(cache_handler.get_cached_token()):
return redirect('/')
Expand All @@ -109,7 +95,7 @@ def currently_playing():

@app.route('/current_user')
def current_user():
cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path=session_cache_path())
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler)
if not auth_manager.validate_token(cache_handler.get_cached_token()):
return redirect('/')
Expand Down
26 changes: 26 additions & 0 deletions spotipy/cache_handler.py
Expand Up @@ -2,6 +2,7 @@
'CacheHandler',
'CacheFileHandler',
'DjangoSessionCacheHandler',
'FlaskSessionCacheHandler',
'MemoryCacheHandler',
'RedisCacheHandler']

Expand Down Expand Up @@ -147,6 +148,31 @@ def save_token_to_cache(self, token_info):
logger.warning("Error saving token to cache: " + str(e))


class FlaskSessionCacheHandler(CacheHandler):
"""
A cache handler that stores the token info in the session framework
provided by flask.
"""

def __init__(self, session):
self.session = session

def get_cached_token(self):
token_info = None
try:
token_info = self.session["token_info"]
except KeyError:
logger.debug("Token not found in the session")

return token_info

def save_token_to_cache(self, token_info):
try:
self.session["token_info"] = token_info
except Exception as e:
logger.warning("Error saving token to cache: " + str(e))


class RedisCacheHandler(CacheHandler):
"""
A cache handler that stores the token info in the Redis.
Expand Down