haraberget commited on
Commit
6c0850c
Β·
verified Β·
1 Parent(s): 77c8e1c

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +284 -140
app.py CHANGED
@@ -13,31 +13,62 @@ SUNO_KEY = os.environ.get("SunoKey", "")
13
  if not SUNO_KEY:
14
  print("⚠️ Warning: SunoKey environment variable not set!")
15
 
 
 
 
16
  # Task storage with auto-polling
17
  tasks_db = {}
18
  polling_threads = {}
19
 
20
- def poll_task_status(task_id: str, max_attempts=60, interval=2):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  """Background thread to automatically poll task status"""
22
  if task_id not in tasks_db:
 
23
  return
24
 
25
  task = tasks_db[task_id]
26
  api_task_id = task["api_task_id"]
27
 
 
 
28
  for attempt in range(max_attempts):
29
  try:
 
 
30
  # Poll the API
31
  url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={api_task_id}"
32
  headers = {"Authorization": f"Bearer {SUNO_KEY}"}
33
 
34
- response = requests.get(url, headers=headers, timeout=30)
35
  data = response.json()
36
 
37
  if response.status_code == 200 and data.get("code") == 200:
38
  task_data = data["data"]
39
  status = task_data.get("status", "unknown")
40
 
 
 
41
  # Update task status
42
  tasks_db[task_id]["status"] = status
43
  tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
@@ -47,25 +78,36 @@ def poll_task_status(task_id: str, max_attempts=60, interval=2):
47
  lyrics_data = task_data["data"]
48
  tasks_db[task_id]["result"] = lyrics_data
49
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
50
- print(f"βœ… Task {task_id} completed via auto-polling")
51
  break
52
 
53
  elif status == "failed":
54
  error_msg = task_data.get("error", "Unknown error")
55
  tasks_db[task_id]["error"] = error_msg
56
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
57
- print(f"❌ Task {task_id} failed: {error_msg}")
58
  break
 
 
 
 
 
 
 
 
 
59
 
60
  # Update attempts
61
  tasks_db[task_id]["poll_attempts"] = attempt + 1
62
 
63
  except Exception as e:
64
- print(f"⚠️ Polling error for task {task_id}: {str(e)}")
65
 
66
  # Wait before next poll
67
  time.sleep(interval)
68
 
 
 
69
  # Clean up polling thread
70
  if task_id in polling_threads:
71
  del polling_threads[task_id]
@@ -81,6 +123,8 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
81
  # Generate task ID
82
  task_id = str(uuid.uuid4())[:8]
83
 
 
 
84
  # Prepare API request
85
  url = "https://api.sunoapi.org/api/v1/lyrics"
86
  headers = {
@@ -88,7 +132,7 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
88
  "Content-Type": "application/json"
89
  }
90
 
91
- # Suno requires a callback URL, but we'll use a dummy one
92
  dummy_callback = "https://dummy.callback.url/not-used"
93
 
94
  payload = {
@@ -97,13 +141,19 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
97
  }
98
 
99
  try:
 
 
100
  # Submit task
101
- response = requests.post(url, headers=headers, json=payload, timeout=30)
102
  data = response.json()
103
 
 
 
104
  if response.status_code == 200 and data.get("code") == 200:
105
  api_task_id = data["data"]["taskId"]
106
 
 
 
107
  # Store task information
108
  tasks_db[task_id] = {
109
  "id": task_id,
@@ -116,7 +166,8 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
116
  "last_checked": None,
117
  "poll_attempts": 0,
118
  "completed_at": None,
119
- "auto_poll": auto_poll
 
120
  }
121
 
122
  # Start auto-polling if enabled
@@ -128,7 +179,7 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
128
  )
129
  polling_threads[task_id] = poll_thread
130
  poll_thread.start()
131
- print(f"πŸš€ Started auto-polling for task {task_id}")
132
 
133
  return f"""βœ… **Task Submitted Successfully!**
134
 
@@ -137,32 +188,42 @@ def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
137
 
138
  πŸ“ **Prompt:** {prompt[:100]}{'...' if len(prompt) > 100 else ''}
139
 
140
- {'πŸ”„ **Auto-polling enabled** - Results will appear automatically!' if auto_poll else 'πŸ“Š **Auto-polling disabled** - Check status manually'}
141
 
142
- ⏳ **Estimated time:** 10-30 seconds
143
 
144
- πŸ’‘ **Tip:** Save your Task ID: `{task_id}`"""
 
 
145
 
146
  else:
147
  error_msg = data.get("msg", f"HTTP {response.status_code}")
 
 
148
  return f"""❌ **Submission Failed**
149
 
150
  **Error:** {error_msg}
151
 
 
 
152
  πŸ’‘ **Possible solutions:**
153
  β€’ Check if your SunoKey is valid
 
154
  β€’ Try a different prompt
155
  β€’ Wait a few minutes and retry"""
156
 
157
  except requests.exceptions.Timeout:
 
158
  return "❌ Error: Request timeout - Suno API is not responding"
