Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -119,18 +119,23 @@ def extract_audio_features(data_tracks, legit_genres):
|
|
119 |
all_tracks_uris = all_tracks_uris[valid_indexes]
|
120 |
all_audio_features = np.array(all_audio_features)[valid_indexes]
|
121 |
all_tracks_audio_features = dict(zip(relevant_audio_features, [[audio_f[k] for audio_f in all_audio_features] for k in relevant_audio_features]))
|
122 |
-
|
|
|
123 |
for index, uri in enumerate(all_tracks_uris):
|
124 |
track = data_tracks[uri]
|
125 |
track_genres = track['track']['genres']
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
129 |
else:
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
134 |
# st.session_state['music_extracted'] = dict(all_tracks_uris=all_tracks_uris,
|
135 |
# all_tracks_audio_features=all_tracks_audio_features,
|
136 |
# genres=genres,
|
@@ -149,11 +154,15 @@ def select_songs(legit_genres):
|
|
149 |
users_playlists = "Add a list of user urls, one per line (optional)"
|
150 |
users_links = st.text_area(users_playlists, value="")
|
151 |
label_playlists = "Add a list of playlists urls, one per line (optional)"
|
152 |
-
playlist_links = st.text_area(label_playlists, value="https://open.spotify.com/playlist/1H7a4q8JZArMQiidRy6qon")
|
153 |
extract_button = centered_button(st.button, 'Extract music', n_columns=5)
|
154 |
|
155 |
-
all_tracks_uris, all_tracks_audio_features,
|
156 |
if extract_button or debug or 'extract_button' in st.session_state.keys():
|
|
|
|
|
|
|
|
|
157 |
st.session_state['extract_button'] = True
|
158 |
# check the user input music sources
|
159 |
if playlist_links == "" and users_links == "":
|
@@ -183,13 +192,13 @@ def select_songs(legit_genres):
|
|
183 |
if len(data_tracks.keys()) < 10:
|
184 |
st.warning('Please select more music sources.')
|
185 |
else:
|
186 |
-
all_tracks_uris, all_tracks_audio_features,
|
187 |
print(f'4. audio feature extraction: {time.time() - init_time:.2f}')
|
188 |
print(f'\t total extraction: {time.time() - init_time_tot:.2f}')
|
189 |
st.success(f'{len(data_tracks.keys())} tracks found!')
|
190 |
-
return all_tracks_uris, all_tracks_audio_features,
|
191 |
|
192 |
-
def customize_widgets(genres_labels):
|
193 |
st.subheader("Step 3: Customize it!")
|
194 |
st.markdown('##### Which genres?')
|
195 |
|
@@ -203,8 +212,9 @@ def customize_widgets(genres_labels):
|
|
203 |
with columns[3]:
|
204 |
uncheck_all = st.button('Uncheck all')
|
205 |
|
206 |
-
if 'checkboxes' not in st.session_state.keys():
|
207 |
st.session_state['checkboxes'] = [True] * len(genres_labels)
|
|
|
208 |
|
209 |
empty_checkboxes = wall_of_checkboxes(genres_labels, max_width=5)
|
210 |
if check_all:
|
@@ -228,12 +238,12 @@ def customize_widgets(genres_labels):
|
|
228 |
return target_mood, exploration
|
229 |
|
230 |
@st.cache
|
231 |
-
def filter_songs_by_genre(checkboxes, genres_labels,
|
232 |
# filter songs by genres
|
233 |
selected_labels = [genres_labels[i] for i in range(len(genres_labels)) if checkboxes[i]]
|
234 |
genre_selected_indexes = []
|
235 |
for label in selected_labels:
|
236 |
-
genre_selected_indexes +=
|
237 |
genre_selected_indexes = np.array(sorted(set(genre_selected_indexes)))
|
238 |
return genre_selected_indexes
|
239 |
|
@@ -246,7 +256,7 @@ def find_best_songs_for_mood(all_tracks_audio_features, genre_selected_indexes,
|
|
246 |
return min_dist_indexes, n_candidates
|
247 |
|
248 |
@st.cache
|
249 |
-
def run_exploration(selected_tracks_uris, playlist_length, exploration, all_tracks_uris, target_mood, reauthenticate):
|
250 |
# sample exploration songs
|
251 |
if exploration > 0:
|
252 |
n_known = int(playlist_length * (1 - exploration))
|
@@ -254,6 +264,7 @@ def run_exploration(selected_tracks_uris, playlist_length, exploration, all_trac
|
|
254 |
print(f'Number of new songs: {n_new}, known songs: {n_known}')
|
255 |
known_songs = selected_tracks_uris[:n_known]
|
256 |
seed_songs = selected_tracks_uris[-n_new:]
|
|
|
257 |
dict_args = dict() # enforce bounds on recommendations' moods
|
258 |
for i_m, m in enumerate(['valence', 'energy', 'danceability']):
|
259 |
dict_args[f'min_{m}'] = max(0, target_mood[i_m] - 0.1)
|
@@ -268,18 +279,20 @@ def run_exploration(selected_tracks_uris, playlist_length, exploration, all_trac
|
|
268 |
dict_args_loose[f'max_{m}'] = min(1, target_mood[i_m] + 0.3)
|
269 |
new_songs = []
|
270 |
counter_seed = 0
|
271 |
-
counter_failure = 0
|
272 |
while len(new_songs) < n_new:
|
273 |
try:
|
274 |
print(seed_songs[counter_seed])
|
275 |
print(dict_args)
|
276 |
-
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]],
|
|
|
277 |
if len(reco) == 0:
|
278 |
print('Using loose bounds')
|
279 |
-
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]],
|
|
|
280 |
if len(reco) == 0:
|
281 |
print('Using looser bounds')
|
282 |
-
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]],
|
|
|
283 |
if len(reco) == 0:
|
284 |
print('Removing bounds')
|
285 |
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], market="from_token")['tracks']
|
@@ -300,17 +313,20 @@ def run_exploration(selected_tracks_uris, playlist_length, exploration, all_trac
|
|
300 |
return selected_tracks_uris
|
301 |
|
302 |
@st.cache
|
303 |
-
def sample_playlist(n_candidates, playlist_length, genre_selected_indexes, min_dist_indexes, all_tracks_uris):
|
304 |
# give more freedom to randomize the playlist
|
305 |
if n_candidates > 5 * playlist_length:
|
306 |
selected_tracks_indexes = genre_selected_indexes[min_dist_indexes[:int(playlist_length * 2)]]
|
307 |
|
308 |
else:
|
309 |
selected_tracks_indexes = genre_selected_indexes[min_dist_indexes[:playlist_length]]
|
310 |
-
|
311 |
-
np.random.shuffle(
|
|
|
|
|
312 |
selected_tracks_uris = selected_tracks_uris[:playlist_length]
|
313 |
-
|
|
|
314 |
|
315 |
def run_app():
|
316 |
global sp
|
@@ -333,16 +349,16 @@ def run_app():
|
|
333 |
return sp
|
334 |
|
335 |
if 'login' in st.session_state or debug:
|
336 |
-
all_tracks_uris, all_tracks_audio_features,
|
337 |
|
338 |
if all_tracks_uris is not None:
|
339 |
-
target_mood, exploration = customize_widgets(genres_labels)
|
340 |
custom_button = centered_button(st.button, 'Run customization', n_columns=5)
|
341 |
-
if custom_button or 'run_custom' in st.session_state.keys():
|
342 |
st.session_state['run_custom'] = True
|
343 |
checkboxes = st.session_state['checkboxes'].copy()
|
344 |
init_time = time.time()
|
345 |
-
genre_selected_indexes = filter_songs_by_genre(checkboxes, genres_labels,
|
346 |
if len(genre_selected_indexes) < 10:
|
347 |
genre_selected_indexes = None
|
348 |
st.warning('Please select more genres or add more music sources.')
|
@@ -361,7 +377,8 @@ def run_app():
|
|
361 |
playlist_length = st.number_input(f'Pick a playlist length, given {n_candidates} candidates.', min_value=5,
|
362 |
value=min(10, n_candidates//3), max_value=n_candidates//3)
|
363 |
|
364 |
-
selected_tracks_uris = sample_playlist(n_candidates, playlist_length, genre_selected_indexes,
|
|
|
365 |
print(f'8. Sample songs: {time.time() - init_time:.2f}')
|
366 |
init_time = time.time()
|
367 |
|
@@ -371,7 +388,8 @@ def run_app():
|
|
371 |
else:
|
372 |
generation_button = centered_button(st.button, 'Generate playlist', n_columns=5)
|
373 |
if generation_button:
|
374 |
-
selected_tracks_uris = run_exploration(selected_tracks_uris, playlist_length, exploration, all_tracks_uris,
|
|
|
375 |
print(f'9. run exploration: {time.time() - init_time:.2f}')
|
376 |
init_time = time.time()
|
377 |
|
|
|
119 |
all_tracks_uris = all_tracks_uris[valid_indexes]
|
120 |
all_audio_features = np.array(all_audio_features)[valid_indexes]
|
121 |
all_tracks_audio_features = dict(zip(relevant_audio_features, [[audio_f[k] for audio_f in all_audio_features] for k in relevant_audio_features]))
|
122 |
+
all_tracks_genres = []
|
123 |
+
indexes_by_genre = dict()
|
124 |
for index, uri in enumerate(all_tracks_uris):
|
125 |
track = data_tracks[uri]
|
126 |
track_genres = track['track']['genres']
|
127 |
+
all_tracks_genres.append([])
|
128 |
+
for glabel in track_genres:
|
129 |
+
legit_genre = find_legit_genre(glabel, legit_genres)
|
130 |
+
if legit_genre in indexes_by_genre.keys():
|
131 |
+
indexes_by_genre[legit_genre].append(index)
|
132 |
else:
|
133 |
+
indexes_by_genre[legit_genre] = [index]
|
134 |
+
all_tracks_genres[-1].append(legit_genre)
|
135 |
+
all_tracks_genres[-1] = sorted(set(all_tracks_genres[-1]))
|
136 |
+
genres_labels = sorted(indexes_by_genre.keys())
|
137 |
+
all_tracks_genres = np.array(all_tracks_genres)
|
138 |
+
return all_tracks_uris, all_tracks_audio_features, all_tracks_genres, indexes_by_genre, genres_labels
|
139 |
# st.session_state['music_extracted'] = dict(all_tracks_uris=all_tracks_uris,
|
140 |
# all_tracks_audio_features=all_tracks_audio_features,
|
141 |
# genres=genres,
|
|
|
154 |
users_playlists = "Add a list of user urls, one per line (optional)"
|
155 |
users_links = st.text_area(users_playlists, value="")
|
156 |
label_playlists = "Add a list of playlists urls, one per line (optional)"
|
157 |
+
playlist_links = st.text_area(label_playlists, value="https://open.spotify.com/playlist/1H7a4q8JZArMQiidRy6qon\nhttps://open.spotify.com/playlist/6wbaZqht4w6CMv3od5taax?si=5c6ebe13fdd049b6")
|
158 |
extract_button = centered_button(st.button, 'Extract music', n_columns=5)
|
159 |
|
160 |
+
all_tracks_uris, all_tracks_audio_features, all_tracks_genres, indexes_by_genre, genres_labels = [None] * 5
|
161 |
if extract_button or debug or 'extract_button' in st.session_state.keys():
|
162 |
+
if extract_button:
|
163 |
+
updated_sources = True
|
164 |
+
else:
|
165 |
+
updated_sources = False
|
166 |
st.session_state['extract_button'] = True
|
167 |
# check the user input music sources
|
168 |
if playlist_links == "" and users_links == "":
|
|
|
192 |
if len(data_tracks.keys()) < 10:
|
193 |
st.warning('Please select more music sources.')
|
194 |
else:
|
195 |
+
all_tracks_uris, all_tracks_audio_features, all_tracks_genres, indexes_by_genre, genres_labels = extract_audio_features(data_tracks, legit_genres)
|
196 |
print(f'4. audio feature extraction: {time.time() - init_time:.2f}')
|
197 |
print(f'\t total extraction: {time.time() - init_time_tot:.2f}')
|
198 |
st.success(f'{len(data_tracks.keys())} tracks found!')
|
199 |
+
return all_tracks_uris, all_tracks_audio_features, all_tracks_genres, indexes_by_genre, genres_labels, updated_sources
|
200 |
|
201 |
+
def customize_widgets(genres_labels, updated_sources):
|
202 |
st.subheader("Step 3: Customize it!")
|
203 |
st.markdown('##### Which genres?')
|
204 |
|
|
|
212 |
with columns[3]:
|
213 |
uncheck_all = st.button('Uncheck all')
|
214 |
|
215 |
+
if 'checkboxes' not in st.session_state.keys() or updated_sources:
|
216 |
st.session_state['checkboxes'] = [True] * len(genres_labels)
|
217 |
+
updated_sources = False
|
218 |
|
219 |
empty_checkboxes = wall_of_checkboxes(genres_labels, max_width=5)
|
220 |
if check_all:
|
|
|
238 |
return target_mood, exploration
|
239 |
|
240 |
@st.cache
|
241 |
+
def filter_songs_by_genre(checkboxes, genres_labels, indexes_by_genre):
|
242 |
# filter songs by genres
|
243 |
selected_labels = [genres_labels[i] for i in range(len(genres_labels)) if checkboxes[i]]
|
244 |
genre_selected_indexes = []
|
245 |
for label in selected_labels:
|
246 |
+
genre_selected_indexes += indexes_by_genre[label]
|
247 |
genre_selected_indexes = np.array(sorted(set(genre_selected_indexes)))
|
248 |
return genre_selected_indexes
|
249 |
|
|
|
256 |
return min_dist_indexes, n_candidates
|
257 |
|
258 |
@st.cache
|
259 |
+
def run_exploration(selected_tracks_uris, selected_tracks_genres, playlist_length, exploration, all_tracks_uris, target_mood, reauthenticate):
|
260 |
# sample exploration songs
|
261 |
if exploration > 0:
|
262 |
n_known = int(playlist_length * (1 - exploration))
|
|
|
264 |
print(f'Number of new songs: {n_new}, known songs: {n_known}')
|
265 |
known_songs = selected_tracks_uris[:n_known]
|
266 |
seed_songs = selected_tracks_uris[-n_new:]
|
267 |
+
seed_genres = selected_tracks_genres[-n_new:]
|
268 |
dict_args = dict() # enforce bounds on recommendations' moods
|
269 |
for i_m, m in enumerate(['valence', 'energy', 'danceability']):
|
270 |
dict_args[f'min_{m}'] = max(0, target_mood[i_m] - 0.1)
|
|
|
279 |
dict_args_loose[f'max_{m}'] = min(1, target_mood[i_m] + 0.3)
|
280 |
new_songs = []
|
281 |
counter_seed = 0
|
|
|
282 |
while len(new_songs) < n_new:
|
283 |
try:
|
284 |
print(seed_songs[counter_seed])
|
285 |
print(dict_args)
|
286 |
+
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], seed_genres=seed_genres[counter_seed],
|
287 |
+
market="from_token", country='from_token', **dict_args)['tracks']
|
288 |
if len(reco) == 0:
|
289 |
print('Using loose bounds')
|
290 |
+
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], seed_genres=seed_genres[counter_seed],
|
291 |
+
market="from_token", country='from_token', **dict_args_loose)['tracks']
|
292 |
if len(reco) == 0:
|
293 |
print('Using looser bounds')
|
294 |
+
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], seed_genres=seed_genres[counter_seed],
|
295 |
+
market="from_token", country='from_token', **dict_args_looser)['tracks']
|
296 |
if len(reco) == 0:
|
297 |
print('Removing bounds')
|
298 |
reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], market="from_token")['tracks']
|
|
|
313 |
return selected_tracks_uris
|
314 |
|
315 |
@st.cache
|
316 |
+
def sample_playlist(n_candidates, playlist_length, genre_selected_indexes, min_dist_indexes, all_tracks_uris, all_tracks_genres):
|
317 |
# give more freedom to randomize the playlist
|
318 |
if n_candidates > 5 * playlist_length:
|
319 |
selected_tracks_indexes = genre_selected_indexes[min_dist_indexes[:int(playlist_length * 2)]]
|
320 |
|
321 |
else:
|
322 |
selected_tracks_indexes = genre_selected_indexes[min_dist_indexes[:playlist_length]]
|
323 |
+
shuffled_indexes = np.arange(len(selected_tracks_indexes))
|
324 |
+
np.random.shuffle(shuffled_indexes)
|
325 |
+
selected_tracks_uris = all_tracks_uris[selected_tracks_indexes][shuffled_indexes]
|
326 |
+
selected_tracks_genres = all_tracks_genres[selected_tracks_indexes][shuffled_indexes]
|
327 |
selected_tracks_uris = selected_tracks_uris[:playlist_length]
|
328 |
+
selected_tracks_genres = selected_tracks_genres[:playlist_length]
|
329 |
+
return selected_tracks_uris, selected_tracks_genres
|
330 |
|
331 |
def run_app():
|
332 |
global sp
|
|
|
349 |
return sp
|
350 |
|
351 |
if 'login' in st.session_state or debug:
|
352 |
+
all_tracks_uris, all_tracks_audio_features, all_tracks_genres, indexes_by_genre, genres_labels, updated_sources = select_songs(legit_genres)
|
353 |
|
354 |
if all_tracks_uris is not None:
|
355 |
+
target_mood, exploration = customize_widgets(genres_labels, updated_sources)
|
356 |
custom_button = centered_button(st.button, 'Run customization', n_columns=5)
|
357 |
+
if custom_button or 'run_custom' in st.session_state.keys() or debug:
|
358 |
st.session_state['run_custom'] = True
|
359 |
checkboxes = st.session_state['checkboxes'].copy()
|
360 |
init_time = time.time()
|
361 |
+
genre_selected_indexes = filter_songs_by_genre(checkboxes, genres_labels, indexes_by_genre)
|
362 |
if len(genre_selected_indexes) < 10:
|
363 |
genre_selected_indexes = None
|
364 |
st.warning('Please select more genres or add more music sources.')
|
|
|
377 |
playlist_length = st.number_input(f'Pick a playlist length, given {n_candidates} candidates.', min_value=5,
|
378 |
value=min(10, n_candidates//3), max_value=n_candidates//3)
|
379 |
|
380 |
+
selected_tracks_uris, selected_tracks_genres = sample_playlist(n_candidates, playlist_length, genre_selected_indexes,
|
381 |
+
min_dist_indexes, all_tracks_uris, all_tracks_genres)
|
382 |
print(f'8. Sample songs: {time.time() - init_time:.2f}')
|
383 |
init_time = time.time()
|
384 |
|
|
|
388 |
else:
|
389 |
generation_button = centered_button(st.button, 'Generate playlist', n_columns=5)
|
390 |
if generation_button:
|
391 |
+
selected_tracks_uris = run_exploration(selected_tracks_uris, selected_tracks_genres, playlist_length, exploration, all_tracks_uris,
|
392 |
+
target_mood.flatten(), reauthenticate)
|
393 |
print(f'9. run exploration: {time.time() - init_time:.2f}')
|
394 |
init_time = time.time()
|
395 |
|