Det9999 commited on
Commit
7be2401
Β·
verified Β·
1 Parent(s): a7c2c28

Upload streamlit_app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. streamlit_app.py +637 -0
streamlit_app.py ADDED
@@ -0,0 +1,637 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import spotipy
3
+ from spotipy.oauth2 import SpotifyOAuth
4
+ import requests
5
+ import json
6
+ import time
7
+ from datetime import datetime, timedelta
8
+ import openai
9
+ import anthropic
10
+ import os
11
+ from typing import Dict, List, Any, Optional
12
+ import base64
13
+
14
+ # Page configuration
15
+ st.set_page_config(
16
+ page_title="Spotify AI Assistant",
17
+ page_icon="🎡",
18
+ layout="wide",
19
+ initial_sidebar_state="expanded"
20
+ )
21
+
22
+ # Custom CSS
23
+ st.markdown("""
24
+ <style>
25
+ .main-header {
26
+ font-size: 2.5rem;
27
+ font-weight: bold;
28
+ background: linear-gradient(90deg, #1DB954, #191414);
29
+ -webkit-background-clip: text;
30
+ -webkit-text-fill-color: transparent;
31
+ margin-bottom: 1rem;
32
+ }
33
+ .spotify-card {
34
+ background: linear-gradient(135deg, #1DB954, #191414);
35
+ color: white;
36
+ padding: 1.5rem;
37
+ border-radius: 1rem;
38
+ margin: 1rem 0;
39
+ }
40
+ .chat-message {
41
+ padding: 1rem;
42
+ border-radius: 0.5rem;
43
+ margin: 0.5rem 0;
44
+ }
45
+ .user-message {
46
+ background-color: #1DB954;
47
+ color: white;
48
+ margin-left: 2rem;
49
+ }
50
+ .ai-message {
51
+ background-color: #f0f0f0;
52
+ color: black;
53
+ margin-right: 2rem;
54
+ }
55
+ .track-card {
56
+ background: #f8f9fa;
57
+ padding: 1rem;
58
+ border-radius: 0.5rem;
59
+ margin: 0.5rem 0;
60
+ border-left: 4px solid #1DB954;
61
+ }
62
+ .stButton>button {
63
+ background-color: #1DB954;
64
+ color: white;
65
+ font-weight: bold;
66
+ }
67
+ .stButton>button:hover {
68
+ background-color: #1ed760;
69
+ }
70
+ </style>
71
+ """, unsafe_allow_html=True)
72
+
73
+ # Initialize session state
74
+ def init_session_state():
75
+ if 'spotify_token' not in st.session_state:
76
+ st.session_state.spotify_token = None
77
+ if 'spotify_auth_manager' not in st.session_state:
78
+ st.session_state.spotify_auth_manager = None
79
+ if 'chat_history' not in st.session_state:
80
+ st.session_state.chat_history = []
81
+ if 'user_profile' not in st.session_state:
82
+ st.session_state.user_profile = None
83
+ if 'ai_provider' not in st.session_state:
84
+ st.session_state.ai_provider = "OpenAI"
85
+ if 'openai_api_key' not in st.session_state:
86
+ st.session_state.openai_api_key = ""
87
+ if 'anthropic_api_key' not in st.session_state:
88
+ st.session_state.anthropic_api_key = ""
89
+ if 'openrouter_api_key' not in st.session_state:
90
+ st.session_state.openrouter_api_key = ""
91
+ if 'current_model' not in st.session_state:
92
+ st.session_state.current_model = "gpt-3.5-turbo"
93
+ if 'spotify_client_id' not in st.session_state:
94
+ st.session_state.spotify_client_id = ""
95
+ if 'spotify_client_secret' not in st.session_state:
96
+ st.session_state.spotify_client_secret = ""
97
+ if 'spotify_redirect_uri' not in st.session_state:
98
+ st.session_state.spotify_redirect_uri = "http://localhost:8501"
99
+
100
+ init_session_state()
101
+
102
+ # Header
103
+ st.markdown('<h1 class="main-header">🎡 Spotify AI Assistant</h1>', unsafe_allow_html=True)
104
+ st.markdown("Built with anycoder β€’ [GitHub](https://github.com/anycoder)", unsafe_allow_html=True)
105
+
106
+ # Sidebar
107
+ with st.sidebar:
108
+ st.image("https://storage.googleapis.com/pr-newsroom-wp/1/2023/05/Spotify_Primary_Logo_RGB_Green.png", width=200)
109
+ st.markdown("### πŸ” Spotify Configuration")
110
+
111
+ # Spotify credentials
112
+ st.session_state.spotify_client_id = st.text_input(
113
+ "Spotify Client ID",
114
+ value=st.session_state.spotify_client_id,
115
+ type="password",
116
+ help="Get from Spotify Developer Dashboard"
117
+ )
118
+ st.session_state.spotify_client_secret = st.text_input(
119
+ "Spotify Client Secret",
120
+ value=st.session_state.spotify_client_secret,
121
+ type="password",
122
+ help="Get from Spotify Developer Dashboard"
123
+ )
124
+ st.session_state.spotify_redirect_uri = st.text_input(
125
+ "Redirect URI",
126
+ value=st.session_state.spotify_redirect_uri,
127
+ help="Must match Spotify Developer Dashboard"
128
+ )
129
+
130
+ st.markdown("---")
131
+ st.markdown("### πŸ€– AI Configuration")
132
+
133
+ # AI Provider selection
134
+ st.session_state.ai_provider = st.selectbox(
135
+ "AI Provider",
136
+ ["OpenAI", "Anthropic", "OpenRouter"],
137
+ help="Choose your AI model provider"
138
+ )
139
+
140
+ # API keys based on provider
141
+ if st.session_state.ai_provider == "OpenAI":
142
+ st.session_state.openai_api_key = st.text_input(
143
+ "OpenAI API Key",
144
+ value=st.session_state.openai_api_key,
145
+ type="password",
146
+ help="Get from platform.openai.com"
147
+ )
148
+ st.session_state.current_model = st.selectbox(
149
+ "Model",
150
+ ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"]
151
+ )
152
+ elif st.session_state.ai_provider == "Anthropic":
153
+ st.session_state.anthropic_api_key = st.text_input(
154
+ "Anthropic API Key",
155
+ value=st.session_state.anthropic_api_key,
156
+ type="password",
157
+ help="Get from console.anthropic.com"
158
+ )
159
+ st.session_state.current_model = st.selectbox(
160
+ "Model",
161
+ ["claude-3-haiku-20240307", "claude-3-sonnet-20240229", "claude-3-opus-20240229"]
162
+ )
163
+ elif st.session_state.ai_provider == "OpenRouter":
164
+ st.session_state.openrouter_api_key = st.text_input(
165
+ "OpenRouter API Key",
166
+ value=st.session_state.openrouter_api_key,
167
+ type="password",
168
+ help="Get from openrouter.ai"
169
+ )
170
+ st.session_state.current_model = st.text_input(
171
+ "Model (OpenRouter Format)",
172
+ value="openai/gpt-3.5-turbo"
173
+ )
174
+
175
+ st.markdown("---")
176
+ st.markdown("### πŸ“Š Options")
177
+ limit = st.slider("Data Limit", 5, 50, 20, help="Number of items to fetch from Spotify")
178
+
179
+ if st.button("Clear Chat History"):
180
+ st.session_state.chat_history = []
181
+ st.success("Chat history cleared!")
182
+
183
+ # Spotify Authentication
184
+ def get_spotify_auth_manager():
185
+ if not st.session_state.spotify_client_id or not st.session_state.spotify_client_secret:
186
+ return None
187
+
188
+ return SpotifyOAuth(
189
+ client_id=st.session_state.spotify_client_id,
190
+ client_secret=st.session_state.spotify_client_secret,
191
+ redirect_uri=st.session_state.spotify_redirect_uri,
192
+ scope="user-read-private user-read-email user-top-read user-read-recently-played playlist-read-private playlist-modify-private playlist-modify-public",
193
+ cache_path=".spotify_cache"
194
+ )
195
+
196
+ def authenticate_spotify():
197
+ auth_manager = get_spotify_auth_manager()
198
+ if not auth_manager:
199
+ st.error("Please enter Spotify credentials in the sidebar")
200
+ return False
201
+
202
+ st.session_state.spotify_auth_manager = auth_manager
203
+
204
+ # Generate auth URL
205
+ auth_url = auth_manager.get_authorize_url()
206
+
207
+ st.markdown("### πŸ” Spotify Authentication")
208
+ st.markdown(f"1. Click [here to authorize]({auth_url})")
209
+ st.markdown("2. Copy the full redirect URL after authorization")
210
+
211
+ redirect_url = st.text_input("Paste redirect URL here:", placeholder="http://localhost:8501/?code=...")
212
+
213
+ if st.button("Complete Authentication"):
214
+ if redirect_url:
215
+ try:
216
+ # Extract code from URL
217
+ code = auth_manager.parse_response_code(redirect_url)
218
+ token_info = auth_manager.get_access_token(code)
219
+ st.session_state.spotify_token = token_info
220
+ st.success("βœ… Successfully authenticated with Spotify!")
221
+ return True
222
+ except Exception as e:
223
+ st.error(f"Authentication failed: {str(e)}")
224
+ return False
225
+ else:
226
+ st.warning("Please paste the redirect URL")
227
+ return False
228
+
229
+ return False
230
+
231
+ def get_spotify_client():
232
+ if not st.session_state.spotify_token:
233
+ return None
234
+
235
+ auth_manager = get_spotify_auth_manager()
236
+ if not auth_manager:
237
+ return None
238
+
239
+ return spotipy.Spotify(auth=st.session_state.spotify_token['access_token'])
240
+
241
+ # Spotify Data Functions
242
+ @st.cache_data(ttl=3600)
243
+ def fetch_user_profile():
244
+ sp = get_spotify_client()
245
+ if not sp:
246
+ return None
247
+ try:
248
+ return sp.current_user()
249
+ except:
250
+ return None
251
+
252
+ @st.cache_data(ttl=1800)
253
+ def fetch_top_tracks(limit=20, time_range='medium_term'):
254
+ sp = get_spotify_client()
255
+ if not sp:
256
+ return []
257
+ try:
258
+ results = sp.current_user_top_tracks(limit=limit, time_range=time_range)
259
+ return results['items']
260
+ except:
261
+ return []
262
+
263
+ @st.cache_data(ttl=1800)
264
+ def fetch_top_artists(limit=20, time_range='medium_term'):
265
+ sp = get_spotify_client()
266
+ if not sp:
267
+ return []
268
+ try:
269
+ results = sp.current_user_top_artists(limit=limit, time_range=time_range)
270
+ return results['items']
271
+ except:
272
+ return []
273
+
274
+ @st.cache_data(ttl=900)
275
+ def fetch_recently_played(limit=20):
276
+ sp = get_spotify_client()
277
+ if not sp:
278
+ return []
279
+ try:
280
+ results = sp.current_user_recently_played(limit=limit)
281
+ return results['items']
282
+ except:
283
+ return []
284
+
285
+ @st.cache_data(ttl=3600)
286
+ def fetch_user_playlists(limit=20):
287
+ sp = get_spotify_client()
288
+ if not sp:
289
+ return []
290
+ try:
291
+ results = sp.current_user_playlists(limit=limit)
292
+ return results['items']
293
+ except:
294
+ return []
295
+
296
+ @st.cache_data(ttl=3600)
297
+ def search_spotify(query, types=['track', 'artist', 'album'], limit=5):
298
+ sp = get_spotify_client()
299
+ if not sp:
300
+ return {}
301
+ try:
302
+ results = sp.search(q=query, type=','.join(types), limit=limit)
303
+ return results
304
+ except:
305
+ return {}
306
+
307
+ def create_playlist(name, description="", public=False):
308
+ sp = get_spotify_client()
309
+ if not sp:
310
+ return None
311
+ try:
312
+ user_id = sp.current_user()['id']
313
+ playlist = sp.user_playlist_create(user_id, name, public=public, description=description)
314
+ return playlist
315
+ except Exception as e:
316
+ st.error(f"Failed to create playlist: {str(e)}")
317
+ return None
318
+
319
+ def add_tracks_to_playlist(playlist_id, track_uris):
320
+ sp = get_spotify_client()
321
+ if not sp:
322
+ return False
323
+ try:
324
+ sp.playlist_add_items(playlist_id, track_uris)
325
+ return True
326
+ except:
327
+ return False
328
+
329
+ # AI Chat Functions
330
+ def get_ai_client():
331
+ provider = st.session_state.ai_provider
332
+
333
+ if provider == "OpenAI" and st.session_state.openai_api_key:
334
+ openai.api_key = st.session_state.openai_api_key
335
+ return "openai"
336
+ elif provider == "Anthropic" and st.session_state.anthropic_api_key:
337
+ return "anthropic"
338
+ elif provider == "OpenRouter" and st.session_state.openrouter_api_key:
339
+ return "openrouter"
340
+
341
+ return None
342
+
343
+ def format_spotify_data_for_ai():
344
+ """Format user Spotify data for AI context"""
345
+ profile = fetch_user_profile()
346
+ top_tracks = fetch_top_tracks(limit=10)
347
+ top_artists = fetch_top_artists(limit=10)
348
+ recent = fetch_recently_played(limit=5)
349
+
350
+ context = "Spotify User Data:\n\n"
351
+
352
+ if profile:
353
+ context += f"User: {profile.get('display_name', 'Unknown')} "
354
+ context += f"({profile.get('email', 'No email')})\n"
355
+ context += f"Country: {profile.get('country', 'Unknown')}\n"
356
+ context += f"Product: {profile.get('product', 'Unknown')}\n\n"
357
+
358
+ if top_tracks:
359
+ context += "Top Tracks:\n"
360
+ for i, track in enumerate(top_tracks[:5], 1):
361
+ artists = ", ".join([a['name'] for a in track['artists']])
362
+ context += f"{i}. {track['name']} by {artists}\n"
363
+ context += "\n"
364
+
365
+ if top_artists:
366
+ context += "Top Artists:\n"
367
+ for i, artist in enumerate(top_artists[:5], 1):
368
+ context += f"{i}. {artist['name']}\n"
369
+ context += "\n"
370
+
371
+ if recent:
372
+ context += "Recently Played:\n"
373
+ for i, item in enumerate(recent[:3], 1):
374
+ track = item['track']
375
+ artists = ", ".join([a['name'] for a in track['artists']])
376
+ context += f"{i}. {track['name']} by {artists}\n"
377
+
378
+ return context
379
+
380
+ def generate_ai_response(prompt, context=""):
381
+ client_type = get_ai_client()
382
+
383
+ if not client_type:
384
+ return "Please configure your AI API keys in the sidebar."
385
+
386
+ full_prompt = f"""You are a knowledgeable Spotify AI assistant with access to user data.
387
+ Use the following Spotify data to provide personalized, accurate responses:
388
+
389
+ {context}
390
+
391
+ User Question: {prompt}
392
+
393
+ Provide helpful, specific recommendations and insights based on their actual listening data.
394
+ If they ask about their music taste, reference their top artists and tracks.
395
+ If they want recommendations, consider their preferences.
396
+ Be conversational and friendly."""
397
+
398
+ try:
399
+ if client_type == "openai":
400
+ response = openai.ChatCompletion.create(
401
+ model=st.session_state.current_model,
402
+ messages=[
403
+ {"role": "system", "content": "You are a helpful Spotify AI assistant."},
404
+ {"role": "user", "content": full_prompt}
405
+ ],
406
+ max_tokens=500,
407
+ temperature=0.7
408
+ )
409
+ return response.choices[0].message.content
410
+
411
+ elif client_type == "anthropic":
412
+ client = anthropic.Anthropic(api_key=st.session_state.anthropic_api_key)
413
+ response = client.messages.create(
414
+ model=st.session_state.current_model,
415
+ max_tokens=500,
416
+ temperature=0.7,
417
+ messages=[
418
+ {"role": "user", "content": full_prompt}
419
+ ]
420
+ )
421
+ return response.content[0].text
422
+
423
+ elif client_type == "openrouter":
424
+ headers = {
425
+ "Authorization": f"Bearer {st.session_state.openrouter_api_key}",
426
+ "Content-Type": "application/json"
427
+ }
428
+ data = {
429
+ "model": st.session_state.current_model,
430
+ "messages": [
431
+ {"role": "user", "content": full_prompt}
432
+ ],
433
+ "max_tokens": 500,
434
+ "temperature": 0.7
435
+ }
436
+ response = requests.post(
437
+ "https://openrouter.ai/api/v1/chat/completions",
438
+ headers=headers,
439
+ json=data
440
+ )
441
+ return response.json()['choices'][0]['message']['content']
442
+
443
+ except Exception as e:
444
+ return f"Error generating response: {str(e)}"
445
+
446
+ def generate_recommendations_from_tracks(seed_tracks, limit=10):
447
+ sp = get_spotify_client()
448
+ if not sp:
449
+ return []
450
+
451
+ try:
452
+ recommendations = sp.recommendations(seed_tracks=seed_tracks, limit=limit)
453
+ return recommendations['tracks']
454
+ except:
455
+ return []
456
+
457
+ # Main App Logic
458
+ def main():
459
+ # Check if authenticated
460
+ if not st.session_state.spotify_token:
461
+ with st.container():
462
+ st.markdown('<div class="spotify-card">', unsafe_allow_html=True)
463
+ st.markdown("### Welcome to Spotify AI Assistant!")
464
+ st.markdown("""
465
+ This app combines Spotify data with AI to provide:
466
+ - Personalized music recommendations
467
+ - Insights into your listening habits
468
+ - Natural language queries about your music
469
+ - Playlist creation assistance
470
+ """)
471
+ st.markdown('</div>', unsafe_allow_html=True)
472
+
473
+ if authenticate_spotify():
474
+ st.rerun()
475
+ return
476
+
477
+ # Main interface after authentication
478
+ sp = get_spotify_client()
479
+ if not sp:
480
+ st.error("Failed to initialize Spotify client")
481
+ return
482
+
483
+ # Fetch user data
484
+ profile = fetch_user_profile()
485
+ if profile:
486
+ st.session_state.user_profile = profile
487
+
488
+ # Create tabs
489
+ tab1, tab2, tab3, tab4 = st.tabs(["πŸ’¬ Chat", "πŸ“Š Insights", "🎡 Recommendations", "🎧 Your Data"])
490
+
491
+ # Chat Tab
492
+ with tab1:
493
+ st.markdown("### πŸ’¬ Chat with your Spotify AI Assistant")
494
+
495
+ # Display chat history
496
+ chat_container = st.container()
497
+ with chat_container:
498
+ for message in st.session_state.chat_history:
499
+ if message["role"] == "user":
500
+ st.markdown(f'<div class="chat-message user-message"><strong>You:</strong> {message["content"]}</div>', unsafe_allow_html=True)
501
+ else:
502
+ st.markdown(f'<div class="chat-message ai-message"><strong>AI:</strong> {message["content"]}</div>', unsafe_allow_html=True)
503
+
504
+ # Chat input
505
+ user_input = st.chat_input("Ask about your music, get recommendations, or create playlists...")
506
+
507
+ if user_input:
508
+ # Add user message to history
509
+ st.session_state.chat_history.append({"role": "user", "content": user_input})
510
+
511
+ # Get context and generate response
512
+ with st.spinner("Thinking..."):
513
+ context = format_spotify_data_for_ai()
514
+ response = generate_ai_response(user_input, context)
515
+
516
+ # Add AI response to history
517
+ st.session_state.chat_history.append({"role": "assistant", "content": response})
518
+
519
+ # Special handling for playlist creation requests
520
+ if "create playlist" in user_input.lower() and "created" in response.lower():
521
+ st.info("πŸ’‘ Tip: You can create playlists by asking 'Create a playlist based on my top artists'")
522
+
523
+ st.rerun()
524
+
525
+ # Insights Tab
526
+ with tab2:
527
+ st.markdown("### πŸ“Š Your Music Insights")
528
+
529
+ if profile:
530
+ col1, col2, col3 = st.columns(3)
531
+ with col1:
532
+ st.metric("Username", profile.get('display_name', 'N/A'))
533
+ with col2:
534
+ st.metric("Country", profile.get('country', 'N/A'))
535
+ with col3:
536
+ st.metric("Account Type", profile.get('product', 'N/A').title())
537
+
538
+ col1, col2 = st.columns(2)
539
+
540
+ with col1:
541
+ st.markdown("#### 🎀 Top Artists")
542
+ top_artists = fetch_top_artists(limit=10)
543
+ for i, artist in enumerate(top_artists, 1):
544
+ st.markdown(f"""
545
+ <div class="track-card">
546
+ <strong>{i}. {artist['name']}</strong><br>
547
+ <small>Genres: {', '.join(artist.get('genres', [])[:3])}</small>
548
+ </div>
549
+ """, unsafe_allow_html=True)
550
+
551
+ with col2:
552
+ st.markdown("#### 🎡 Top Tracks")
553
+ top_tracks = fetch_top_tracks(limit=10)
554
+ for i, track in enumerate(top_tracks, 1):
555
+ artists = ", ".join([a['name'] for a in track['artists']])
556
+ st.markdown(f"""
557
+ <div class="track-card">
558
+ <strong>{i}. {track['name']}</strong><br>
559
+ <small>by {artists}</small>
560
+ </div>
561
+ """, unsafe_allow_html=True)
562
+
563
+ # Recommendations Tab
564
+ with tab3:
565
+ st.markdown("### 🎡 AI-Powered Recommendations")
566
+
567
+ # Get seed tracks from top tracks
568
+ top_tracks = fetch_top_tracks(limit=5)
569
+ if top_tracks:
570
+ seed_tracks = [track['id'] for track in top_tracks[:3]]
571
+
572
+ st.markdown("#### Based on your top tracks:")
573
+ for track in top_tracks:
574
+ artists = ", ".join([a['name'] for a in track['artists']])
575
+ st.markdown(f"- {track['name']} by {artists}")
576
+
577
+ if st.button("Generate Recommendations"):
578
+ with st.spinner("Generating personalized recommendations..."):
579
+ recommendations = generate_recommendations_from_tracks(seed_tracks, limit=10)
580
+
581
+ if recommendations:
582
+ st.success("Here are your personalized recommendations:")
583
+ for track in recommendations:
584
+ artists = ", ".join([a['name'] for a in track['artists']])
585
+ album = track['album']['name']
586
+ st.markdown(f"""
587
+ <div class="track-card">
588
+ <strong>{track['name']}</strong> by {artists}<br>
589
+ <small>Album: {album}</small>
590
+ </div>
591
+ """, unsafe_allow_html=True)
592
+ else:
593
+ st.warning("Could not generate recommendations. Try again later.")
594
+ else:
595
+ st.info("Listen to more music to get recommendations!")
596
+
597
+ # Data Tab
598
+ with tab4:
599
+ st.markdown("### 🎧 Your Spotify Data")
600
+
601
+ col1, col2 = st.columns(2)
602
+
603
+ with col1:
604
+ st.markdown("#### Recently Played")
605
+ recent = fetch_recently_played(limit=10)
606
+ for item in recent:
607
+ track = item['track']
608
+ played_at = datetime.strptime(item['played_at'][:19], "%Y-%m-%dT%H:%M:%S")
609
+ artists = ", ".join([a['name'] for a in track['artists']])
610
+ st.markdown(f"""
611
+ <div class="track-card">
612
+ <strong>{track['name']}</strong> by {artists}<br>
613
+ <small>Played at: {played_at.strftime('%Y-%m-%d %H:%M')}</small>
614
+ </div>
615
+ """, unsafe_allow_html=True)
616
+
617
+ with col2:
618
+ st.markdown("#### Your Playlists")
619
+ playlists = fetch_user_playlists(limit=10)
620
+ for playlist in playlists:
621
+ st.markdown(f"""
622
+ <div class="track-card">
623
+ <strong>{playlist['name']}</strong><br>
624
+ <small>{playlist['tracks']['total']} tracks</small>
625
+ </div>
626
+ """, unsafe_allow_html=True)
627
+
628
+ # Footer
629
+ st.markdown("---")
630
+ st.markdown("""
631
+ <div style="text-align: center; color: #666;">
632
+ <p>Built with ❀️ using Streamlit | Data powered by Spotify API | Intelligence powered by AI</p>
633
+ </div>
634
+ """, unsafe_allow_html=True)
635
+
636
+ if __name__ == "__main__":
637
+ main()