Spotify / app.py
co42's picture
co42 HF staff
Update app.py
f7e616d
raw
history blame
9.36 kB
import json
from urllib import request
from fastapi import FastAPI
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from starlette.requests import Request
import gradio as gr
import uvicorn
from fastapi.responses import HTMLResponse
from fastapi.responses import RedirectResponse
import pandas as pd
import spotipy
from spotipy import oauth2
import heatmap
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, RegularPolygon
from matplotlib.path import Path
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection
from matplotlib.spines import Spine
from matplotlib.transforms import Affine2D
import matplotlib
matplotlib.use('SVG')
def get_features2(spotify):
features = []
for index in range(0, 10):
results = spotify.current_user_saved_tracks(offset=index*50, limit=50)
track_ids = [item['track']['id'] for item in results['items']]
features.extend(spotify.audio_features(track_ids))
df = pd.DataFrame(data=features)
names = [
'danceability',
'energy',
# 'loudness',
'speechiness',
'acousticness',
'instrumentalness',
'liveness',
'valence',
]
features_means = df[names].mean()
return names, features_means.values
def radar_factory(num_vars, frame='circle'):
"""
Create a radar chart with `num_vars` axes.
This function creates a RadarAxes projection and registers it.
Parameters
----------
num_vars : int
Number of variables for radar chart.
frame : {'circle', 'polygon'}
Shape of frame surrounding axes.
"""
# calculate evenly-spaced axis angles
theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)
class RadarTransform(PolarAxes.PolarTransform):
def transform_path_non_affine(self, path):
# Paths with non-unit interpolation steps correspond to gridlines,
# in which case we force interpolation (to defeat PolarTransform's
# autoconversion to circular arcs).
if path._interpolation_steps > 1:
path = path.interpolated(num_vars)
return Path(self.transform(path.vertices), path.codes)
class RadarAxes(PolarAxes):
name = 'radar'
PolarTransform = RadarTransform
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# rotate plot such that the first axis is at the top
self.set_theta_zero_location('N')
def fill(self, *args, closed=True, **kwargs):
"""Override fill so that line is closed by default"""
return super().fill(closed=closed, *args, **kwargs)
def plot(self, *args, **kwargs):
"""Override plot so that line is closed by default"""
lines = super().plot(*args, **kwargs)
for line in lines:
self._close_line(line)
def _close_line(self, line):
x, y = line.get_data()
# FIXME: markers at x[0], y[0] get doubled-up
if x[0] != x[-1]:
x = np.append(x, x[0])
y = np.append(y, y[0])
line.set_data(x, y)
def set_varlabels(self, labels):
self.set_thetagrids(np.degrees(theta), labels)
def _gen_axes_patch(self):
# The Axes patch must be centered at (0.5, 0.5) and of radius 0.5
# in axes coordinates.
if frame == 'circle':
return Circle((0.5, 0.5), 0.5)
elif frame == 'polygon':
return RegularPolygon((0.5, 0.5), num_vars,
radius=.5, edgecolor="k")
else:
raise ValueError("Unknown value for 'frame': %s" % frame)
def _gen_axes_spines(self):
if frame == 'circle':
return super()._gen_axes_spines()
elif frame == 'polygon':
# spine_type must be 'left'/'right'/'top'/'bottom'/'circle'.
spine = Spine(axes=self,
spine_type='circle',
path=Path.unit_regular_polygon(num_vars))
# unit_regular_polygon gives a polygon of radius 1 centered at
# (0, 0) but we want a polygon of radius 0.5 centered at (0.5,
# 0.5) in axes coordinates.
spine.set_transform(Affine2D().scale(.5).translate(.5, .5)
+ self.transAxes)
return {'polar': spine}
else:
raise ValueError("Unknown value for 'frame': %s" % frame)
register_projection(RadarAxes)
return theta
def get_spider_plot(request: gr.Request):
token = request.request.session.get('token')
sp = spotipy.Spotify(token)
names, data = get_features2(sp)
theta = radar_factory(len(names), frame='polygon')
fig = plt.figure(figsize=(9, 9))
ax = fig.add_axes([0, 0, 1, 1], projection='radar')
# Plot the four cases from the example data on separate axes
title = 'test'
ax.set_rgrids([0.2, 0.4, 0.6, 0.8])
ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1),
horizontalalignment='center', verticalalignment='center')
ax.plot(theta, data)
ax.fill(theta, data, alpha=0.25, label='_nolegend_')
ax.set_varlabels(names)
return fig
PORT_NUMBER = 8080
SPOTIPY_CLIENT_ID = 'c087fa97cebb4f67b6f08ba841ed8378'
SPOTIPY_CLIENT_SECRET = 'ae27d6916d114ac4bb948bb6c58a72d9'
SPOTIPY_REDIRECT_URI = 'https://hf-hackathon-2023-01-spotify.hf.space'
SCOPE = 'user-library-read'
sp_oauth = oauth2.SpotifyOAuth(SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI, scope=SCOPE)
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="w.o.w")
@app.get('/', response_class=HTMLResponse)
async def homepage(request: Request):
token = request.session.get('token')
if token:
return RedirectResponse("/gradio")
url = str(request.url)
code = sp_oauth.parse_response_code(url)
if code != url:
token_info = sp_oauth.get_access_token(code)
request.session['token'] = token_info['access_token']
return RedirectResponse("/gradio")
auth_url = sp_oauth.get_authorize_url()
return "<a href='" + auth_url + "'>Login to Spotify</a>"
from vega_datasets import data
iris = data.iris()
def scatter_plot_fn(request: gr.Request):
token = request.request.session.get('token')
if token:
sp = spotipy.Spotify(token)
results = sp.current_user()
print(results)
return gr.ScatterPlot(
value=iris,
)
def heatmap_plot_fn(request: gr.Request):
token = request.request.session.get('token')
if token:
sp = spotipy.Spotify(token)
data = heatmap.build_heatmap(heatmap.fetch_recent_songs(sp))
fig, _ = heatmap.plot(data)
return fig
def get_features(spotify):
features = []
for index in range(0, 10):
results = spotify.current_user_saved_tracks(offset=index*50, limit=50)
track_ids = [item['track']['id'] for item in results['items']]
features.extend(spotify.audio_features(track_ids))
df = pd.DataFrame(data=features)
names = [
'danceability',
'energy',
'loudness',
'speechiness',
'acousticness',
'instrumentalness',
'liveness',
'valence',
'tempo',
]
features_means = df[names].mean()
# print (features_means.to_json())
return features_means
##########
def get_started():
# redirects to spotify and comes back
# then generates plots
return
with gr.Blocks() as demo:
gr.Markdown(" ## Spotify Analyzer 🥳🎉")
gr.Markdown("This app analyzes how cool your music taste is. We dare you to take this challenge!")
with gr.Row():
get_started_btn = gr.Button("Get Started")
with gr.Row():
spider_plot = gr.Plot()
with gr.Row():
with gr.Column():
with gr.Row():
with gr.Column():
hm_plot = gr.Plot().style(container=True)
with gr.Column():
plot = gr.ScatterPlot(show_label=False).style(container=True)
with gr.Row():
with gr.Column():
plot = gr.ScatterPlot(show_label=False).style(container=True)
with gr.Column():
plot = gr.ScatterPlot(show_label=False).style(container=True)
with gr.Row():
gr.Markdown(" ### We have recommendations for you!")
with gr.Row():
gr.Dataframe(
headers=["Song", "Album", "Artist"],
datatype=["str", "str", "str"],
label="Reccomended Songs",
value=[["something", "something", "something"], ["something", "something", "something"]] # TODO: replace with actual reccomendations once get_started() is implemeted.
)
#demo.load(fn=scatter_plot_fn, outputs=plot)
demo.load(fn=heatmap_plot_fn, output=hm_plot)
demo.load(fn=get_spider_plot, outputs=spider_plot)
gradio_app = gr.mount_gradio_app(app, demo, "/gradio")
uvicorn.run(app, host="0.0.0.0", port=7860)