File size: 21,148 Bytes
5621d80
 
7155a3e
2eaa647
 
c063c00
77c8e1c
c3f6215
c063c00
d3cdef9
c3f6215
c063c00
 
 
e1c2b50
77c8e1c
c3f6215
77c8e1c
d3cdef9
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3f6215
5621d80
c3f6215
c063c00
c3f6215
 
2eaa647
c3f6215
 
5621d80
c3f6215
5621d80
 
 
 
 
2eaa647
77c8e1c
c3f6215
c063c00
5621d80
 
c3f6215
5621d80
2eaa647
5621d80
c3f6215
c063c00
 
5621d80
c3f6215
2eaa647
 
c3f6215
 
 
2eaa647
c3f6215
 
 
 
 
77c8e1c
 
 
 
2eaa647
 
77c8e1c
 
 
 
 
 
 
 
 
 
 
c063c00
 
 
c3f6215
2eaa647
c063c00
2eaa647
77c8e1c
c063c00
77c8e1c
 
 
2eaa647
5621d80
c3f6215
c063c00
 
c3f6215
c063c00
 
c3f6215
c063c00
c3f6215
c063c00
c3f6215
 
 
 
d3cdef9
c3f6215
5621d80
77c8e1c
 
c063c00
 
2eaa647
c3f6215
 
5621d80
c3f6215
 
77c8e1c
 
 
 
 
c3f6215
77c8e1c
 
c3f6215
77c8e1c
 
 
 
 
c3f6215
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c063c00
 
c3f6215
77c8e1c
c063c00
c3f6215
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
c063c00
 
 
77c8e1c
 
c063c00
77c8e1c
 
5621d80
77c8e1c
c063c00
77c8e1c
c3f6215
c063c00
 
 
c3f6215
 
 
77c8e1c
 
c3f6215
 
5621d80
2eaa647
 
c3f6215
2eaa647
c3f6215
 
 
 
 
 
2eaa647
c3f6215
 
c063c00
c3f6215
 
c063c00
c3f6215
 
c063c00
 
c3f6215
c063c00
77c8e1c
c3f6215
 
 
 
77c8e1c
c3f6215
 
77c8e1c
c3f6215
 
77c8e1c
c063c00
 
77c8e1c
c063c00
77c8e1c
c3f6215
 
c063c00
77c8e1c
 
 
 
 
 
c063c00
c3f6215
5621d80
c3f6215
 
77c8e1c
c3f6215
77c8e1c
c063c00
c3f6215
e1c2b50
77c8e1c
 
c3f6215
 
c063c00
77c8e1c
c3f6215
2eaa647
c3f6215
 
5621d80
c3f6215
2eaa647
77c8e1c
 
 
2eaa647
c3f6215
c063c00
2eaa647
c063c00
c3f6215
c063c00
2eaa647
c3f6215
c063c00
2eaa647
 
77c8e1c
 
 
 
 
 
c3f6215
2eaa647
c3f6215
2eaa647
c3f6215
 
 
77c8e1c
2eaa647
 
c063c00
c3f6215
 
 
2eaa647
5621d80
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
 
2eaa647
77c8e1c
 
 
2eaa647
 
c3f6215
c063c00
5621d80
2eaa647
c3f6215
2eaa647
c063c00
77c8e1c
c3f6215
2eaa647
 
77c8e1c
 
 
 
 
 
 
 
 
 
c063c00
c3f6215
 
77c8e1c
c063c00
c3f6215
 
 
5621d80
2eaa647
c063c00
c3f6215
 
2eaa647
 
77c8e1c
c063c00
c3f6215
2eaa647
c063c00
2eaa647
 
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3f6215
77c8e1c
c3f6215
 
 
77c8e1c
c3f6215
 
c063c00
 
77c8e1c
 
 
 
 
 
 
 
c3f6215
 
2eaa647
c3f6215
2eaa647
 
c3f6215
 
 
c063c00
 
 
c3f6215
 
 
c063c00
77c8e1c
c3f6215
77c8e1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3f6215
c063c00
 
77c8e1c
c3f6215
77c8e1c
 
 
 
c063c00
77c8e1c
 
 
 
c3f6215
77c8e1c
 
 
 
c3f6215
2eaa647
77c8e1c
c3f6215
77c8e1c
 
 
 
 
2eaa647
77c8e1c
 
 
 
5621d80
c063c00
 