159
  except requests.exceptions.ConnectionError:
 
160
  return "❌ Error: Connection failed - Check your internet connection"
161
  except Exception as e:
 
162
  return f"❌ Error: {str(e)}"
163
 
164
- def check_task_status(task_id: str, auto_refresh: bool = False) -> str:
165
- """Check the status of a task"""
166
  if not task_id or not task_id.strip():
167
  return "❌ Please enter a Task ID"
168
 
@@ -170,19 +231,25 @@ def check_task_status(task_id: str, auto_refresh: bool = False) -> str:
170
  return f"❌ Task ID `{task_id}` not found. Please submit a task first."
171
 
172
  task = tasks_db[task_id]
 
173
 
174
- # If auto_refresh is True, force an immediate API check
175
- if auto_refresh:
 
 
176
  try:
177
- url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={task['api_task_id']}"
 
178
  headers = {"Authorization": f"Bearer {SUNO_KEY}"}
179
 
180
- response = requests.get(url, headers=headers, timeout=30)
181
  data = response.json()
182
 
183
  if response.status_code == 200 and data.get("code") == 200:
184
  task_data = data["data"]
185
  status = task_data.get("status", "unknown")
 
 
186
  tasks_db[task_id]["status"] = status
187
  tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
188
 
@@ -190,30 +257,39 @@ def check_task_status(task_id: str, auto_refresh: bool = False) -> str:
190
  lyrics_data = task_data["data"]
191
  tasks_db[task_id]["result"] = lyrics_data
192
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
 
 
 
 
193
 
194
- except Exception:
195
- pass # Keep existing status if API check fails
196
 
197
  status = task.get("status", "unknown")
198
  created_time = datetime.fromisoformat(task["created_at"])
199
  elapsed = int((datetime.now() - created_time).total_seconds())
200
 
 
 
 
201
  if status == "completed" and task.get("result"):
202
- # Format completed lyrics
203
  return format_lyrics_output(task["result"], task_id, elapsed)
204
 
205
  elif status == "failed":
206
  error_msg = task.get("error", "Unknown error")
 
207
  return f"""❌ **Task Failed**
208
 
209
  **Task ID:** `{task_id}`
 
210
  **Error:** {error_msg}
211
  **Elapsed time:** {elapsed} seconds
212
 
213
  πŸ’‘ Please try generating again with a different prompt."""
214
 
215
  else:
216
- # Still processing
217
  attempts = task.get("poll_attempts", 0)
218
  last_checked = task.get("last_checked")
219
  last_checked_str = ""
@@ -222,17 +298,34 @@ def check_task_status(task_id: str, auto_refresh: bool = False) -> str:
222
  last_time = datetime.fromisoformat(last_checked)
223
  last_checked_str = f"\n**Last checked:** {last_time.strftime('%H:%M:%S')}"
224
 
 
 
 
 
225
  return f"""⏳ **Task Processing...**
226
 
227
  **Task ID:** `{task_id}`
 
228
  **Status:** {status}
229
  **Elapsed:** {elapsed} seconds
230
  **Poll attempts:** {attempts}{last_checked_str}
231
 
232
- ⏰ **Estimated completion:** 10-30 seconds
233
- πŸ”„ {'Auto-polling active' if task.get('auto_poll') else 'Auto-polling disabled'}
 
 
 
 
 
234
 
235
- {'βœ… Results will appear automatically when ready!' if task.get('auto_poll') else 'πŸ“Š Click "Check Status" again to refresh'}"""
 
 
 
 
 
 
 
236
 
237
  def format_lyrics_output(lyrics_data, task_id, elapsed_time):
238
  """Format the lyrics for display"""
@@ -264,7 +357,7 @@ def format_lyrics_output(lyrics_data, task_id, elapsed_time):
264
  return "\n".join(output_lines)
265
 
266
  def list_all_tasks():
267
- """List all submitted tasks"""
268
  if not tasks_db:
269
  return "πŸ“­ No tasks found. Generate some lyrics first!"
270
 
@@ -272,6 +365,7 @@ def list_all_tasks():
272
 
273
  for task_id, task in sorted(tasks_db.items(), key=lambda x: x[1]["created_at"], reverse=True):
274
  status = task.get("status", "unknown")
 
275
  prompt_preview = task.get("prompt", "")[:50]
276
  created = task.get("created_at", "")[:19]
277
 
@@ -286,34 +380,71 @@ def list_all_tasks():
286
  icon = "⏳"
287
  color = "orange"
288
 
289
- output_lines.append(f"<span style='color:{color}'>{icon} **{task_id}** - {status}</span>")
 
 
 
 
 
290
  output_lines.append(f" Prompt: {prompt_preview}...")
291
  output_lines.append(f" Created: {created}")
 
 
 
 
 
292
 
293
  if task.get("completed_at"):
294
  completed = task["completed_at"][:19]
295
  output_lines.append(f" Completed: {completed}")
296
 
297
- if task.get("auto_poll"):
298
- output_lines.append(f" Auto-polling: βœ… Enabled")
299
-
300
  output_lines.append("")
