sjw commited on
Commit
8196b26
·
1 Parent(s): f37e32a

Upload final_funcs.py

Browse files
Files changed (1) hide show
  1. final_funcs.py +640 -0
final_funcs.py ADDED
@@ -0,0 +1,640 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from spotipy.oauth2 import SpotifyOAuth
2
+ import spotipy
3
+ from spotipy.exceptions import SpotifyException
4
+ from urllib.parse import urlparse, parse_qs
5
+ import gradio as gr
6
+ from sklearn.metrics.pairwise import cosine_similarity
7
+ import lyricsgenius
8
+ import re
9
+ import requests
10
+ from bs4 import BeautifulSoup
11
+ from langchain.schema import HumanMessage, SystemMessage
12
+ import os
13
+ from sentence_transformers import SentenceTransformer
14
+ from fuzzywuzzy import fuzz
15
+ import random
16
+
17
+
18
+
19
+ ### ### ### Authentication ### ### ###
20
+
21
+
22
+ redirect_uri = "https://huggingface.co/sjw"
23
+ scope= ['user-library-read',
24
+ 'user-read-playback-state',
25
+ 'user-modify-playback-state',
26
+ 'playlist-modify-public',
27
+ 'user-top-read']
28
+
29
+
30
+ sp = None
31
+ device_id = None
32
+ def spotify_auth(client_id, client_secret, code=None):
33
+ global sp # global variables to power the Spotify functions
34
+ global device_id
35
+ auth_manager = SpotifyOAuth(client_id=client_id,
36
+ client_secret=client_secret,
37
+ redirect_uri=redirect_uri,
38
+ scope=scope)
39
+
40
+ if code:
41
+ parsed_url = urlparse(code) # to paste entire link and not just code portion
42
+ code = parse_qs(parsed_url.query)['code'][0]
43
+ auth_manager.get_access_token(code=code, as_dict=False)
44
+
45
+ sp = spotipy.Spotify(auth_manager=auth_manager)
46
+ devices = sp.devices()
47
+ device_id = devices['devices'][0]['id']
48
+
49
+ return f"Spotify Authentication Success.\n\nYour Device:\n{devices}"
50
+ else:
51
+ auth_url = auth_manager.get_authorize_url()
52
+ return f"Authenticate your Spotify [here]({auth_url}).\n\nCopy the entire redirect url and paste below."
53
+
54
+
55
+ global_vars = {'OPENAI_API_KEY': None} # different format from above because importing elsewhere
56
+ def openai_auth(key):
57
+ global_vars['OPENAI_API_KEY'] = key
58
+ return "OpenAI Authentication Success."
59
+
60
+
61
+ def end_session():
62
+ global_vars['OPENAI_API_KEY'] = None
63
+ sp = None
64
+ device_id = None
65
+ return "You have been logged out."
66
+
67
+
68
+ with gr.Blocks() as auth:
69
+ #gr.Markdown("...") UPDATE
70
+
71
+ #with gr.Tab("Authentication"): UPDATE
72
+
73
+ with gr.Row():
74
+ client_id = gr.Textbox(label="Spotify Client ID")
75
+ client_secret = gr.Textbox(label="Spotify Client Secret")
76
+
77
+ code_button = gr.Button("Submit Credentials")
78
+ code_result = gr.Markdown()
79
+ code = gr.Textbox(label="Paste Entire Redirect URL")
80
+
81
+ auth_button = gr.Button("Authenticate Spotify")
82
+ auth_result = gr.Markdown()
83
+
84
+ key = gr.Textbox(label="Paste OpenAI API Key")
85
+ key_button = gr.Button("Activate Key")
86
+ key_result = gr.Markdown()
87
+
88
+ logout_button = gr.Button("End Your Session")
89
+
90
+ code_button.click(fn=spotify_auth, inputs=[client_id, client_secret], outputs=code_result)
91
+ auth_button.click(fn=spotify_auth, inputs=[client_id, client_secret, code], outputs=auth_result)
92
+ key_button.click(fn=openai_auth, inputs=[key], outputs=key_result)
93
+ logout_button.click(fn=end_session)
94
+
95
+
96
+ ### ### ### Basic Functions ### ### ###
97
+
98
+
99
+ def find_track_by_name(track_name):
100
+ if sp is None:
101
+ return f"Spotify client not initialized. Authenticate Spotify first."
102
+
103
+ results = sp.search(q=track_name, type='track')
104
+ track_uri = results['tracks']['items'][0]['uri']
105
+ return track_uri
106
+
107
+
108
+ def play_track_by_name(track_name):
109
+ if sp is None or device_id is None:
110
+ return f"Spotify client not initialized. Authenticate Spotify first."
111
+
112
+ track_uri = find_track_by_name(track_name)
113
+ track_name = sp.track(track_uri)["name"]
114
+ artist_name = sp.track(track_uri)['artists'][0]['name']
115
+ try:
116
+ sp.start_playback(device_id=device_id, uris=[track_uri])
117
+ return f"♫ Now playing {track_name} by {artist_name} ♫"
118
+ except SpotifyException as e:
119
+ return f"An error occurred with Spotify: {e}. \n\n**Remember to wake up Spotify.**"
120
+ except Exception as e:
121
+ return f"An unexpected error occurred: {e}."
122
+
123
+
124
+ def queue_track_by_name(track_name):
125
+ """
126
+ Queues track given its name
127
+ """
128
+ if sp is None or device_id is None:
129
+ return f"Spotify client not initialized. Authenticate Spotify first."
130
+
131
+ track_uri = find_track_by_name(track_name)
132
+ track_name = sp.track(track_uri)["name"]
133
+ sp.add_to_queue(uri=track_uri, device_id=device_id)
134
+ return f"♫ Added {track_name} to your queue ♫"
135
+
136
+
137
+ def pause_track():
138
+ if sp is None or device_id is None:
139
+ return f"Spotify client not initialized. Authenticate Spotify first."
140
+ sp.pause_playback(device_id=device_id)
141
+ return "♫ Playback paused ♫"
142
+
143
+
144
+ def play_track():
145
+ if sp is None or device_id is None:
146
+ return f"Spotify client not initialized. Authenticate Spotify first."
147
+ sp.start_playback(device_id=device_id)
148
+ return "♫ Playback started ♫"
149
+
150
+
151
+ def skip_track():
152
+ if sp is None or device_id is None:
153
+ return f"Spotify client not initialized. Authenticate Spotify first."
154
+ sp.next_track(device_id=device_id)
155
+ return "♫ Skipped to your next track ♫"
156
+
157
+
158
+ ### ### ### More Elaborate ### ### ###
159
+
160
+
161
+ from requests.exceptions import Timeout
162
+ def play_album_by_name_and_artist(album_name, artist_name):
163
+ """
164
+ Plays an album given its name and artist
165
+ context_uri (for artist, album, or playlist) expects a string
166
+ """
167
+ if sp is None or device_id is None:
168
+ return f"Spotify client not initialized. Authenticate Spotify first."
169
+ results = sp.search(q=f'{album_name} {artist_name}', type='album')
170
+
171
+ album_id = results['albums']['items'][0]['id']
172
+ album_info = sp.album(album_id)
173
+ album_name = album_info['name']
174
+ artist_name = album_info['artists'][0]['name']
175
+ try:
176
+ sp.start_playback(device_id=device_id, context_uri=f'spotify:album:{album_id}')
177
+ return f"♫ Now playing {album_name} by {artist_name} ♫"
178
+ except spotipy.SpotifyException as e:
179
+ return f"An error occurred with Spotify: {e}. \n\n**Remember to wake up Spotify.**"
180
+ except Timeout:
181
+ return f"An unexpected error occurred: {e}."
182
+
183
+
184
+ def play_playlist_by_name(playlist_name):
185
+ if sp is None or device_id is None:
186
+ return f"Spotify client not initialized. Authenticate Spotify first."
187
+
188
+ playlists = sp.current_user_playlists()
189
+ playlist_dict = {playlist['name']: (playlist['id'], playlist['owner']['display_name']) for playlist in playlists['items']}
190
+ playlist_names = [key for key in playlist_dict.keys()]
191
+
192
+ # defined inside function to play new playlists created in session
193
+ playlist_name_embeddings = model.encode(playlist_names)
194
+ user_playlist_embedding = model.encode([playlist_name])
195
+
196
+ similarity_scores = cosine_similarity(user_playlist_embedding, playlist_name_embeddings)
197
+ most_similar_index = similarity_scores.argmax()
198
+ playlist_name = playlist_names[most_similar_index]
199
+ try:
200
+ playlist_id, creator_name = playlist_dict[playlist_name]
201
+ sp.start_playback(device_id=device_id, context_uri=f'spotify:playlist:{playlist_id}')
202
+ return f'♫ Now playing {playlist_name} by {creator_name} ♫'
203
+ except:
204
+ return "Unable to find playlist. Please try again."
205
+
206
+
207
+ # avoid creating new object everytime function is called
208
+ genius_token = "yjmR9yz3KJB7AsX23Od_CAWcr5l67LV59JS0MlACwJR9uw5qS7SKlVzEaVTnytWZ"
209
+ genius = lyricsgenius.Genius(genius_token)
210
+ def get_track_info():
211
+ """
212
+ Harvests information for explain_track() using the Genius API and webscraping.
213
+ """
214
+ if sp is None:
215
+ return f"Spotify client not initialized. Authenticate Spotify first."
216
+
217
+ current_track_item = sp.current_user_playing_track()['item']
218
+ track_name = current_track_item['name']
219
+ artist_name = current_track_item['artists'][0]['name']
220
+ album_name = current_track_item['album']['name']
221
+ release_date = current_track_item['album']['release_date']
222
+ basic_info = {
223
+ 'track_name': track_name,
224
+ 'artist_name': artist_name,
225
+ 'album_name': album_name,
226
+ 'release_date': release_date,
227
+ }
228
+ # features hinder Genius search
229
+ track_name = re.split(' \(with | \(feat\. ', track_name)[0]
230
+ result = genius.search_song(track_name, artist_name)
231
+ # if no Geniius page exists
232
+ if result is not None and hasattr(result, 'artist'):
233
+ genius_artist = result.artist.lower().replace(" ", "")
234
+ spotify_artist = artist_name.lower().replace(" ", "")
235
+ print(spotify_artist)
236
+ print(genius_artist)
237
+ if spotify_artist not in genius_artist:
238
+ return basic_info, None, None, None
239
+ else:
240
+ genius_artist = None
241
+ return basic_info, None, None, None
242
+
243
+ # if a Genius page exists
244
+ lyrics = result.lyrics
245
+ url = result.url
246
+ response = requests.get(url)
247
+
248
+ # parsing the webpage & locating About section
249
+ soup = BeautifulSoup(response.text, 'html.parser')
250
+ about_section = soup.select_one('div[class^="RichText__Container-oz284w-0"]')
251
+
252
+ # if no About section exists
253
+ if not about_section:
254
+ return basic_info, None, lyrics, url
255
+
256
+ # if an About section exists
257
+ else:
258
+ about_section = about_section.get_text(separator='\n')
259
+ return basic_info, about_section, lyrics, url
260
+
261
+
262
+ def explain_track():
263
+ from final_agent import llm
264
+ """
265
+ Presents track information in an organized, compelling manner.
266
+ """
267
+ basic_info, about_section, lyrics, url = get_track_info()
268
+ print(basic_info, about_section, lyrics, url)
269
+ if lyrics: # (essentially, if a Genius page exists; lyrics != None)
270
+ system_message_content = """
271
+ Your task is to create an engaging summary for a track using the available details
272
+ about the track and its lyrics. If there's insufficient or no additional information
273
+ besides the lyrics, craft the entire summary based solely on the lyrical content."
274
+ """
275
+ human_message_content = f"{about_section}\n\n{lyrics}"
276
+ messages = [
277
+ SystemMessage(content=system_message_content),
278
+ HumanMessage(content=human_message_content)
279
+ ]
280
+ ai_response = llm(messages).content
281
+ summary = f"""
282
+ **Name:** <span style="color: red; font-weight: bold; font-style: italic;">{basic_info["track_name"]}</span>
283
+ **Artist:** {basic_info["artist_name"]}
284
+ **Album:** {basic_info["album_name"]}
285
+ **Release:** {basic_info["release_date"]}
286
+
287
+ **About:**
288
+ {ai_response}
289
+
290
+ <a href='{url}'>Click here for more information on Genius!</a>
291
+ """
292
+ return summary
293
+ else: # (if no Genius page exists - Lyrics; None)
294
+ url = "https://genius.com/Genius-how-to-add-songs-to-genius-annotated"
295
+ summary = f"""
296
+ **Name:** <span style="color: red; font-weight: bold; font-style: italic;">{basic_info["track_name"]}</span>
297
+ **Artist:** {basic_info["artist_name"]}
298
+ **Album:** {basic_info["album_name"]}
299
+ **Release:** {basic_info["release_date"]}
300
+
301
+ **About:**
302
+ Unfortunately, this track has not been uploaded to Genius.com
303
+
304
+ <a href='{url}'>Be the first to change that!</a>
305
+ """
306
+ return summary
307
+
308
+
309
+ ### ### ### Genre + Mood ### ### ###
310
+
311
+
312
+ os.environ["TOKENIZERS_PARALLELISM"] = "false" # satisfies warning
313
+ model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
314
+ """
315
+ * MiniLM models are smaller and faster versions of BERT-like models with competitive performance.
316
+ * L6 indicates the number of layers in the model -
317
+ a reduced number of layers compared to larger models like BERT, which typically have 12 or 24 layers.
318
+ """
319
+
320
+
321
+ mood_list = ["happy", "sad", "energetic", "calm"]
322
+ mood_embeddings = model.encode(mood_list)
323
+ def get_user_mood(user_mood):
324
+ if user_mood.lower() in mood_list:
325
+ user_mood = user_mood.lower()
326
+ return user_mood
327
+ else:
328
+ user_mood_embedding = model.encode([user_mood.lower()])
329
+ similarity_scores = cosine_similarity(user_mood_embedding, mood_embeddings)
330
+ most_similar_index = similarity_scores.argmax()
331
+ user_mood = mood_list[most_similar_index]
332
+ return user_mood
333
+
334
+
335
+ if sp is None or device_id is None:
336
+ #print("Spotify client not initialized. Authenticate Spotify first.")
337
+ None
338
+ else:
339
+ genre_list = sp.recommendation_genre_seeds()["genres"] # genres accepted by recommendations()
340
+ genre_embeddings = model.encode(genre_list)
341
+
342
+
343
+ def get_genre_by_name(genre_name):
344
+ """
345
+ Matches genre_name input to closest existing genre in genre_list;
346
+ recommendations() only accepts from list
347
+ """
348
+ if genre_name.lower() in genre_list:
349
+ genre_name = genre_name.lower()
350
+ return genre_name
351
+ else:
352
+ genre_name_embedding = model.encode([genre_name.lower()])
353
+ similarity_scores = cosine_similarity(genre_name_embedding, genre_embeddings)
354
+ most_similar_index = similarity_scores.argmax()
355
+ genre_name = genre_list[most_similar_index]
356
+ return genre_name
357
+
358
+
359
+ MOOD_SETTINGS = {
360
+ "happy": {"max_instrumentalness": 0.001, "min_valence": 0.6},
361
+ "sad": {"max_danceability": 0.65, "max_valence": 0.4},
362
+ "energetic": {"min_tempo": 120, "min_danceability": 0.75},
363
+ "calm": {"max_energy": 0.65, "max_tempo": 130}
364
+ }
365
+
366
+
367
+ similarity_threshold = 75 # found after testing; out of 100
368
+ def is_genre_match(genre1, genre2, threshold=similarity_threshold):
369
+ """
370
+ if you have a short string and a longer one,
371
+ fuzz.partial_token_set_ratio() will score the similarity
372
+ of the shorter string to every possible sub-string
373
+ (of the same length as the shorter string) in the longer string.
374
+ The highest similarity score is then returned.
375
+ """
376
+ score = fuzz.token_set_ratio(genre1, genre2)
377
+ print(score) # debugging
378
+ return score >= threshold
379
+
380
+
381
+
382
+ def create_track_list_str(track_uris):
383
+ """
384
+ Helper function; creates list of track names and corresponding artists
385
+ """
386
+ if sp is None:
387
+ return f"Spotify client not initialized. Authenticate Spotify first."
388
+
389
+ track_details = sp.tracks(track_uris)
390
+ track_names_with_artists = [f"{track['name']} by {track['artists'][0]['name']}" for track in track_details['tracks']]
391
+ track_list_str = "<br>".join(track_names_with_artists)
392
+
393
+ return track_list_str
394
+
395
+
396
+ NUM_ARTISTS = 20 # max 50
397
+ TIME_RANGE = "medium_term"
398
+ NUM_TRACKS = 10
399
+ MAX_ARTISTS = 4
400
+ def play_genre_by_name_and_mood(genre_name, user_mood):
401
+ """
402
+ 1. Retrieves user's genre request and their current mood
403
+ 2. Matches genre and mood to existing options
404
+ 3. Gets 4 of user's top artists that align with genre
405
+ 4. Conducts personalized recommendations() search
406
+ """
407
+ if sp is None or device_id is None:
408
+ return f"Spotify client not initialized. Authenticate Spotify first."
409
+
410
+ genre_name = get_genre_by_name(genre_name)
411
+ user_mood = get_user_mood(user_mood).lower()
412
+ #mood_setting = mood_settings[user_mood]
413
+ print(genre_name)
414
+ print(user_mood)
415
+ #print(mood_setting)
416
+
417
+ # increased personalization
418
+ user_top_artists = sp.current_user_top_artists(limit=NUM_ARTISTS, time_range=TIME_RANGE)
419
+ matching_artists_ids = []
420
+
421
+ ### READABLE ###
422
+ for artist in user_top_artists['items']:
423
+ #print(artist['genres'])
424
+ for artist_genre in artist['genres']:
425
+ if is_genre_match(genre_name, artist_genre):
426
+ matching_artists_ids.append(artist['id'])
427
+ break # don't waste time cycling artist genres after match
428
+ if len(matching_artists_ids) == MAX_ARTISTS: # limit to pass to recommendations()
429
+ break
430
+
431
+ ### EFFICIENT ### [WORSE VERSION] ~ INCLUDES ARTIST MULTIPLE TIMES PER MATCH
432
+ # matching_artists_ids = list(islice((artist['id'] for artist in user_top_artists['items']
433
+ # for artist_genre in artist['genres']
434
+ # if is_genre_match(genre_name, artist_genre)), MAX_ARTISTS))
435
+
436
+ if not matching_artists_ids:
437
+ matching_artists_ids = None
438
+ else:
439
+ artist_names = [artist['name'] for artist in sp.artists(matching_artists_ids)['artists']]
440
+ print(artist_names)
441
+ print(matching_artists_ids)
442
+
443
+ recommendations = sp.recommendations( # can pass maximum {genre + artists} = 5 seeds
444
+ seed_artists=matching_artists_ids,
445
+ seed_genres=[genre_name],
446
+ seed_tracks=None,
447
+ limit=NUM_TRACKS, # number of tracks to return
448
+ country=None,
449
+ **MOOD_SETTINGS[user_mood])
450
+
451
+ track_uris = [track['uri'] for track in recommendations['tracks']]
452
+ track_list_str = create_track_list_str(track_uris)
453
+ sp.start_playback(device_id=device_id, uris=track_uris)
454
+
455
+ return f"""
456
+ **♫ Now Playing:** <span style="color: red; font-weight: bold; font-style: italic;">{genre_name}</span> ♫
457
+
458
+ **Selected Tracks:**
459
+ {track_list_str}
460
+ """
461
+
462
+
463
+ ### ### ### Artist + Mood ### ### ###
464
+
465
+
466
+ NUM_ALBUMS = 20 # max 20
467
+ MAX_TRACKS = 10
468
+ def play_artist_by_name_and_mood(artist_name, user_mood):
469
+ if sp is None or device_id is None:
470
+ return f"Spotify client not initialized. Authenticate Spotify first."
471
+
472
+ user_mood = get_user_mood(user_mood).lower()
473
+ print(user_mood)
474
+
475
+ # retrieving and shuffling all artist's tracks
476
+ first_name = artist_name.split(',')[0].strip()
477
+ results = sp.search(q=first_name, type='artist')
478
+ artist_id = results['artists']['items'][0]['id']
479
+ # most recent albums retrieved first
480
+ artist_albums = sp.artist_albums(artist_id, album_type='album', limit=NUM_ALBUMS)
481
+ artist_tracks = []
482
+ for album in artist_albums['items']:
483
+ album_tracks = sp.album_tracks(album['id'])['items']
484
+ artist_tracks.extend(album_tracks)
485
+ random.shuffle(artist_tracks)
486
+
487
+ # filtering until we find enough tracks that match user's mood
488
+ selected_tracks = []
489
+ for track in artist_tracks:
490
+ if len(selected_tracks) == MAX_TRACKS: # number of tracks to queue
491
+ break
492
+ features = sp.audio_features([track['id']])[0]
493
+ mood_criteria = MOOD_SETTINGS[user_mood]
494
+
495
+ match = True
496
+ for criteria, threshold in mood_criteria.items():
497
+ if "min_" in criteria and features[criteria[4:]] < threshold:
498
+ match = False
499
+ break
500
+ elif "max_" in criteria and features[criteria[4:]] > threshold:
501
+ match = False
502
+ break
503
+ if match:
504
+ print(f"{features}\n{mood_criteria}\n\n") # checking our work
505
+ selected_tracks.append(track)
506
+
507
+ track_names = [track['name'] for track in selected_tracks]
508
+ track_list_str = "<br>".join(track_names) # Using HTML line breaks for each track name
509
+ print(track_list_str)
510
+ track_uris = [track['uri'] for track in selected_tracks]
511
+ sp.start_playback(device_id=device_id, uris=track_uris)
512
+
513
+ return f"""
514
+ **♫ Now Playing:** <span style="color: red; font-weight: bold; font-style: italic;">{artist_name}</span> ♫
515
+
516
+ **Selected Tracks:**
517
+ {track_list_str}
518
+ """
519
+
520
+
521
+ ### ### ### Recommendations ### ### ###
522
+
523
+
524
+ NUM_TRACKS = 10
525
+ def recommend_tracks(genre_name=None, artist_name=None, track_name=None, user_mood=None):
526
+ """
527
+ 1. Retrieves user's preferences based on artist_name, track_name, genre_name, and user_mood.
528
+ 2. Uses these parameters to conduct personalized recommendations() search.
529
+ """
530
+ if sp is None or device_id is None:
531
+ return f"Spotify client not initialized. Authenticate Spotify first."
532
+
533
+ user_mood = get_user_mood(user_mood).lower() if user_mood else None
534
+ print(user_mood)
535
+
536
+ seed_genre, seed_artist, seed_track = None, None, None
537
+
538
+ if genre_name:
539
+ first_name = genre_name.split(',')[0].strip()
540
+ genre_name = get_genre_by_name(first_name)
541
+ seed_genre = [genre_name]
542
+ print(seed_genre)
543
+
544
+ if artist_name:
545
+ first_name = artist_name.split(',')[0].strip() # if user provides multiple artists, use the first
546
+ results = sp.search(q='artist:' + first_name, type='artist')
547
+ seed_artist = [results['artists']['items'][0]['id']]
548
+
549
+ if track_name:
550
+ first_name = track_name.split(',')[0].strip()
551
+ results = sp.search(q='track:' + first_name, type='track')
552
+ seed_track = [results['tracks']['items'][0]['id']]
553
+
554
+ if seed_genre is None and seed_artist is None and seed_track is None:
555
+ raise ValueError("At least one genre, artist, or track must be provided.")
556
+
557
+ recommendations = sp.recommendations( # requires at least one seed value
558
+ seed_artists=seed_artist,
559
+ seed_genres=seed_genre,
560
+ seed_tracks=seed_track,
561
+ limit=NUM_TRACKS,
562
+ country=None,
563
+ **MOOD_SETTINGS[user_mood] if user_mood else {})
564
+
565
+ track_uris = [track['uri'] for track in recommendations['tracks']]
566
+ return track_uris
567
+
568
+
569
+ def play_recommended_tracks(genre_name=None, artist_name=None, track_name=None, user_mood=None):
570
+ if sp is None or device_id is None:
571
+ return f"Spotify client not initialized. Authenticate Spotify first."
572
+
573
+ try:
574
+ track_uris = recommend_tracks(genre_name, artist_name, track_name, user_mood)
575
+ track_list_str = create_track_list_str(track_uris)
576
+ sp.start_playback(device_id=device_id, uris=track_uris)
577
+
578
+ return f"""
579
+ **♫ Now Playing Recommendations Based On:** <span style="color: red; font-weight: bold; font-style: italic;">{', '.join(filter(None, [genre_name, artist_name, track_name, "Your Mood"]))}</span> ♫
580
+
581
+ **Selected Tracks:**
582
+ {track_list_str}
583
+ """
584
+ except ValueError as e:
585
+ return str(e) # Return the error message, or handle it as needed.
586
+
587
+
588
+ def create_playlist_from_recommendations(genre_name=None, artist_name=None, track_name=None, user_mood=None):
589
+ if sp is None or device_id is None:
590
+ return f"Spotify client not initialized. Authenticate Spotify first."
591
+
592
+ user_id = sp.current_user()['id']
593
+ user_name = sp.current_user()["display_name"]
594
+
595
+ playlists = sp.current_user_playlists()
596
+ playlist_names = [playlist['name'] for playlist in playlists["items"]]
597
+ themes = ["Epic", "Hypnotic", "Dreamy", "Legendary", "Majestic",
598
+ "Enchanting", "Ethereal", "Super Lit", "Harmonious", "Heroic"]
599
+ chosen_theme = random.choice(themes)
600
+ playlist_name = f"{user_name}'s {chosen_theme} Playlist"
601
+
602
+ while playlist_name in playlist_names:
603
+ chosen_theme = random.choice(themes)
604
+ playlist_name = f"{user_name}'s {chosen_theme} Playlist"
605
+
606
+ playlist_description=f"Apollo AI's personalized playlist for {user_name}. Get yours here: (add link)."
607
+ new_playlist = sp.user_playlist_create(user=user_id, name=playlist_name, public=True, collaborative=False, description=playlist_description)
608
+
609
+ track_uris = recommend_tracks(genre_name, artist_name, track_name, user_mood)
610
+ track_list_str = create_track_list_str(track_uris)
611
+ sp.user_playlist_add_tracks(user=user_id, playlist_id=new_playlist['id'], tracks=track_uris, position=None)
612
+ playlist_url = f"https://open.spotify.com/playlist/{new_playlist['id']}"
613
+
614
+ return f"""
615
+ ♫ Created *{playlist_name}* Based On: <span style='color: red; font-weight: bold; font-style: italic;'>{', '.join(filter(None, [genre_name, artist_name, track_name, 'Your Mood']))}</span> ♫
616
+
617
+ **Selected Tracks:**
618
+ {track_list_str}
619
+
620
+ <a href='{playlist_url}'>Click here to listen to the playlist on Spotify!</a>
621
+ """
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+
630
+
631
+
632
+
633
+
634
+
635
+
636
+
637
+
638
+
639
+
640
+