597c3bb
c3f6215
77c8e1c
2eaa647
5621d80
 
 
2eaa647
c063c00
c3f6215
5621d80
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
import gradio as gr
import requests
import os
import json
import uuid
import time
import threading
from datetime import datetime
from typing import Dict, Optional

# Load Suno API key
SUNO_KEY = os.environ.get("SunoKey", "")
if not SUNO_KEY:
    print("⚠️ Warning: SunoKey environment variable not set!")

# Task storage with auto-polling
tasks_db = {}
polling_threads = {}

def poll_task_status(task_id: str, max_attempts=60, interval=2):
    """Background thread to automatically poll task status"""
    if task_id not in tasks_db:
        return
    
    task = tasks_db[task_id]
    api_task_id = task["api_task_id"]
    
    for attempt in range(max_attempts):
        try:
            # Poll the API
            url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={api_task_id}"
            headers = {"Authorization": f"Bearer {SUNO_KEY}"}
            
            response = requests.get(url, headers=headers, timeout=30)
            data = response.json()
            
            if response.status_code == 200 and data.get("code") == 200:
                task_data = data["data"]
                status = task_data.get("status", "unknown")
                
                # Update task status
                tasks_db[task_id]["status"] = status
                tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
                
                if status == "completed" and "data" in task_data:
                    # Task completed successfully
                    lyrics_data = task_data["data"]
                    tasks_db[task_id]["result"] = lyrics_data
                    tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
                    print(f"✅ Task {task_id} completed via auto-polling")
                    break
                
                elif status == "failed":
                    error_msg = task_data.get("error", "Unknown error")
                    tasks_db[task_id]["error"] = error_msg
                    tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
                    print(f"❌ Task {task_id} failed: {error_msg}")
                    break
            
            # Update attempts
            tasks_db[task_id]["poll_attempts"] = attempt + 1
            
        except Exception as e:
            print(f"⚠️ Polling error for task {task_id}: {str(e)}")
        
        # Wait before next poll
        time.sleep(interval)
    
    # Clean up polling thread
    if task_id in polling_threads:
        del polling_threads[task_id]

def generate_lyrics(prompt: str, auto_poll: bool = True) -> str:
    """Submit lyrics generation task to Suno API"""
    if not SUNO_KEY:
        return "❌ Error: SunoKey environment variable not set. Please add it in Space Settings."
    
    if not prompt or not prompt.strip():
        return "❌ Please enter a lyrics prompt"
    
    # Generate task ID
    task_id = str(uuid.uuid4())[:8]
    
    # Prepare API request
    url = "https://api.sunoapi.org/api/v1/lyrics"
    headers = {
        "Authorization": f"Bearer {SUNO_KEY}",
        "Content-Type": "application/json"
    }
    
    # Suno requires a callback URL, but we'll use a dummy one
    dummy_callback = "https://dummy.callback.url/not-used"
    
    payload = {
        "prompt": prompt,
        "callBackUrl": dummy_callback
    }
    
    try:
        # Submit task
        response = requests.post(url, headers=headers, json=payload, timeout=30)
        data = response.json()
        
        if response.status_code == 200 and data.get("code") == 200:
            api_task_id = data["data"]["taskId"]
            
            # Store task information
            tasks_db[task_id] = {
                "id": task_id,
                "api_task_id": api_task_id,
                "prompt": prompt,
                "status": "submitted",
                "result": None,
                "error": None,
                "created_at": datetime.now().isoformat(),
                "last_checked": None,
                "poll_attempts": 0,
                "completed_at": None,
                "auto_poll": auto_poll
            }
            
            # Start auto-polling if enabled
            if auto_poll:
                poll_thread = threading.Thread(
                    target=poll_task_status,
                    args=(task_id,),
                    daemon=True
                )
                polling_threads[task_id] = poll_thread
                poll_thread.start()
                print(f"🚀 Started auto-polling for task {task_id}")
            
            return f"""✅ **Task Submitted Successfully!**

**Your Task ID:** `{task_id}`
**API Task ID:** `{api_task_id}`

📝 **Prompt:** {prompt[:100]}{'...' if len(prompt) > 100 else ''}

{'🔄 **Auto-polling enabled** - Results will appear automatically!' if auto_poll else '📊 **Auto-polling disabled** - Check status manually'}

⏳ **Estimated time:** 10-30 seconds

💡 **Tip:** Save your Task ID: `{task_id}`"""
        
        else:
            error_msg = data.get("msg", f"HTTP {response.status_code}")
            return f"""❌ **Submission Failed**

**Error:** {error_msg}

💡 **Possible solutions:**
• Check if your SunoKey is valid
• Try a different prompt
• Wait a few minutes and retry"""
    
    except requests.exceptions.Timeout:
        return "❌ Error: Request timeout - Suno API is not responding"
    except requests.exceptions.ConnectionError:
        return "❌ Error: Connection failed - Check your internet connection"
    except Exception as e:
        return f"❌ Error: {str(e)}"

