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 |
|