301
 
302
  # Add summary
303
  completed = sum(1 for t in tasks_db.values() if t.get("status") == "completed")
304
  processing = sum(1 for t in tasks_db.values() if t.get("status") not in ["completed", "failed", "error"])
 
305
  total = len(tasks_db)
306
- output_lines.append(f"**Summary:** {completed} completed, {processing} processing, {total} total")
 
 
307
 
308
  return "\n".join(output_lines)
309
 
310
- def auto_refresh_display(task_id: str):
311
- """Auto-refresh the status display"""
312
- if not task_id or task_id not in tasks_db:
313
- return task_id, "❌ Invalid Task ID"
314
 
315
- result = check_task_status(task_id, auto_refresh=True)
316
- return task_id, result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
  # Create the Gradio interface
319
  with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
@@ -332,7 +463,7 @@ with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
332
  prompt_input = gr.Textbox(
333
  label="Lyrics Prompt",
334
  placeholder="Example: A romantic ballad about stargazing on a summer night...",
335
- lines=4
336
  )
337
 
338
  auto_poll_checkbox = gr.Checkbox(
@@ -343,12 +474,16 @@ with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
343
 
344
  submit_btn = gr.Button("πŸš€ Generate Lyrics", variant="primary", size="lg")
345
 
346
- gr.Markdown("### πŸ“ Tips:")
347
  gr.Markdown("""
348
- β€’ Be descriptive in your prompt
349
- β€’ Include genre, mood, or theme
350
- β€’ Typical processing time: 10-30 seconds
351
- β€’ Auto-polling will show results automatically
 
 
 
 
352
  """)
353
 
354
  with gr.Column(scale=3):
@@ -383,7 +518,7 @@ with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
383
  gr.Markdown("### Enter your Task ID")
384
  task_id_input = gr.Textbox(
385
  label="Task ID",
386
- placeholder="Paste your Task ID here (e.g., 7d3b63c4)",
387
  scale=1
388
  )
389
 
@@ -399,8 +534,8 @@ with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
399
 
400
  with gr.Row():
401
  check_btn = gr.Button("πŸ” Check Status", variant="primary")
 
402
  auto_refresh_btn = gr.Button("πŸ”„ Auto-refresh", variant="secondary")
403
- stop_refresh_btn = gr.Button("⏹️ Stop", variant="stop", visible=False)
404
 
405
  gr.Markdown("---")
406
  refresh_all_btn = gr.Button("πŸ“‹ List All Tasks")
@@ -419,130 +554,139 @@ with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
419
  outputs=status_output
420
  )
421
 
422
- # Auto-refresh function
423
- def start_auto_refresh(task_id, stop_flag):
424
- if not task_id:
425
- return task_id, "❌ Please enter a Task ID first", gr.update(visible=False), gr.update(visible=True)
426
-
427
- # Update button states
428
- return task_id, "πŸ”„ Starting auto-refresh...", gr.update(visible=True), gr.update(visible=False)
429
 
430
- def do_auto_refresh(task_id, iterations=10):
431
- results = []
432
- for i in range(iterations):
433
- if not task_id or task_id not in tasks_db:
434
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
- result = check_task_status(task_id, auto_refresh=True)
437
- results.append(result)
 
 
438
 
439
- # If completed, stop early
440
- task = tasks_db.get(task_id, {})
441
- if task.get("status") in ["completed", "failed"]:
442
- break
443
 
444
- # Wait between refreshes
445
- if i < iterations - 1:
446
- time.sleep(3)
447
-
448
- return task_id, results[-1] if results else "Auto-refresh stopped"
449
 
450
- # Connect auto-refresh button
451
- auto_refresh_click = auto_refresh_btn.click(
452
- fn=start_auto_refresh,
453
- inputs=[task_id_input, gr.State(False)],
454
- outputs=[task_id_input, status_output, stop_refresh_btn, auto_refresh_btn]
455
- ).then(
456
- fn=lambda: time.sleep(1),
457
  inputs=None,
458
- outputs=None
459
- ).then(
460
- fn=do_auto_refresh,
461
- inputs=task_id_input,
462
- outputs=[task_id_input, status_output]
463
  )
464
 
465
- # Stop button
466
- stop_refresh_btn.click(
467
- fn=lambda: ("", "⏹️ Auto-refresh stopped", gr.update(visible=False), gr.update(visible=True)),
468
  inputs=None,
469
- outputs=[task_id_input, status_output, stop_refresh_btn, auto_refresh_btn]
470
- )
471
-
472
- # List all tasks
473
- refresh_all_btn.click(
474
- fn=list_all_tasks,
475
  inputs=None,
476
- outputs=tasks_list
477
  )
478
 
479
- # Tab 3: Help & Info
480
  with gr.TabItem("ℹ️ Help"):
481
- gr.Markdown("# Help & Information")
482
 
483
  with gr.Row():
484
  with gr.Column():