def check_task_status(task_id: str, auto_refresh: bool = False) -> str:
    """Check the status of a task"""
    if not task_id or not task_id.strip():
        return "❌ Please enter a Task ID"
    
    if task_id not in tasks_db:
        return f"❌ Task ID `{task_id}` not found. Please submit a task first."
    
    task = tasks_db[task_id]
    
    # If auto_refresh is True, force an immediate API check
    if auto_refresh:
        try:
            url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={task['api_task_id']}"
            headers = {"Authorization": f"Bearer {SUNO_KEY}"}
            
            response = requests.get(url, headers=headers, timeout=30)
            data = response.json()
            
            if response.status_code == 200 and data.get("code") == 200:
                task_data = data["data"]
                status = task_data.get("status", "unknown")
                tasks_db[task_id]["status"] = status
                tasks_db[task_id]["last_checked"] = datetime.now().isoformat()
                
                if status == "completed" and "data" in task_data:
                    lyrics_data = task_data["data"]
                    tasks_db[task_id]["result"] = lyrics_data
                    tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
        
        except Exception:
            pass  # Keep existing status if API check fails
    
    status = task.get("status", "unknown")
    created_time = datetime.fromisoformat(task["created_at"])
    elapsed = int((datetime.now() - created_time).total_seconds())
    
    if status == "completed" and task.get("result"):
        # Format completed lyrics
        return format_lyrics_output(task["result"], task_id, elapsed)
    
    elif status == "failed":
        error_msg = task.get("error", "Unknown error")
        return f"""❌ **Task Failed**

**Task ID:** `{task_id}`
**Error:** {error_msg}
**Elapsed time:** {elapsed} seconds

💡 Please try generating again with a different prompt."""
    
    else:
        # Still processing
        attempts = task.get("poll_attempts", 0)
        last_checked = task.get("last_checked")
        last_checked_str = ""
        
        if last_checked:
            last_time = datetime.fromisoformat(last_checked)
            last_checked_str = f"\n**Last checked:** {last_time.strftime('%H:%M:%S')}"
        
        return f"""⏳ **Task Processing...**

**Task ID:** `{task_id}`
**Status:** {status}
**Elapsed:** {elapsed} seconds
**Poll attempts:** {attempts}{last_checked_str}

⏰ **Estimated completion:** 10-30 seconds
🔄 {'Auto-polling active' if task.get('auto_poll') else 'Auto-polling disabled'}

{'✅ Results will appear automatically when ready!' if task.get('auto_poll') else '📊 Click "Check Status" again to refresh'}"""

def format_lyrics_output(lyrics_data, task_id, elapsed_time):
    """Format the lyrics for display"""
    if not lyrics_data:
        return "✅ Task completed but no lyrics data received"
    
    output_lines = [
        f"# 🎵 Generated Lyrics (Task: {task_id})",
        "",
        f"**Generated in:** {elapsed_time} seconds",
        f"**Completed at:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
        ""
    ]
    
    for i, item in enumerate(lyrics_data, 1):
        title = item.get('title', f'Variant {i}')
        lyrics = item.get('text', 'No lyrics generated')
        
        output_lines.append(f"## Variant {i}: {title}")
        output_lines.append("```")
        output_lines.append(lyrics)
        output_lines.append("```")
        output_lines.append("---")
        output_lines.append("")
    
    output_lines.append("### 🎉 All done!")
    output_lines.append("You can generate more lyrics or try different prompts.")
    
    return "\n".join(output_lines)

