-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
321 lines (268 loc) · 12.9 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import streamlit as st
import json
import weave
from spotify_utils import create_spotify_client, create_medley_queue
import time
from dotenv import load_dotenv
import spotipy
from litellm import completion
# Load environment variables at startup
load_dotenv()
st.set_page_config(page_title="Mixtape Medley Maker", page_icon="🎵")
def initialize_weave():
if "weave_initialized" not in st.session_state:
weave.init("llm-mixtape")
st.session_state.weave_initialized = True
@weave.op()
def generate_mixtape_json(prompt):
system_prompt = """You are a music expert that creates mixtapes. Generate a JSON object for a mixtape based on the user's description.
The JSON should have this structure:
```
{
"mixtape_title": "string",
"total_duration": "string (e.g., '5 minutes')",
"songs": [
{
"song": "string",
"artist": "string",
"start_timestamp": "string (MM:SS format, e.g., '0:30')",
"stop_timestamp": "string (MM:SS format, e.g., '1:00')"
}
]
}
```
For each song, choose the most iconic or memorable part of the song (like the chorus, a famous guitar solo, or a memorable hook).
The timestamps should reflect where these iconic moments occur in the original song.
For example, if a song's famous chorus starts at 1:45 and ends at 2:15, use those timestamps.
Keep segments between 20-60 seconds for each song, but make sure they capture the most recognizable parts."""
try:
response = completion(
model="gpt-4o",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Create a mixtape based on this description: {prompt}"}
],
response_format={"type": "json_object"}
)
return response.choices[0].message.content
except Exception as e:
st.error(f"Error generating mixtape: {str(e)}")
return None
def load_mixtape_data(json_text):
try:
return json.loads(json_text)
except json.JSONDecodeError:
st.error("Invalid JSON format")
return None
def main():
# Initialize weave once when the app starts
initialize_weave()
st.title("🎵 Mixtape Medley Maker")
# Debug section at the top
debug_container = st.empty()
# Initialize Spotify client
try:
# Store Spotify client in session state so it persists
if 'spotify_client' not in st.session_state:
st.session_state.spotify_client = create_spotify_client()
sp = st.session_state.spotify_client
debug_container.info("✅ Successfully connected to Spotify API")
except Exception as e:
st.error(f"Failed to connect to Spotify: {str(e)}")
st.info("Please make sure you have set up your Spotify credentials in .env file")
return
# Store mixtape data and medley queue in session state
if 'current_mixtape_data' not in st.session_state:
st.session_state.current_mixtape_data = None
if 'current_medley_queue' not in st.session_state:
st.session_state.current_medley_queue = None
# Add tabs for different input methods
tab1, tab2 = st.tabs(["Generate Mixtape", "Manual JSON Input"])
with tab1:
st.write("Describe the mixtape you want to create:")
mixtape_description = st.text_area(
"Your description",
placeholder="Example: Create a 5-minute mixtape of classic rock hits from the 70s, focusing on guitar solos",
height=150
)
if st.button("Generate Mixtape"):
if mixtape_description:
with st.spinner("Generating mixtape..."):
generated_json = generate_mixtape_json(mixtape_description)
if generated_json:
st.session_state.json_input = generated_json
st.code(generated_json, language="json")
st.success("Mixtape generated! You can now play it below.")
else:
st.warning("Please enter a description for your mixtape")
with tab2:
# Text area for JSON input
json_input = st.text_area(
"Enter your mixtape JSON:",
value=st.session_state.get('json_input', ''),
height=300
)
# Add this where you want to display the devices
st.subheader("Spotify Devices")
# Get available devices
try:
devices = sp.devices()
except spotipy.exceptions.SpotifyException as e:
st.error(f"Error getting devices: {str(e)}")
return
if devices['devices']:
# Create a table of devices
device_data = []
for device in devices['devices']:
device_data.append({
"Name": device['name'],
"Type": device['type'],
"Active": "✅" if device['is_active'] else "❌",
"Volume": f"{device['volume_percent']}%" if device['volume_percent'] else "N/A"
})
# Display devices in a nice table
st.table(device_data)
# Device selector
device_names = [device['name'] for device in devices['devices']]
selected_device = st.selectbox("Select Playback Device", device_names)
# Store the selected device ID in session state
selected_device_id = next(
device['id'] for device in devices['devices']
if device['name'] == selected_device
)
st.session_state.selected_device_id = selected_device_id
def activate_device(device_id):
"""Helper function to activate a device and ensure it's ready"""
try:
# First check if there's any active playback
current = sp.current_playback()
# If nothing is playing, we need to start something to activate the device
if not current or current.get('device', {}).get('id') != device_id:
# Transfer to the selected device
sp.transfer_playback(device_id=device_id, force_play=False)
time.sleep(1)
# Start playing a track to activate the device
try:
sp.start_playback(device_id=device_id)
except:
# If no previous track, play a short track
sp.start_playback(
device_id=device_id,
uris=['spotify:track:7oK9VyNzrYvRFo7nQEYkWN']
)
time.sleep(1) # Wait for playback to start
sp.pause_playback(device_id=device_id) # Pause it immediately
# Verify the device is now active
current = sp.current_playback()
if current and current.get('device', {}).get('id') == device_id:
return True
return False
except spotipy.exceptions.SpotifyException as e:
st.error(f"Spotify Error: {str(e)}")
return False
# Add a button to test/activate the selected device
col1, col2 = st.columns([1, 2])
with col1:
if st.button("Test Selected Device"):
try:
if activate_device(selected_device_id):
st.session_state.device_activated = True
st.success(f"Successfully activated device: {selected_device}")
st.info("✨ Device is ready for playback! You can now create and play your medley.")
else:
st.error("Failed to activate device")
st.info("Please make sure Spotify is open and active on your device")
except Exception as e:
st.error(f"Error: {str(e)}")
st.info("Please make sure Spotify is open and active on your device")
with col2:
st.info("💡 Before playing, click 'Test Selected Device' to ensure your device is ready")
else:
st.warning("No Spotify devices found. Please open Spotify app or web player.")
if st.button("Create and Play Medley"):
if 'selected_device_id' not in st.session_state:
st.error("Please select a playback device first")
return
if 'device_activated' not in st.session_state or not st.session_state.device_activated:
st.error("Please test and activate your selected device first")
return
debug_container.info("🎵 Starting medley creation process...")
# Use either the generated JSON or manual input
current_json = st.session_state.get('json_input', '') if tab1 else json_input
if not current_json:
st.warning("Please enter mixtape JSON data or generate a mixtape first")
return
debug_container.info("📝 Parsing JSON data...")
mixtape_data = load_mixtape_data(current_json)
if not mixtape_data:
debug_container.error("❌ Failed to parse JSON data")
return
# Create medley queue
debug_container.info("🔍 Searching for tracks on Spotify...")
medley_queue = create_medley_queue(sp, mixtape_data)
if not medley_queue:
debug_container.error("❌ No tracks found on Spotify")
return
# Store current mixtape data and medley queue
st.session_state.current_mixtape_data = mixtape_data
st.session_state.current_medley_queue = medley_queue
# Display found tracks
debug_container.info(f"✅ Found {len(medley_queue)} tracks on Spotify")
st.write("Found tracks:")
for track in medley_queue:
st.write(f"- {track['song']} by {track['artist']}")
# Display mixtape information
st.subheader(f"Now Playing: {mixtape_data['mixtape_title']}")
st.write(f"Total Duration: {mixtape_data['total_duration']}")
# Create progress container
progress_container = st.empty()
# Play each track in the medley
debug_container.info("▶️ Starting playback...")
for i, track in enumerate(medley_queue, 1):
try:
# Play the track
try:
# First ensure the device is active
if not activate_device(st.session_state.selected_device_id):
st.error("Failed to activate device. Please try testing the device again.")
break
# Start playback on the active device
sp.start_playback(
device_id=st.session_state.selected_device_id,
uris=[track['uri']],
position_ms=track['start_ms']
)
# Give a moment for playback to start
time.sleep(0.5)
# Verify playback started
current = sp.current_playback()
if not current or not current.get('is_playing'):
st.error("Failed to start playback. Please make sure Spotify is open and active.")
break
except spotipy.exceptions.SpotifyException as e:
if "Player command failed" in str(e):
st.error("Failed to play track. Please make sure Spotify is open and playing on your device.")
break
else:
st.error(f"Spotify Error: {str(e)}")
break
# Display current track
progress_container.info(
f"Playing ({i}/{len(medley_queue)}): {track['song']} by {track['artist']}\n"
f"Duration: {(track['stop_ms'] - track['start_ms'])/1000:.1f} seconds"
)
# Wait for the duration of the track segment
duration = track['stop_ms'] - track['start_ms']
time.sleep(duration / 1000) # Convert to seconds
# If this is the last track, stop playback
if i == len(medley_queue):
sp.pause_playback()
except spotipy.exceptions.SpotifyException as e:
debug_container.error(f"❌ Spotify Error playing {track['song']}: {str(e)}")
break
except Exception as e:
debug_container.error(f"❌ Error playing {track['song']}: {str(e)}")
break
progress_container.success("✨ Medley playback completed!")
if __name__ == "__main__":
main()