485
- gr.Markdown("### πŸ”‘ API Status")
486
- api_status = "βœ… Configured" if SUNO_KEY else "❌ Not configured"
487
- gr.Markdown(f"**SunoKey:** {api_status}")
488
-
489
- gr.Markdown("### πŸš€ How to Use:")
490
  gr.Markdown("""
491
- 1. **Generate Tab:**
492
- - Enter lyrics prompt
493
- - Enable auto-polling (recommended)
494
- - Click Generate
495
- - Save your Task ID
 
496
 
497
- 2. **Check Status Tab:**
498
- - Paste your Task ID
499
- - Click "Check Status" for manual check
500
- - Click "Auto-refresh" for automatic updates
501
- - Results appear automatically with auto-polling
502
 
503
- 3. **Results:**
504
- - Appear automatically if auto-polling enabled
505
- - Can take 10-30 seconds
506
- - Multiple lyric variants provided
 
 
 
 
 
507
  """)
508
 
509
  with gr.Column():
510
- gr.Markdown("### πŸ”§ Features")
511
  gr.Markdown("""
512
- **Auto-polling:**
513
- - Background thread checks status every 2 seconds
514
- - Results appear automatically
515
- - Stops after completion or 2 minutes
 
 
 
 
 
 
 
516
 
517
- **Manual checking:**
518
- - Check specific Task ID status
519
- - Auto-refresh button for continuous updates
520
- - List all submitted tasks
521
 
522
- **Task management:**
523
- - All tasks stored in memory
524
- - Status tracking
525
- - Error handling
526
  """)
527
-
528
- gr.Markdown("### ⚠️ Troubleshooting")
529
- gr.Markdown("""
530
- **If tasks aren't completing:**
531
- 1. Check your SunoKey has available credits
532
- 2. Try simpler prompts
533
- 3. Wait 30+ seconds for processing
534
- 4. Use the "Auto-refresh" button
535
-
536
- **Common errors:**
537
- - "Task ID not found": Submit a new task
538
- - "SunoKey not set": Add API key in Space Settings
539
- - "Timeout": Suno API may be busy
540
- """)
541
 
542
  # Launch the app
543
  if __name__ == "__main__":
544
- print(f"πŸ”‘ SunoKey status: {'Configured' if SUNO_KEY else 'NOT SET'}")
545
- print("πŸš€ Starting Suno Lyrics Generator with auto-polling...")
 
 
 
 
 
 
 
546
 
