ccolas commited on
Commit
dbea0da
·
1 Parent(s): 9be7350

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -31
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
- genres = dict()
 
123
  for index, uri in enumerate(all_tracks_uris):
124
  track = data_tracks[uri]
125
  track_genres = track['track']['genres']
126
- for g in track_genres:
127
- if g not in genres.keys():
128
- genres[g] = [index]
 
 
129
  else:
130
- genres[g].append(index)
131
- genres = aggregate_genres(genres, legit_genres)
132
- genres_labels = sorted(genres.keys())
133
- return all_tracks_uris, all_tracks_audio_features, genres, genres_labels
 
 
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, genres, genres_labels = [None] * 4
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, genres, genres_labels = extract_audio_features(data_tracks, legit_genres)
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, genres, genres_labels
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, genres):
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 += genres[label]
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]], market="from_token", **dict_args)['tracks']
 
277
  if len(reco) == 0:
278
  print('Using loose bounds')
279
- reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], market="from_token", **dict_args_loose)['tracks']
 
280
  if len(reco) == 0:
281
  print('Using looser bounds')
282
- reco = sp.recommendations(seed_tracks=[seed_songs[counter_seed]], market="from_token", **dict_args_looser)['tracks']
 
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
- selected_tracks_uris = all_tracks_uris[selected_tracks_indexes]
311
- np.random.shuffle(selected_tracks_uris)
 
 
312
  selected_tracks_uris = selected_tracks_uris[:playlist_length]
313
- return selected_tracks_uris
 
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, genres, genres_labels = select_songs(legit_genres)
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, genres)
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, min_dist_indexes, all_tracks_uris)
 
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, target_mood.flatten(), reauthenticate)
 
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