def list_all_tasks():
    """List all submitted tasks"""
    if not tasks_db:
        return "📭 No tasks found. Generate some lyrics first!"
    
    output_lines = ["# 📋 All Submitted Tasks", ""]
    
    for task_id, task in sorted(tasks_db.items(), key=lambda x: x[1]["created_at"], reverse=True):
        status = task.get("status", "unknown")
        prompt_preview = task.get("prompt", "")[:50]
        created = task.get("created_at", "")[:19]
        
        # Status icon and color
        if status == "completed":
            icon = "✅"
            color = "green"
        elif status in ["failed", "error"]:
            icon = "❌"
            color = "red"
        else:
            icon = "⏳"
            color = "orange"
        
        output_lines.append(f"<span style='color:{color}'>{icon} **{task_id}** - {status}</span>")
        output_lines.append(f"   Prompt: {prompt_preview}...")
        output_lines.append(f"   Created: {created}")
        
        if task.get("completed_at"):
            completed = task["completed_at"][:19]
            output_lines.append(f"   Completed: {completed}")
        
        if task.get("auto_poll"):
            output_lines.append(f"   Auto-polling: ✅ Enabled")
        
        output_lines.append("")
    
    # Add summary
    completed = sum(1 for t in tasks_db.values() if t.get("status") == "completed")
    processing = sum(1 for t in tasks_db.values() if t.get("status") not in ["completed", "failed", "error"])
    total = len(tasks_db)
    output_lines.append(f"**Summary:** {completed} completed, {processing} processing, {total} total")
    
    return "\n".join(output_lines)

def auto_refresh_display(task_id: str):
    """Auto-refresh the status display"""
    if not task_id or task_id not in tasks_db:
        return task_id, "❌ Invalid Task ID"
    
    result = check_task_status(task_id, auto_refresh=True)
    return task_id, result