547
  app.launch(
548
  server_name="0.0.0.0",
 
13
  if not SUNO_KEY:
14
  print("⚠️ Warning: SunoKey environment variable not set!")
15
 
16
+ # Debug mode
17
+ DEBUG = True
18
+
19
  # Task storage with auto-polling
20
  tasks_db = {}
21
  polling_threads = {}
22
 
23
+ def debug_log(message):
24
+ """Debug logging"""
25
+ if DEBUG:
26
+ timestamp = datetime.now().strftime("%H:%M:%S")
27
+ print(f"[{timestamp}] {message}")
28
+
29
+ def make_api_request(method, url, **kwargs):
30
+ """Make API request with detailed error logging"""
31
+ try:
32
+ debug_log(f"Making {method} request to {url}")
33
+ response = requests.request(method, url, **kwargs)
34
+ debug_log(f"Response status: {response.status_code}")
35
+
36
+ if DEBUG and len(response.content) < 10000: # Don't log huge responses
37
+ debug_log(f"Response: {response.text[:500]}")
38
+
39
+ return response
40
+ except Exception as e:
41
+ debug_log(f"API request error: {str(e)}")
42
+ raise
43
+
44
+ def poll_task_status(task_id: str, max_attempts=120, interval=3):
45
  """Background thread to automatically poll task status"""
46
  if task_id not in tasks_db:
47
+ debug_log(f"Task {task_id} not found in DB")
48
  return
49
 
50
  task = tasks_db[task_id]
51
  api_task_id = task["api_task_id"]
52
 
53
+ debug_log(f"Starting auto-polling for task {task_id} (API ID: {api_task_id})")
54
+
55
  for attempt in range(max_attempts):
56
  try:
57
+ debug_log(f"Poll attempt {attempt + 1} for task {task_id}")
58
+
59
  # Poll the API
60
  url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={api_task_id}"
61
  headers = {"Authorization": f"Bearer {SUNO_KEY}"}
62
 
63
+ response = make_api_request("GET", url, headers=headers, timeout=30)
64
  data = response.json()
65
 
66
  if response.status_code == 200 and data.get("code") == 200:
67
  task_data = data["data"]
68
  status = task_data.get("status", "unknown")
69
 
70
+ debug_log(f"Task {task_id} status: {status}")
71
+
72
  # Update task status
73
  tasks_db[task_id]["status"] = status
74
  tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
 
78
  lyrics_data = task_data["data"]
79
  tasks_db[task_id]["result"] = lyrics_data
80
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
81
+ debug_log(f"βœ… Task {task_id} completed successfully!")
82
  break
83
 
84
  elif status == "failed":
85
  error_msg = task_data.get("error", "Unknown error")
86
  tasks_db[task_id]["error"] = error_msg
87
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
88
+ debug_log(f"❌ Task {task_id} failed: {error_msg}")
89
  break
90
+ elif status == "processing":
91
+ debug_log(f"πŸ”„ Task {task_id} is processing...")
92
+ else:
93
+ debug_log(f"πŸ“Š Task {task_id} has unknown status: {status}")
94
+
95
+ else:
96
+ error_code = data.get("code", "unknown")
97
+ error_msg = data.get("msg", "No error message")
98
+ debug_log(f"❌ API error for task {task_id}: Code {error_code}, {error_msg}")
99
 
100
  # Update attempts
101
  tasks_db[task_id]["poll_attempts"] = attempt + 1
102
 
103
  except Exception as e:
104
+ debug_log(f"⚠️ Polling error for task {task_id}: {str(e)}")
105
 
106
  # Wait before next poll
107
  time.sleep(interval)
108
 
109
+ debug_log(f"Finished polling for task {task_id} after {max_attempts} attempts")
110
+
111
  # Clean up polling thread
112
  if task_id in polling_threads:
113
  del polling_threads[task_id]
 
123
  # Generate task ID
124
  task_id = str(uuid.uuid4())[:8]
125
 
126
+ debug_log(f"Starting generation for task {task_id} with prompt: {prompt[:50]}...")
127
+
128
  # Prepare API request
129
  url = "https://api.sunoapi.org/api/v1/lyrics"
130
  headers = {
 
132
  "Content-Type": "application/json"
133
  }
134
 
135
+ # Suno requires a callback URL
136
  dummy_callback = "https://dummy.callback.url/not-used"
137
 
138
  payload = {
 
141
  }
142
 
143
  try:
144
+ debug_log(f"Submitting to Suno API: {json.dumps(payload, indent=2)}")
145
+
146
  # Submit task
147
+ response = make_api_request("POST", url, headers=headers, json=payload, timeout=30)
148
  data = response.json()
149
 
150
+ debug_log(f"Submission response: {json.dumps(data, indent=2)[:500]}...")
151
+
152
  if response.status_code == 200 and data.get("code") == 200:
153
  api_task_id = data["data"]["taskId"]
154
 
155
+ debug_log(f"βœ… Submission successful! Task ID: {task_id}, API Task ID: {api_task_id}")
156
+
157
  # Store task information
158
  tasks_db[task_id] = {
159
  "id": task_id,
 
166
  "last_checked": None,
167
  "poll_attempts": 0,
168
  "completed_at": None,
169
+ "auto_poll": auto_poll,
170
+ "raw_response": data # Store for debugging
171
  }
172
 
173
  # Start auto-polling if enabled
 
179
  )
180
  polling_threads[task_id] = poll_thread
181
  poll_thread.start()
182
+ debug_log(f"πŸš€ Started auto-polling thread for task {task_id}")
183
 
184
  return f"""βœ… **Task Submitted Successfully!**
185
 
 
188
 
189
  πŸ“ **Prompt:** {prompt[:100]}{'...' if len(prompt) > 100 else ''}
190
 
191
+ πŸ”„ **Auto-polling enabled** - Results will appear automatically!
192
 
193
+ ⏳ **Estimated time:** Usually 10-60 seconds
194
 
195
+ πŸ“Š **Check Status tab** to monitor progress
196
+
197
+ πŸ’‘ **Save your Task ID:** `{task_id}`"""
198
 
199
  else:
200
  error_msg = data.get("msg", f"HTTP {response.status_code}")
201
+ debug_log(f"❌ Submission failed: {error_msg}")
202
+
203
  return f"""❌ **Submission Failed**
204
 
205
  **Error:** {error_msg}
206
 
207
+ **Response:** {json.dumps(data, indent=2)[:500]}
208
+
209
  πŸ’‘ **Possible solutions:**
210
  β€’ Check if your SunoKey is valid
211
+ β€’ Ensure API has available credits
212
  β€’ Try a different prompt
213
  β€’ Wait a few minutes and retry"""
214
 
215
  except requests.exceptions.Timeout:
216
+ debug_log("❌ Request timeout")
217
  return "❌ Error: Request timeout - Suno API is not responding"
218
  except requests.exceptions.ConnectionError:
219
+ debug_log("❌ Connection error")
220
  return "❌ Error: Connection failed - Check your internet connection"
221
  except Exception as e:
222
+ debug_log(f"❌ Unexpected error: {str(e)}")
223
  return f"❌ Error: {str(e)}"
224
 
225
+ def check_task_status(task_id: str, force_check: bool = False) -> str:
226
+ """Check the status of a task with detailed debugging"""
227
  if not task_id or not task_id.strip():
228
  return "❌ Please enter a Task ID"
229
 
 
231
  return f"❌ Task ID `{task_id}` not found. Please submit a task first."
232
 
233
  task = tasks_db[task_id]
234
+ api_task_id = task.get("api_task_id", "unknown")
235
 
