SUNO-API-V5 / app.py
MySafeCode's picture
Update app.py
1b4d13d verified
raw
history blame
14.8 kB
import gradio as gr
import requests
import os
import json
# Suno API key
SUNO_KEY = os.environ.get("SunoKey", "")
if not SUNO_KEY:
print("⚠️ SunoKey not set!")
def submit_song_generation(lyrics_text, style, title, instrumental, model):
"""Submit song generation request - IMMEDIATE RESPONSE WITH TASK ID"""
if not SUNO_KEY:
return "❌ Error: SunoKey not configured", ""
if not lyrics_text.strip() and not instrumental:
return "❌ Error: Please provide lyrics or select instrumental", ""
if not style.strip():
return "❌ Error: Please provide a music style", ""
if not title.strip():
return "❌ Error: Please provide a song title", ""
try:
# Prepare request data
request_data = {
"customMode": True,
"instrumental": instrumental,
"model": model,
"callBackUrl": "https://1hit.no/callback.php",
"style": style,
"title": title,
}
if not instrumental:
# Apply character limits
if model == "V4" and len(lyrics_text) > 3000:
lyrics_text = lyrics_text[:3000]
elif model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"] and len(lyrics_text) > 5000:
lyrics_text = lyrics_text[:5000]
request_data["prompt"] = lyrics_text
else:
request_data["prompt"] = ""
# Submit generation request
resp = requests.post(
"https://api.sunoapi.org/api/v1/generate",
json=request_data,
headers={
"Authorization": f"Bearer {SUNO_KEY}",
"Content-Type": "application/json"
},
timeout=30
)
if resp.status_code != 200:
return f"❌ Submission failed: HTTP {resp.status_code}\n\n{resp.text}", ""
data = resp.json()
# Extract task ID - IMMEDIATELY
task_id = None
# Try different response formats
if "taskId" in data:
task_id = data["taskId"]
elif "data" in data and "taskId" in data["data"]:
task_id = data["data"]["taskId"]
elif data.get("data") and "taskId" in data.get("data", {}):
task_id = data["data"]["taskId"]
if not task_id:
return f"❌ Could not extract Task ID\n\n**Raw:**\n```json\n{json.dumps(data, indent=2)}\n```", ""
# RETURN TASK ID IMMEDIATELY
output = f"## ✅ TASK SUBMITTED!\n\n"
output += f"**🎯 YOUR TASK ID:** `{task_id}`\n\n"
output += f"**📋 Details:**\n"
output += f"- **Title:** {title}\n"
output += f"- **Style:** {style}\n"
output += f"- **Model:** {model}\n"
output += f"- **Instrumental:** {'Yes' if instrumental else 'No'}\n\n"
output += f"**🚀 Next Steps:**\n"
output += f"1. **Copy this Task ID:** `{task_id}`\n"
output += f"2. **Switch to 'Check Status' tab**\n"
output += f"3. **Paste the Task ID**\n"
output += f"4. **Click 'Check Status'** for results\n\n"
output += f"**⏱️ Processing time:** 1-3 minutes\n"
output += f"**📞 Callback URL:** https://1hit.no/callback.php\n"
return output, task_id
except Exception as e:
return f"❌ Error: {str(e)}", ""
def check_song_status(task_id, show_raw=False):
"""Check song generation status - RETURNS DOWNLOAD LINKS"""
if not task_id:
return "❌ Please enter a Task ID", ""
try:
resp = requests.get(
"https://api.sunoapi.org/api/v1/generate/record-info",
headers={"Authorization": f"Bearer {SUNO_KEY}"},
params={"taskId": task_id},
timeout=30
)
if resp.status_code != 200:
return f"❌ HTTP Error {resp.status_code}\n\n{resp.text}", ""
data = resp.json()
# RAW JSON for debugging (always stored, shown if requested)
raw_json = f"```json\n{json.dumps(data, indent=2)}\n```"
if data.get("code") != 200:
return f"❌ API Error: {data.get('msg', 'Unknown')}", raw_json
task_data = data.get("data", {})
status = task_data.get("status", "UNKNOWN")
# Format main output
output = f"## 🔍 Status Check: `{task_id}`\n\n"
output += f"**Status:** {status}\n"
if status == "TEXT_SUCCESS":
# This is the key status! Songs are ready!
response_data = task_data.get("response", {})
songs = response_data.get("sunoData", [])
if not songs:
output += f"\n**⚠️ Status TEXT_SUCCESS but no songs found**\n"
output += f"Raw data available in dropdown below\n"
return output, raw_json
output += f"## 🎵 SONGS READY! ({len(songs)} tracks)\n\n"
for i, song in enumerate(songs, 1):
output += f"### Track {i}\n"
output += f"**Title:** {song.get('title', 'Untitled')}\n"
# Get all possible URLs
audio_url = song.get('audioUrl')
stream_url = song.get('streamAudioUrl') or song.get('streamAudioURL') or song.get('stream_url')
source_stream = song.get('sourceStreamAudioUrl')
source_audio = song.get('sourceAudioUrl')
# Image URL
image_url = song.get('imageUrl') or song.get('sourceImageUrl')
# Best URL for listening
best_url = audio_url or stream_url or source_stream or source_audio
if best_url:
output += f"**🎧 Listen Now:** [Click to Play]({best_url})\n"
# Audio player
output += f"""<audio controls style="width: 100%; margin: 10px 0;">
<source src="{best_url}" type="audio/mpeg">
Your browser does not support audio.
</audio>\n"""
# Download button
if audio_url:
output += f"**💾 Download:** [MP3 File]({audio_url})\n"
elif stream_url:
output += f"**💾 Download:** [Stream MP3]({stream_url})\n"
if image_url:
output += f"**🖼️ Cover Art:** [View Image]({image_url})\n"
output += f"**ID:** `{song.get('id', 'N/A')}`\n"
output += f"**Model:** {song.get('modelName', 'N/A')}\n"
output += f"**Tags:** {song.get('tags', 'N/A')}\n"
if song.get('duration'):
output += f"**Duration:** {song.get('duration')}s\n"
# Show first 200 chars of prompt
prompt = song.get('prompt', '')
if prompt:
output += f"**Preview:** {prompt[:200]}...\n"
output += "\n---\n\n"
# Summary
output += f"**✅ Generation complete!**\n\n"
output += "**To save your songs:**\n"
output += "1. Right-click 'Listen Now' links → 'Save link as...'\n"
output += "2. Or use the download links above\n"
output += "3. Note the Track IDs for reference\n"
elif status in ["PENDING", "PROCESSING", "RUNNING"]:
output += f"\n**⏳ Song is still being generated...**\n"
output += "Check again in 30-60 seconds\n"
output += f"This usually takes 1-3 minutes total\n"
elif status == "SUCCESS":
output += f"\n**✅ Generation succeeded!**\n"
output += "Check raw data below for song URLs\n"
elif status == "FAILED":
error_msg = task_data.get("errorMessage", "Unknown error")
output += f"\n**❌ Generation failed:** {error_msg}\n"
else:
output += f"\n**⚠️ Unknown status:** {status}\n"
output += "Check raw data below\n"
# Always show premium info
output += f"\n**💡 Tip:** Songs may take 2-3 minutes to fully process\n"
output += f"**📞 Callback sent to:** https://1hit.no/callback.php\n"
output += f"**🔗 Viewer:** [https://1hit.no/viewer.php?task_id={task_id}](https://1hit.no/viewer.php?task_id={task_id})\n"
return output, raw_json
except Exception as e:
return f"❌ Error checking task: {str(e)}", ""
# Create the app
with gr.Blocks() as app:
gr.Markdown("# 🎵 Suno Song Generator")
# Store current task ID
current_task_id = gr.State(value="")
with gr.Tab("🎶 Generate Song"):
with gr.Row():
with gr.Column(scale=1):
# Inputs
lyrics_text = gr.Textbox(
label="Lyrics (leave empty for instrumental)",
placeholder="[Verse 1]\nType your lyrics here...",
lines=8
)
style = gr.Textbox(
label="Music Style",
value="Pop",
placeholder="e.g., Rock, Jazz, Electronic"
)
title = gr.Textbox(
label="Song Title",
value="My Song"
)
with gr.Row():
instrumental = gr.Checkbox(label="Instrumental Only")
model = gr.Dropdown(
choices=["V5", "V4_5PLUS", "V4_5ALL", "V4_5", "V4"],
value="V4_5ALL",
label="Model"
)
submit_btn = gr.Button("🚀 Generate Song", variant="primary")
gr.Markdown("""
**Instructions:**
1. Enter lyrics (or blank for instrumental)
2. Set style & title
3. Click Generate
4. **Copy the Task ID**
5. Check status in next tab
""")
with gr.Column(scale=2):
# Submission output
submission_output = gr.Markdown(
value="### Ready to generate!\n\nFill in the form and click 'Generate Song'"
)
with gr.Tab("🔍 Check Status"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Check Song Status")
# Task ID input (auto-fills from generation)
status_task_id = gr.Textbox(
label="Task ID",
placeholder="Paste your Task ID here",
info="Will auto-fill from generated task"
)
check_btn = gr.Button("🔍 Check Status Now", variant="primary")
auto_check = gr.Checkbox(label="Auto-check every 30s", value=False)
gr.Markdown("""
**What to expect:**
- **TEXT_SUCCESS**: Songs ready! Listen/download
- **PENDING/PROCESSING**: Still generating
- Check every 30s until ready
""")
with gr.Column(scale=2):
# Status output
status_output = gr.Markdown(
value="### Enter a Task ID to check status"
)
# Raw data in accordion
with gr.Accordion("📊 Raw API Response (Debug)", open=False):
raw_output = gr.Markdown(
value="*Raw JSON will appear here when you check status*"
)
with gr.Tab("📋 Instructions"):
gr.Markdown("""
## 🎵 Complete Workflow
### 1. Generate Song
- Enter lyrics & settings
- Click **Generate Song**
- **IMMEDIATELY get a Task ID**
- Copy the Task ID
### 2. Check Status
- Switch to **Check Status** tab
- Task ID auto-fills
- Click **Check Status Now**
- Wait for **TEXT_SUCCESS** status
### 3. Download Songs
- When **TEXT_SUCCESS** appears:
- **Listen Now** links with audio players
- **Download** links for MP3 files
- Cover art images
- Track IDs for reference
### 4. Tips
- Processing: **1-3 minutes**
- Check every **30 seconds**
- Use **Auto-check** for convenience
- Raw data available for debugging
### 5. Status Meanings
- **TEXT_SUCCESS**: ✅ Songs ready!
- **PENDING/PROCESSING**: ⏳ Still working
- **SUCCESS**: Check raw data
- **FAILED**: Try again
### 6. Callback System
- Results also sent to: https://1hit.no/callback.php
- View all results: https://1hit.no/viewer.php
""")
# Auto-fill status tab with task ID
def update_status_field(task_id):
return task_id
current_task_id.change(
fn=update_status_field,
inputs=[current_task_id],
outputs=[status_task_id]
)
# Generate song
def on_generate(lyrics, style, title, instrumental, model):
output, task_id = submit_song_generation(lyrics, style, title, instrumental, model)
if task_id:
return output, task_id, task_id # Also updates status_task_id
return output, "", "" # Clear if error
submit_btn.click(
fn=on_generate,
inputs=[lyrics_text, style, title, instrumental, model],
outputs=[submission_output, current_task_id, status_task_id]
)
# Check status
def on_check(task_id, show_raw):
output, raw = check_song_status(task_id, show_raw)
return output, raw
check_btn.click(
fn=on_check,
inputs=[status_task_id, gr.State(True)], # Always show raw
outputs=[status_output, raw_output]
)
if __name__ == "__main__":
print("🚀 Starting Suno Song Generator")
print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
print("🌐 Open your browser to: http://localhost:7860")
app.launch(server_name="0.0.0.0", server_port=7860, share=False)