# Create the Gradio interface
with gr.Blocks(title="Suno Lyrics Generator", theme=gr.themes.Soft()) as app:
    gr.Markdown("# 🎵 Suno AI Lyrics Generator")
    gr.Markdown("Generate song lyrics using Suno's AI API")
    
    # Store the current task ID in a hidden state
    current_task_id = gr.State("")
    
    with gr.Tabs():
        # Tab 1: Generate Lyrics
        with gr.TabItem("✨ Generate"):
            with gr.Row():
                with gr.Column(scale=2):
                    gr.Markdown("### Enter your lyrics idea")
                    prompt_input = gr.Textbox(
                        label="Lyrics Prompt",
                        placeholder="Example: A romantic ballad about stargazing on a summer night...",
                        lines=4
                    )
                    
                    auto_poll_checkbox = gr.Checkbox(
                        label="🔁 Enable auto-polling",
                        value=True,
                        info="Automatically check for results (recommended)"
                    )
                    
                    submit_btn = gr.Button("🚀 Generate Lyrics", variant="primary", size="lg")
                    
                    gr.Markdown("### 📝 Tips:")
                    gr.Markdown("""
                    • Be descriptive in your prompt
                    • Include genre, mood, or theme
                    • Typical processing time: 10-30 seconds
                    • Auto-polling will show results automatically
                    """)
                
                with gr.Column(scale=3):
                    output_area = gr.Markdown(
                        label="Result",
                        value="Your task submission result will appear here..."
                    )
            
            def handle_generation(prompt, auto_poll):
                result = generate_lyrics(prompt, auto_poll)
                # Extract task ID from result
                task_id = ""
                if "Task ID:" in result:
                    for line in result.split('\n'):
                        if 'Task ID:' in line:
                            parts = line.split('`')
                            if len(parts) > 1:
                                task_id = parts[1]
                                break
                return result, task_id
            
            submit_btn.click(
                fn=handle_generation,
                inputs=[prompt_input, auto_poll_checkbox],
                outputs=[output_area, current_task_id]
            )
        
        # Tab 2: Check Status
        with gr.TabItem("🔍 Check Status"):
            with gr.Row():
                with gr.Column():
                    gr.Markdown("### Enter your Task ID")
                    task_id_input = gr.Textbox(
                        label="Task ID",
                        placeholder="Paste your Task ID here (e.g., 7d3b63c4)",
                        scale=1
                    )
                    
                    # Auto-populate if we have a current task ID
                    def update_task_id_input(current_id):
                        return current_id if current_id else ""
                    
                    current_task_id.change(
                        fn=update_task_id_input,
                        inputs=current_task_id,
                        outputs=task_id_input
                    )
                    
                    with gr.Row():
                        check_btn = gr.Button("🔍 Check Status", variant="primary")
                        auto_refresh_btn = gr.Button("🔄 Auto-refresh", variant="secondary")
                        stop_refresh_btn = gr.Button("⏹️ Stop", variant="stop", visible=False)
                    
                    gr.Markdown("---")
                    refresh_all_btn = gr.Button("📋 List All Tasks")
                    tasks_list = gr.Markdown(label="All Tasks")
                
                with gr.Column():
                    status_output = gr.Markdown(
                        label="Status",
                        value="Enter a Task ID above and click Check Status"
                    )
            
            # Regular check
            check_btn.click(
                fn=check_task_status,
                inputs=task_id_input,
                outputs=status_output
            )
            
            # Auto-refresh function
            def start_auto_refresh(task_id, stop_flag):
                if not task_id:
                    return task_id, "❌ Please enter a Task ID first", gr.update(visible=False), gr.update(visible=True)
                
                # Update button states
                return task_id, "🔄 Starting auto-refresh...", gr.update(visible=True), gr.update(visible=False)
            
            def do_auto_refresh(task_id, iterations=10):
                results = []
                for i in range(iterations):
                    if not task_id or task_id not in tasks_db:
                        break
                    
                    result = check_task_status(task_id, auto_refresh=True)
                    results.append(result)
                    
                    # If completed, stop early
                    task = tasks_db.get(task_id, {})
                    if task.get("status") in ["completed", "failed"]:
                        break
                    
                    # Wait between refreshes
                    if i < iterations - 1:
                        time.sleep(3)
                
                return task_id, results[-1] if results else "Auto-refresh stopped"
            
            # Connect auto-refresh button
            auto_refresh_click = auto_refresh_btn.click(
                fn=start_auto_refresh,
                inputs=[task_id_input, gr.State(False)],
                outputs=[task_id_input, status_output, stop_refresh_btn, auto_refresh_btn]
            ).then(
                fn=lambda: time.sleep(1),
                inputs=None,
                outputs=None
            ).then(
                fn=do_auto_refresh,
                inputs=task_id_input,
                outputs=[task_id_input, status_output]
            )
            
            # Stop button
            stop_refresh_btn.click(
                fn=lambda: ("", "⏹️ Auto-refresh stopped", gr.update(visible=False), gr.update(visible=True)),
                inputs=None,
                outputs=[task_id_input, status_output, stop_refresh_btn, auto_refresh_btn]
            )
            
            # List all tasks
            refresh_all_btn.click(
                fn=list_all_tasks,
                inputs=None,
                outputs=tasks_list
            )
        
        # Tab 3: Help & Info
        with gr.TabItem("ℹ️ Help"):
            gr.Markdown("# Help & Information")
            
            with gr.Row():
                with gr.Column():
                    gr.Markdown("### 🔑 API Status")
                    api_status = "✅ Configured" if SUNO_KEY else "❌ Not configured"
                    gr.Markdown(f"**SunoKey:** {api_status}")
                    
                    gr.Markdown("### 🚀 How to Use:")
                    gr.Markdown("""
                    1. **Generate Tab:** 
                       - Enter lyrics prompt
                       - Enable auto-polling (recommended)
                       - Click Generate
                       - Save your Task ID
                    
                    2. **Check Status Tab:**
                       - Paste your Task ID
                       - Click "Check Status" for manual check
                       - Click "Auto-refresh" for automatic updates
                       - Results appear automatically with auto-polling
                    
                    3. **Results:**
                       - Appear automatically if auto-polling enabled
                       - Can take 10-30 seconds
                       - Multiple lyric variants provided
                    """)
                
                with gr.Column():
                    gr.Markdown("### 🔧 Features")
                    gr.Markdown("""
                    **Auto-polling:**
                    - Background thread checks status every 2 seconds
                    - Results appear automatically
                    - Stops after completion or 2 minutes
                    
                    **Manual checking:**
                    - Check specific Task ID status
                    - Auto-refresh button for continuous updates
                    - List all submitted tasks
                    
                    **Task management:**
                    - All tasks stored in memory
                    - Status tracking
                    - Error handling
                    """)
            
            gr.Markdown("### ⚠️ Troubleshooting")
            gr.Markdown("""
            **If tasks aren't completing:**
            1. Check your SunoKey has available credits
            2. Try simpler prompts
            3. Wait 30+ seconds for processing
            4. Use the "Auto-refresh" button
            
            **Common errors:**
            - "Task ID not found": Submit a new task
            - "SunoKey not set": Add API key in Space Settings
            - "Timeout": Suno API may be busy
            """)

# Launch the app
if __name__ == "__main__":
    print(f"🔑 SunoKey status: {'Configured' if SUNO_KEY else 'NOT SET'}")
    print("🚀 Starting Suno Lyrics Generator with auto-polling...")
    
    app.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        debug=False,
        show_error=True
    )