236
+ debug_log(f"Checking status for task {task_id} (API: {api_task_id})")
237
+
238
+ # Force an immediate API check if requested
239
+ if force_check:
240
  try:
241
+ debug_log(f"Forcing API check for task {task_id}")
242
+ url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={api_task_id}"
243
  headers = {"Authorization": f"Bearer {SUNO_KEY}"}
244
 
245
+ response = make_api_request("GET", url, headers=headers, timeout=30)
246
  data = response.json()
247
 
248
  if response.status_code == 200 and data.get("code") == 200:
249
  task_data = data["data"]
250
  status = task_data.get("status", "unknown")
251
+ debug_log(f"Forced check result: {status}")
252
+
253
  tasks_db[task_id]["status"] = status
254
  tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
255
 
 
257
  lyrics_data = task_data["data"]
258
  tasks_db[task_id]["result"] = lyrics_data
259
  tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
260
+ elif status == "failed":
261
+ error_msg = task_data.get("error", "Unknown error")
262
+ tasks_db[task_id]["error"] = error_msg
263
+ tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
264
 
265
+ except Exception as e:
266
+ debug_log(f"Force check error: {str(e)}")
267
 
268
  status = task.get("status", "unknown")
269
  created_time = datetime.fromisoformat(task["created_at"])
270
  elapsed = int((datetime.now() - created_time).total_seconds())
271
 
272
+ debug_log(f"Task {task_id} - Status: {status}, Elapsed: {elapsed}s")
273
+
274
+ # Display based on status
275
  if status == "completed" and task.get("result"):
276
+ debug_log(f"Task {task_id} has completed results")
277
  return format_lyrics_output(task["result"], task_id, elapsed)
278
 
279
  elif status == "failed":
280
  error_msg = task.get("error", "Unknown error")
281
+ debug_log(f"Task {task_id} failed: {error_msg}")
282
  return f"""❌ **Task Failed**
283
 
284
  **Task ID:** `{task_id}`
285
+ **API Task ID:** `{api_task_id}`
286
  **Error:** {error_msg}
287
  **Elapsed time:** {elapsed} seconds
288
 
289
  πŸ’‘ Please try generating again with a different prompt."""
290
 
291
  else:
292
+ # Still processing or unknown status
293
  attempts = task.get("poll_attempts", 0)
294
  last_checked = task.get("last_checked")
295
  last_checked_str = ""
 
298
  last_time = datetime.fromisoformat(last_checked)
299
  last_checked_str = f"\n**Last checked:** {last_time.strftime('%H:%M:%S')}"
300
 
301
+ debug_info = ""
302
+ if DEBUG and "raw_response" in task:
303
+ debug_info = f"\n\n**Debug Info:**\n```json\n{json.dumps(task.get('raw_response', {}), indent=2)[:300]}...\n```"
304
+
305
  return f"""⏳ **Task Processing...**
306
 
307
  **Task ID:** `{task_id}`
308
+ **API Task ID:** `{api_task_id}`
309
  **Status:** {status}
310
  **Elapsed:** {elapsed} seconds
311
  **Poll attempts:** {attempts}{last_checked_str}
312
 
313
+ ⏰ **Status Guide:**
314
+ - **submitted:** Task accepted by API
315
+ - **processing:** AI is generating lyrics
316
+ - **completed:** Ready! Check auto-refresh
317
+ - **failed:** Error occurred
318
+
319
+ πŸ”„ **Auto-polling:** {'βœ… Active' if task.get('auto_poll') else '❌ Disabled'}
320
 
321
+ πŸ’‘ **What to do:**
322
+ 1. Wait 30-60 seconds for processing
323
+ 2. Click "Force Check" for immediate update
324
+ 3. Results appear automatically when ready{debug_info}"""
325
+
326
+ def force_check_task(task_id: str):
327
+ """Force an immediate API check"""
328
+ return check_task_status(task_id, force_check=True)
329
 
330
  def format_lyrics_output(lyrics_data, task_id, elapsed_time):
331
  """Format the lyrics for display"""
 
357
  return "\n".join(output_lines)
358
 
359
  def list_all_tasks():
360
+ """List all submitted tasks with detailed info"""
361
  if not tasks_db:
362
  return "πŸ“­ No tasks found. Generate some lyrics first!"
363
 
 
365
 
366
  for task_id, task in sorted(tasks_db.items(), key=lambda x: x[1]["created_at"], reverse=True):
367
  status = task.get("status", "unknown")
368
+ api_task_id = task.get("api_task_id", "unknown")
369
  prompt_preview = task.get("prompt", "")[:50]
370
  created = task.get("created_at", "")[:19]
371
 
 
380
  icon = "⏳"
381
  color = "orange"
382
 
383
+ # Age calculation
384
+ created_time = datetime.fromisoformat(task["created_at"])
385
+ age_seconds = int((datetime.now() - created_time).total_seconds())
386
+
387
+ output_lines.append(f"<span style='color:{color}'>{icon} **{task_id}** - {status} ({age_seconds}s)</span>")
388
+ output_lines.append(f" API ID: `{api_task_id}`")
389
  output_lines.append(f" Prompt: {prompt_preview}...")
