Spaces:
Paused
Paused
| import gradio as gr | |
| import requests | |
| import os | |
| import json | |
| import uuid | |
| import time | |
| 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 | |
| tasks_db = {} | |
| def generate_lyrics(prompt: str) -> 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 and rely on polling | |
| 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 | |
| } | |
| return f"""✅ **Task Submitted Successfully!** | |
| **Your Task ID:** `{task_id}` | |
| **API Task ID:** `{api_task_id}` | |
| 📝 **Prompt:** {prompt[:100]}{'...' if len(prompt) > 100 else ''} | |
| ⏳ **Next Steps:** | |
| 1. Task is now processing with Suno AI | |
| 2. This usually takes 10-30 seconds | |
| 3. Click **"Check Status"** tab to check progress | |
| 4. Use your Task ID: `{task_id}` | |
| 💡 **Tip:** Save your Task ID to check results later!""" | |
| 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 requests.exceptions.HTTPError as e: | |
| return f"❌ Error: HTTP {e.response.status_code if e.response else 'Unknown'}" | |
| except Exception as e: | |
| return f"❌ Error: {str(e)}" | |
| def check_task_status(task_id: str) -> str: | |
| """Check the status of a task using polling""" | |
| 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] | |
| api_task_id = task["api_task_id"] | |
| # Poll the Suno API | |
| url = f"https://api.sunoapi.org/api/v1/lyrics/details?taskId={api_task_id}" | |
| headers = {"Authorization": f"Bearer {SUNO_KEY}"} | |
| try: | |
| 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 | |
| # Format the output | |
| return format_lyrics_output(lyrics_data, task_id) | |
| elif status == "failed": | |
| error_msg = task_data.get("error", "Unknown error") | |
| tasks_db[task_id]["error"] = error_msg | |
| return f"""❌ **Task Failed** | |
| **Task ID:** `{task_id}` | |
| **Error:** {error_msg} | |
| 💡 Please try generating again with a different prompt.""" | |
| else: | |
| # Still processing | |
| elapsed = time_since(task["created_at"]) | |
| return f"""⏳ **Task Processing...** | |
| **Task ID:** `{task_id}` | |
| **Status:** {status} | |
| **Elapsed:** {elapsed} | |
| ⏰ **Estimated time:** 10-30 seconds | |
| 🔄 **Auto-refresh recommended** | |
| 💡 Check back in a few seconds!""" | |
| else: | |
| error_msg = data.get("msg", f"HTTP {response.status_code}") | |
| return f"❌ Error checking status: {error_msg}" | |
| except Exception as e: | |
| return f"❌ Error: {str(e)}" | |
| def format_lyrics_output(lyrics_data, task_id): | |
| """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 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 time_since(iso_timestamp: str) -> str: | |
| """Calculate time elapsed since timestamp""" | |
| try: | |
| created = datetime.fromisoformat(iso_timestamp) | |
| elapsed = datetime.now() - created | |
| seconds = int(elapsed.total_seconds()) | |
| if seconds < 60: | |
| return f"{seconds} seconds" | |
| elif seconds < 3600: | |
| minutes = seconds // 60 | |
| return f"{minutes} minutes" | |
| else: | |
| hours = seconds // 3600 | |
| return f"{hours} hours" | |
| except: | |
| return "Unknown" | |
| 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 tasks_db.items(): | |
| status = task.get("status", "unknown") | |
| prompt_preview = task.get("prompt", "")[:50] | |
| created = task.get("created_at", "")[:19] | |
| # Status icon | |
| if status == "completed": | |
| icon = "✅" | |
| elif status in ["failed", "error"]: | |
| icon = "❌" | |
| else: | |
| icon = "⏳" | |
| output_lines.append(f"{icon} **{task_id}** - {status}") | |
| output_lines.append(f" Prompt: {prompt_preview}...") | |
| output_lines.append(f" Created: {created}") | |
| if task.get("last_checked"): | |
| last_checked = task["last_checked"][:19] | |
| output_lines.append(f" Last checked: {last_checked}") | |
| output_lines.append("") | |
| # Add summary | |
| completed = sum(1 for t in tasks_db.values() if t.get("status") == "completed") | |
| total = len(tasks_db) | |
| output_lines.append(f"**Summary:** {completed}/{total} tasks completed") | |
| return "\n".join(output_lines) | |
| def auto_refresh_status(task_id: str): | |
| """Auto-refresh status with loading animation""" | |
| if not task_id or task_id not in tasks_db: | |
| return task_id, "❌ Invalid Task ID" | |
| result = check_task_status(task_id) | |
| 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") | |
| 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 | |
| ) | |
| 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 | |
| • Specify if you want verses, chorus, bridge | |
| • Typical processing time: 10-30 seconds | |
| """) | |
| with gr.Column(scale=3): | |
| output_area = gr.Markdown( | |
| label="Result", | |
| value="Your task submission result will appear here..." | |
| ) | |
| submit_btn.click( | |
| fn=generate_lyrics, | |
| inputs=prompt_input, | |
| outputs=output_area | |
| ) | |
| # 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., a1b2c3d4)", | |
| scale=1 | |
| ) | |
| with gr.Row(): | |
| check_btn = gr.Button("🔍 Check Status", variant="primary") | |
| auto_refresh_btn = gr.Button("🔄 Auto-refresh", variant="secondary") | |
| 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" | |
| ) | |
| # Connect buttons to functions | |
| check_btn.click( | |
| fn=check_task_status, | |
| inputs=task_id_input, | |
| outputs=status_output | |
| ) | |
| auto_refresh_btn.click( | |
| fn=auto_refresh_status, | |
| inputs=task_id_input, | |
| outputs=[task_id_input, status_output] | |
| ).then( | |
| fn=lambda: time.sleep(3), | |
| inputs=None, | |
| outputs=None | |
| ).then( | |
| fn=auto_refresh_status, | |
| inputs=task_id_input, | |
| outputs=[task_id_input, status_output] | |
| ) | |
| 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 a lyrics prompt and submit | |
| 2. **Save your Task ID** from the result | |
| 3. **Check Status Tab:** Paste your Task ID to check progress | |
| 4. **Results:** Lyrics will appear when processing is complete | |
| """) | |
| with gr.Column(): | |
| gr.Markdown("### 🚨 Troubleshooting") | |
| gr.Markdown(""" | |
| **Common Issues:** | |
| • **"SunoKey not set":** Add your API key in Space Settings → Repository secrets | |
| • **"Task ID not found":** Submit a new task and save the ID | |
| • **Long processing time:** Suno API can take 10-30 seconds | |
| • **API errors:** Check if your API key is valid and has credits | |
| **For best results:** | |
| - Use descriptive prompts | |
| - Check status after 15-20 seconds | |
| - Save your Task IDs | |
| """) | |
| gr.Markdown("### 📞 Support") | |
| gr.Markdown(""" | |
| If you continue to have issues: | |
| 1. **Check your SunoKey** is correct and has available credits | |
| 2. **Try a simpler prompt** to test the API | |
| 3. **Wait a few minutes** if the API seems busy | |
| 4. **Contact Suno API support** for API-specific issues | |
| """) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| print(f"🔑 SunoKey status: {'Configured' if SUNO_KEY else 'NOT SET'}") | |
| print(f"📊 Tasks in memory: {len(tasks_db)}") | |
| print("🚀 Starting Suno Lyrics Generator...") | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| debug=False, | |
| show_error=True | |
| ) |