trhacknon sbrandeis HF Staff commited on
Commit
78f1a69
·
0 Parent(s):

Duplicate from hf-hackathon-2023-01/Spotify

Browse files

Co-authored-by: Simon Brandeis <[email protected]>

Files changed (5) hide show
  1. .gitattributes +34 -0
  2. README.md +13 -0
  3. app.py +327 -0
  4. heatmap.py +39 -0
  5. requirements.txt +6 -0
.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Spotify
3
+ emoji: 🎶
4
+ colorFrom: green
5
+ colorTo: yellow
6
+ sdk: gradio
7
+ sdk_version: 3.16.1
8
+ app_file: app.py
9
+ pinned: false
10
+ duplicated_from: hf-hackathon-2023-01/Spotify
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from urllib import request
3
+ from fastapi import FastAPI
4
+ from starlette.middleware.sessions import SessionMiddleware
5
+ from starlette.responses import HTMLResponse, RedirectResponse
6
+ from starlette.requests import Request
7
+ import gradio as gr
8
+ import uvicorn
9
+ from fastapi.responses import HTMLResponse
10
+ from fastapi.responses import RedirectResponse
11
+ import pandas as pd
12
+
13
+ import spotipy
14
+ from spotipy import oauth2
15
+
16
+ import heatmap
17
+
18
+ import numpy as np
19
+
20
+ import matplotlib.pyplot as plt
21
+ from matplotlib.patches import Circle, RegularPolygon
22
+ from matplotlib.path import Path
23
+ from matplotlib.projections.polar import PolarAxes
24
+ from matplotlib.projections import register_projection
25
+ from matplotlib.spines import Spine
26
+ from matplotlib.transforms import Affine2D
27
+ import matplotlib
28
+
29
+ matplotlib.use('SVG')
30
+
31
+
32
+ def 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.values
52
+
53
+
54
+ def 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
+ Parameters
61
+ ----------
62
+ num_vars : int
63
+ Number of variables for radar chart.
64
+ frame : {'circle', 'polygon'}
65
+ Shape of frame surrounding axes.
66
+
67
+ """
68
+ # calculate evenly-spaced axis angles
69
+ 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's
76
+ # 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 = RadarTransform
85
+
86
+ def __init__(self, *args, **kwargs):
87
+ super().__init__(*args, **kwargs)
88
+ # rotate plot such that the first axis is at the top
89
+ 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-up
104
+ 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.5
114
+ # 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 at
132
+ # (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 theta
142
+
143
+ def 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 axes
154
+ 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 fig
165
+
166
+
167
+ PORT_NUMBER = 8080
168
+ SPOTIPY_CLIENT_ID = 'c087fa97cebb4f67b6f08ba841ed8378'
169
+ SPOTIPY_CLIENT_SECRET = 'ae27d6916d114ac4bb948bb6c58a72d9'
170
+ SPOTIPY_REDIRECT_URI = 'https://hf-hackathon-2023-01-spotify.hf.space'
171
+ SCOPE = '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
+
173
+ sp_oauth = oauth2.SpotifyOAuth(SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI, scope=SCOPE)
174
+
175
+ app = FastAPI()
176
+ app.add_middleware(SessionMiddleware, secret_key="w.o.w")
177
+
178
+ @app.get('/', response_class=HTMLResponse)
179
+ async 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
+
196
+ from vega_datasets import data
197
+
198
+ iris = data.iris()
199
+
200
+
201
+ def 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
+
215
+ def 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
+
229
+ def 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 fig
236
+
237
+
238
+ def 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 df
260
+
261
+
262
+ def 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_means
284
+
285
+
286
+ ##########
287
+ def get_started():
288
+ # redirects to spotify and comes back
289
+ # then generates plots
290
+ return
291
+
292
+ with 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
+
326
+ gradio_app = gr.mount_gradio_app(app, demo, "/gradio")
327
+ uvicorn.run(app, host="0.0.0.0", port=7860)
heatmap.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from typing import List
3
+ import numpy as np
4
+ from spotipy import Spotify
5
+ from dateutil.parser import parse
6
+ import matplotlib.pyplot as plt
7
+
8
+ def fetch_recent_songs(client: Spotify):
9
+ cursor = client.current_user_recently_played()
10
+ recently_played: List[dict] = cursor["items"]
11
+
12
+ max_iterations = 30
13
+ it = 0
14
+ while it < max_iterations and cursor["cursors"] is not None:
15
+ cursor = client.current_user_recently_played(before=cursor["cursors"]["before"])
16
+ recently_played.extend(cursor["items"])
17
+
18
+ return recently_played
19
+
20
+ def build_heatmap(recent_songs: List[dict]) -> np.ndarray:
21
+ heatmap = np.zeros((7, 20))
22
+ now = datetime.now().astimezone()
23
+
24
+ for track in recent_songs:
25
+ played_at = parse(track["played_at"])
26
+ weekday = datetime.weekday(played_at)
27
+ week_offset = (now - played_at).days // 7
28
+ heatmap[weekday, -(week_offset +1)] +=1
29
+ return heatmap
30
+
31
+
32
+ def plot(heatmap: np.ndarray):
33
+ fig, ax = plt.subplots()
34
+
35
+ ax.imshow(heatmap, cmap="Greens")
36
+ ax.set_ylim(0, 6)
37
+ ax.set_title("Recent activity")
38
+ ax.set_yticklabels(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"])
39
+ return fig, ax
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ spotipy==2.22.0
2
+ vega-datasets==0.9.0
3
+ Authlib==1.2.0
4
+ flask==2.2.2
5
+ pandas==1.5.2
6
+ matplotlib