390
  output_lines.append(f" Created: {created}")
391
+ output_lines.append(f" Poll attempts: {task.get('poll_attempts', 0)}")
392
+
393
+ if task.get("last_checked"):
394
+ last_checked = task["last_checked"][:19]
395
+ output_lines.append(f" Last checked: {last_checked}")
396
 
397
  if task.get("completed_at"):
398
  completed = task["completed_at"][:19]
399
  output_lines.append(f" Completed: {completed}")
400
 
 
 
 
401
  output_lines.append("")
402
 
403
  # Add summary
404
  completed = sum(1 for t in tasks_db.values() if t.get("status") == "completed")
405
  processing = sum(1 for t in tasks_db.values() if t.get("status") not in ["completed", "failed", "error"])
406
+ failed = sum(1 for t in tasks_db.values() if t.get("status") in ["failed", "error"])
407
  total = len(tasks_db)
408
+
409
+ output_lines.append(f"**Summary:** {completed} βœ…, {processing} ⏳, {failed} ❌, {total} total")
410
+ output_lines.append(f"**Active poll threads:** {len(polling_threads)}")
411
 
412
  return "\n".join(output_lines)
413
 
414
+ def get_diagnostic_info():
415
+ """Get diagnostic information about the system"""
416
+ info_lines = ["# 🩺 Diagnostic Information", ""]
 
417
 
