trhacknon/Spotify
0
1import json2from urllib import request3from fastapi import FastAPI4from starlette.middleware.sessions import SessionMiddleware5from starlette.responses import HTMLResponse, RedirectResponse6from starlette.requests import Request7import gradio as gr8import uvicorn9from fastapi.responses import HTMLResponse10from fastapi.responses import RedirectResponse11import pandas as pd12 13import spotipy14from spotipy import oauth215 16import heatmap17 18import numpy as np19 20import matplotlib.pyplot as plt21from matplotlib.patches import Circle, RegularPolygon22from matplotlib.path import Path23from matplotlib.projections.polar import PolarAxes24from matplotlib.projections import register_projection25from matplotlib.spines import Spine26from matplotlib.transforms import Affine2D27import matplotlib28 29matplotlib.use('SVG')30 31 32def get_features2(spotify):33 features = []34 for index in range(0, 10):35 results = spotify.current_user_saved_tracks(offset=index*50, limit=50)36 track_ids = [item['track']['id'] for item in results['items']]37 features.extend(spotify.audio_features(track_ids))38 39 df = pd.DataFrame(data=features)40 names = [41 'danceability',42 'energy',43 # 'loudness',44 'speechiness',45 'acousticness',46 'instrumentalness',47 'liveness',48 'valence',49 ]50 features_means = df[names].mean()51 return names, features_means.values52 53 54def radar_factory(num_vars, frame='circle'):55 """56 Create a radar chart with `num_vars` axes.57 58 This function creates a RadarAxes projection and registers it.59 60 Parameters61 ----------62 num_vars : int63 Number of variables for radar chart.64 frame : {'circle', 'polygon'}65 Shape of frame surrounding axes.66 67 """68 # calculate evenly-spaced axis angles69 theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)70 71 class RadarTransform(PolarAxes.PolarTransform):72 73 def transform_path_non_affine(self, path):74 # Paths with non-unit interpolation steps correspond to gridlines,75 # in which case we force interpolation (to defeat PolarTransform's76 # autoconversion to circular arcs).77 if path._interpolation_steps > 1:78 path = path.interpolated(num_vars)79 return Path(self.transform(path.vertices), path.codes)80 81 class RadarAxes(PolarAxes):82 83 name = 'radar'84 PolarTransform = RadarTransform85 86 def __init__(self, *args, **kwargs):87 super().__init__(*args, **kwargs)88 # rotate plot such that the first axis is at the top89 self.set_theta_zero_location('N')90 91 def fill(self, *args, closed=True, **kwargs):92 """Override fill so that line is closed by default"""93 return super().fill(closed=closed, *args, **kwargs)94 95 def plot(self, *args, **kwargs):96 """Override plot so that line is closed by default"""97 lines = super().plot(*args, **kwargs)98 for line in lines:99 self._close_line(line)100 101 def _close_line(self, line):102 x, y = line.get_data()103 # FIXME: markers at x[0], y[0] get doubled-up104 if x[0] != x[-1]:105 x = np.append(x, x[0])106 y = np.append(y, y[0])107 line.set_data(x, y)108 109 def set_varlabels(self, labels):110 self.set_thetagrids(np.degrees(theta), labels)111 112 def _gen_axes_patch(self):113 # The Axes patch must be centered at (0.5, 0.5) and of radius 0.5114 # in axes coordinates.115 if frame == 'circle':116 return Circle((0.5, 0.5), 0.5)117 elif frame == 'polygon':118 return RegularPolygon((0.5, 0.5), num_vars,119 radius=.5, edgecolor="k")120 else:121 raise ValueError("Unknown value for 'frame': %s" % frame)122 123 def _gen_axes_spines(self):124 if frame == 'circle':125 return super()._gen_axes_spines()126 elif frame == 'polygon':127 # spine_type must be 'left'/'right'/'top'/'bottom'/'circle'.128 spine = Spine(axes=self,129 spine_type='circle',130 path=Path.unit_regular_polygon(num_vars))131 # unit_regular_polygon gives a polygon of radius 1 centered at132 # (0, 0) but we want a polygon of radius 0.5 centered at (0.5,133 # 0.5) in axes coordinates.134 spine.set_transform(Affine2D().scale(.5).translate(.5, .5)135 + self.transAxes)136 return {'polar': spine}137 else:138 raise ValueError("Unknown value for 'frame': %s" % frame)139 140 register_projection(RadarAxes)141 return theta142 143def get_spider_plot(request: gr.Request):144 token = request.request.session.get('token')145 sp = spotipy.Spotify(token)146 names, data = get_features2(sp)147 148 theta = radar_factory(len(names), frame='polygon')149 150 fig = plt.figure(figsize=(9, 9))151 ax = fig.add_axes([0, 0, 1, 1], projection='radar')152 153 # Plot the four cases from the example data on separate axes154 title = 'test'155 ax.set_rgrids([0.2, 0.4, 0.6, 0.8])156 ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1),157 horizontalalignment='center', verticalalignment='center')158 159 ax.plot(theta, data)160 ax.fill(theta, data, alpha=0.25, label='_nolegend_')161 162 ax.set_varlabels(names)163 164 return fig165 166 167PORT_NUMBER = 8080168SPOTIPY_CLIENT_ID = 'c087fa97cebb4f67b6f08ba841ed8378'169SPOTIPY_CLIENT_SECRET = 'ae27d6916d114ac4bb948bb6c58a72d9'170SPOTIPY_REDIRECT_URI = 'https://hf-hackathon-2023-01-spotify.hf.space'171SCOPE = 'ugc-image-upload user-read-playback-state user-modify-playback-state user-read-currently-playing app-remote-control streaming playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public user-follow-modify user-follow-read user-read-playback-position user-top-read user-read-recently-played user-library-modify user-library-read user-read-email user-read-private'172 173sp_oauth = oauth2.SpotifyOAuth(SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI, scope=SCOPE)174 175app = FastAPI()176app.add_middleware(SessionMiddleware, secret_key="w.o.w")177 178@app.get('/', response_class=HTMLResponse)179async def homepage(request: Request):180 token = request.session.get('token')181 if token:182 return RedirectResponse("/gradio")183 184 url = str(request.url)185 code = sp_oauth.parse_response_code(url)186 if code != url:187 token_info = sp_oauth.get_access_token(code)188 request.session['token'] = token_info['access_token']189 return RedirectResponse("/gradio")190 191 auth_url = sp_oauth.get_authorize_url()192 return "<a href='" + auth_url + "'>Login to Spotify</a>"193 194 195 196from vega_datasets import data197 198iris = data.iris()199 200 201def scatter_plot_fn_energy(request: gr.Request):202 203 token = request.request.session.get('token')204 if token:205 sp = spotipy.Spotify(token)206 results = sp.current_user()207 print(results)208 df = get_features(sp)209 return gr.ScatterPlot(210 value=df,211 x="danceability",212 y="energy"213 )214 215def scatter_plot_fn_liveness(request: gr.Request):216 token = request.request.session.get('token')217 if token:218 sp = spotipy.Spotify(token)219 results = sp.current_user()220 print(results)221 df = get_features(sp)222 print(df)223 return gr.ScatterPlot(224 value=df,225 x="acousticness",226 y="liveness"227 )228 229def heatmap_plot_fn(request: gr.Request):230 token = request.request.session.get('token')231 if token:232 sp = spotipy.Spotify(token)233 data = heatmap.build_heatmap(heatmap.fetch_recent_songs(sp))234 fig, ax = heatmap.plot(data)235 return fig236 237 238def get_features(spotify):239 features = []240 for index in range(0, 10):241 results = spotify.current_user_saved_tracks(offset=index*50, limit=50)242 track_ids = [item['track']['id'] for item in results['items']]243 features.extend(spotify.audio_features(track_ids))244 245 df = pd.DataFrame(data=features)246 names = [247 'danceability',248 'energy',249 'loudness',250 'speechiness',251 'acousticness',252 'instrumentalness',253 'liveness',254 'valence',255 'tempo',256 ]257 258 # print (features_means.to_json())259 return df260 261 262def get_features(spotify):263 features = []264 for index in range(0, 10):265 results = spotify.current_user_saved_tracks(offset=index*50, limit=50)266 track_ids = [item['track']['id'] for item in results['items']]267 features.extend(spotify.audio_features(track_ids))268 269 df = pd.DataFrame(data=features)270 names = [271 'danceability', 272 'energy',273 'loudness',274 'speechiness',275 'acousticness',276 'instrumentalness',277 'liveness',278 'valence',279 'tempo',280 ]281 features_means = df[names].mean()282 # print (features_means.to_json())283 return features_means284 285 286##########287def get_started():288 # redirects to spotify and comes back289 # then generates plots290 return291 292with gr.Blocks() as demo:293 gr.Markdown(" ## Spotify Analyzer 🥳🎉")294 gr.Markdown("This app analyzes how cool your music taste is. We dare you to take this challenge!")295 with gr.Row():296 get_started_btn = gr.Button("Get Started")297 with gr.Row():298 spider_plot = gr.Plot()299 # with gr.Row():300 # with gr.Column():301 # with gr.Row():302 # with gr.Column():303 # energy_plot = gr.ScatterPlot(show_label=False).style(container=True)304 # with gr.Column():305 # liveness_plot = gr.ScatterPlot(show_label=False).style(container=True)306 with gr.Row():307 gr.Markdown(" ### We have recommendations for you!")308 with gr.Row():309 heatmap_plot = gr.Plot()310 with gr.Row():311 gr.Markdown(" ### We have recommendations for you!")312 with gr.Row():313 gr.Dataframe(314 headers=["Song", "Album", "Artist"],315 datatype=["str", "str", "str"],316 label="Reccomended Songs",317 value=[["Fired Up", "Fired Up", "Randy Houser"], ["Something Just Like This", "Memories... Do Not Open", "The Chainsmokers"]] # TODO: replace with actual reccomendations once get_started() is implemeted.318 )319 320 demo.load(fn=get_spider_plot, outputs = spider_plot)321 demo.load(fn=heatmap_plot_fn, outputs = heatmap_plot)322 # demo.load(fn=scatter_plot_fn_energy, outputs = energy_plot)323 # demo.load(fn=scatter_plot_fn_liveness, outputs = liveness_plot)324 325 326gradio_app = gr.mount_gradio_app(app, demo, "/gradio")327uvicorn.run(app, host="0.0.0.0", port=7860)328 