spotistats/app.py
2022-10-03 21:48:00 +01:00

114 lines
4 KiB
Python

import matplotlib
# allow running headless
matplotlib.use('agg')
from flask import Flask, render_template, g, abort, redirect, session, request, url_for
from markupsafe import escape
import spotipy
from typing import Optional
from dotenv import load_dotenv
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import io
import base64
load_dotenv()
app = Flask(__name__)
def get_spotify() -> Optional[spotipy.Spotify]:
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(scope='playlist-read-private', cache_handler=cache_handler, show_dialog=True)
if not auth_manager.validate_token(cache_handler.get_cached_token()):
return None
spotify = spotipy.Spotify(auth_manager=auth_manager)
return spotify
@app.route('/playlist/<id>')
def playlist(id: str):
spotify = get_spotify()
if not spotify:
return redirect(url_for('login', return_playlist=id))
try:
playlist = spotify.playlist(playlist_id=id)
tracks = []
result = spotify.playlist_items(playlist_id=id)
tracks.extend(result['items'])
# if playlist is larger than 100 songs, continue loading it until end
while result['next']:
result = spotify.next(result)
tracks.extend(result['items'])
artists = {}
for track in tracks:
for artist in track['track']['artists']:
if artists.get(artist['uri'], None):
artists[artist['uri']]['count'] += 1
else:
artists[artist['uri']] = {
'count': 1,
'name': artist['name'],
}
artists = list(map(lambda artist: artist, artists.values()))
artists.sort(key=lambda artist: artist['name'])
artists.sort(key=lambda artist: -artist['count'])
counts = list(map(lambda artist: artist['count'], artists))
labels = list(map(lambda artist: artist['name'] + f' ({round(artist["count"] / len(tracks) * 100)}%)', artists))
fig = Figure()
axis = fig.add_subplot(1, 1, 1)
axis.pie(counts, labels=labels, rotatelabels=True, explode=[.2] + ([0] * (len(artists) - 1)))
data = io.BytesIO()
FigureCanvas(fig).print_figure(data, format='png', bbox_inches='tight')
pie = base64.b64encode(data.getvalue()).decode('ascii')
except spotipy.SpotifyException as e:
if e.code == 404:
return abort(404)
else:
return abort(500)
return render_template('playlist.html', playlist=playlist, tracks=tracks, track_count=len(tracks), artists=artists, pie=pie)
@app.route('/login')
def login():
cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
auth_manager = spotipy.oauth2.SpotifyOAuth(scope='playlist-read-private', cache_handler=cache_handler, show_dialog=True)
if request.args.get('code'):
auth_manager.get_access_token(request.args.get('code'))
if session.get('return_playlist'):
return redirect(url_for('playlist', id=session.get('return_playlist')))
else:
return redirect('/')
if not auth_manager.validate_token(cache_handler.get_cached_token()):
if request.args.get('return_playlist'):
session['return_playlist'] = request.args.get('return_playlist')
return redirect(auth_manager.get_authorize_url())
return redirect('/')
@app.route('/logout')
def logout():
session.pop("token_info", None)
return redirect('/')
@app.route('/')
def index():
spotify = get_spotify()
if spotify:
playlists = []
result = spotify.current_user_playlists()
playlists.extend(result['items'])
while result['next']:
result = spotify.next(result)
playlists.extend(result['items'])
return render_template('index.html', playlists=playlists)
else:
return render_template('index-login.html')