418
+ # API Key status
419
+ api_status = "βœ… Configured" if SUNO_KEY else "❌ NOT SET"
420
+ api_preview = SUNO_KEY[:10] + "..." if SUNO_KEY and len(SUNO_KEY) > 10 else SUNO_KEY or "None"
421
+ info_lines.append(f"**SunoKey:** {api_status} ({api_preview})")
422
+
423
+ # Tasks summary
424
+ info_lines.append(f"\n**Tasks in memory:** {len(tasks_db)}")
425
+ info_lines.append(f"**Active poll threads:** {len(polling_threads)}")
426
+
427
+ # Recent tasks
428
+ if tasks_db:
429
+ info_lines.append("\n**Recent Tasks:**")
430
+ for task_id, task in sorted(tasks_db.items(), key=lambda x: x[1]["created_at"], reverse=True)[:5]:
431
+ status = task.get("status", "unknown")
432
+ age = int((datetime.now() - datetime.fromisoformat(task["created_at"])).total_seconds())
433
+ info_lines.append(f"- `{task_id}`: {status} ({age}s ago)")
434
+
435
+ # Test API connection
436
+ info_lines.append("\n**API Connection Test:**")
437
+ try:
438
+ test_response = requests.get("https://api.sunoapi.org", timeout=5)
439
+ info_lines.append(f"βœ… Suno API reachable (HTTP {test_response.status_code})")
440
+ except Exception as e:
441
+ info_lines.append(f"❌ Cannot reach Suno API: {str(e)}")
442
+
443
+ # Debug info
444
+ info_lines.append(f"\n**Debug Mode:** {'βœ… Enabled' if DEBUG else '❌ Disabled'}")
445
+ info_lines.append(f"**Current Time:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
446
+
447
+ return "\n".join(info_lines)
448
 
449
  # Create the Gradio interface
450
  with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
 
463
  prompt_input = gr.Textbox(
464
  label="Lyrics Prompt",
465
  placeholder="Example: A romantic ballad about stargazing on a summer night...",
466
+ lines=3
467
  )
468
 
469
  auto_poll_checkbox = gr.Checkbox(
 
474
 
475
  submit_btn = gr.Button("πŸš€ Generate Lyrics", variant="primary", size="lg")
476
 
477
+ gr.Markdown("### ⚠️ Current Issue:")
478
  gr.Markdown("""
479
+ Tasks are getting stuck in "submitted" status.
480
+ This version includes **debugging tools** to diagnose the issue.
481
+
482
+ **What to try:**
483
+ 1. Submit a simple prompt
484
+ 2. Check "Diagnostic" tab
485
+ 3. Use "Force Check" button
486
+ 4. Monitor debug logs
487
  """)
488
 
489
  with gr.Column(scale=3):
 
518
  gr.Markdown("### Enter your Task ID")
519
  task_id_input = gr.Textbox(
520
  label="Task ID",
521
+ placeholder="Paste your Task ID here (e.g., 0f015fcb)",
522
  scale=1
523
  )
524
 
 
534
 
535
  with gr.Row():
536
  check_btn = gr.Button("πŸ” Check Status", variant="primary")
537
+ force_check_btn = gr.Button("⚑ Force Check", variant="secondary")
538
  auto_refresh_btn = gr.Button("πŸ”„ Auto-refresh", variant="secondary")
 
539
 
540
  gr.Markdown("---")
541
  refresh_all_btn = gr.Button("πŸ“‹ List All Tasks")
 
554
  outputs=status_output
555
  )
556
 
557
+ # Force check (immediate API call)
558
+ force_check_btn.click(
559
+ fn=force_check_task,
560
+ inputs=task_id_input,
561
+ outputs=status_output
562
+ )
 
563
 
564
+ # List all tasks
565
+ refresh_all_btn.click(
566
+ fn=list_all_tasks,
567
+ inputs=None,
568
+ outputs=tasks_list
569
+ )
570
+
571
+ # Tab 3: Diagnostic
572
+ with gr.TabItem("🩺 Diagnostic"):
573
+ gr.Markdown("# System Diagnostics")
574
+ gr.Markdown("Use this tab to diagnose why tasks are getting stuck")
575
+
576
+ with gr.Row():
577
+ with gr.Column():
578
+ diagnostic_btn = gr.Button("πŸ”„ Refresh Diagnostics", variant="primary")
579
+ diagnostic_output = gr.Markdown(label="Diagnostic Info")
580
+
581
+ with gr.Column():
582
+ gr.Markdown("### 🚨 Common Issues:")
583
+ gr.Markdown("""
584
+ **1. API Key Issues:**
585
+ - Invalid or expired SunoKey
586
+ - No API credits remaining
587
+ - Incorrect environment variable name
588
 
589
+ **2. API Response Issues:**
590
+ - Suno API returning errors
591
+ - Tasks stuck in queue
592
+ - Rate limiting
593
 
594
+ **3. Network Issues:**
595
+ - Cannot reach api.sunoapi.org
596
+ - Timeout errors
597
+ - Connection refused
598
 
599
+ **4. Task Processing:**
600
+ - Suno AI taking longer than expected
601
+ - Tasks stuck in "submitted" state
602
+ - Server-side delays
603
+ """)
604
 
605
+ diagnostic_btn.click(
606
+ fn=get_diagnostic_info,
 
 
 
 
 
607
  inputs=None,
608
+ outputs=diagnostic_output
 
 
 
 
609
  )
610
 
611
+ # Auto-refresh diagnostics every 10 seconds
612
+ diagnostic_btn.click(
613
+ fn=lambda: time.sleep(10),
614
  inputs=None,
615
+ outputs=None
616
+ ).then(
617
+ fn=get_diagnostic_info,
 
 
 
618
  inputs=None,
619
+ outputs=diagnostic_output
620
  )
621
 
622
+ # Tab 4: Help
623
  with gr.TabItem("ℹ️ Help"):
624
+ gr.Markdown("# Help & Troubleshooting")
625
 
626
  with gr.Row():
627
  with gr.Column():
628
+ gr.Markdown("### πŸ› Debugging Stuck Tasks")
 
 
 
 
629
  gr.Markdown("""
630
+ **If tasks are stuck in "submitted":**
631
+
632
+ 1. **Check Diagnostic Tab:**
633
+ - Verify API key is set
634
+ - Test API connection
635
+ - View recent task status
636
 
637
+ 2. **Use Force Check:**
638
+ - Makes immediate API call
639
+ - Bypasses cached status
640
+ - Shows raw API response
 
641
 
642
+ 3. **Monitor Debug Logs:**
643
+ - Check Space logs (bottom of page)
644
+ - Look for API errors
645
+ - Note timeout messages
646
+
647
+ 4. **Try Simple Test:**
648
+ - Use a very simple prompt
649
+ - Disable auto-polling
650
+ - Check after 60 seconds
651
  """)
652
 
653
  with gr.Column():
654
+ gr.Markdown("### πŸ“ž Support")
655
  gr.Markdown("""
656
+ **If issues persist:**
657
+
658
+ 1. **Check SunoKey:**
659
+ - Ensure it's valid
660
+ - Check credit balance
661
+ - Try in Suno's own interface
662
+
663
+ 2. **API Status:**
664
+ - Suno API may be down
665
+ - Check Suno status page
666
+ - Wait and try later
667
 
668
+ 3. **Contact Support:**
669
+ - Suno API support
670
+ - Provide your Task IDs
671
+ - Share debug logs
672
 
673
+ 4. **Alternative:**
674
+ - Try a different prompt
675
+ - Wait 5 minutes
676
+ - Restart the Space
677
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678
 
679
  # Launch the app
680
  if __name__ == "__main__":
681
+ print("=" * 60)
682
+ print("πŸš€ Starting Suno Lyrics Generator - DEBUG MODE")
683
+ print("=" * 60)
684
+ print(f"πŸ”‘ SunoKey: {'βœ… Configured' if SUNO_KEY else '❌ NOT SET'}")
685
+ if SUNO_KEY:
686
+ print(f"πŸ”‘ Preview: {SUNO_KEY[:10]}...")
687
+ print(f"πŸ› Debug Mode: {'βœ… Enabled' if DEBUG else '❌ Disabled'}")
688
+ print(f"πŸ“Š Pre-existing tasks: {len(tasks_db)}")
689
+ print("=" * 60)
690
 
691
  app.launch(
692
  server_name="0.0.0.0",