SUNO-API-V5 / app.py
MySafeCode's picture
Update app.py
f2cdd25 verified
raw
history blame
18.1 kB
import gradio as gr
import requests
import os
import time
import json
import tempfile
# Suno API key
SUNO_KEY = os.environ.get("SunoKey", "")
if not SUNO_KEY:
print("⚠️ SunoKey not set!")
def generate_lyrics(prompt):
"""Final working lyrics generator"""
if not SUNO_KEY:
yield "❌ Error: SunoKey not configured in environment variables"
return
if not prompt.strip():
yield "❌ Error: Please enter a prompt"
return
# Submit task
try:
resp = requests.post(
"https://api.sunoapi.org/api/v1/lyrics",
json={
"prompt": prompt,
"callBackUrl": "http://dummy.com/callback" # Required but not used
},
headers={
"Authorization": f"Bearer {SUNO_KEY}",
"Content-Type": "application/json"
},
timeout=30
)
if resp.status_code != 200:
yield f"❌ Submission failed: HTTP {resp.status_code}"
return
data = resp.json()
if data.get("code") != 200:
yield f"❌ API error: {data.get('msg', 'Unknown')}"
return
task_id = data["data"]["taskId"]
yield f"✅ **Submitted!**\nTask ID: `{task_id}`\n\n⏳ Waiting for lyrics...\n"
# Poll for results
for attempt in range(30): # 30 attempts * 5 seconds = 150 seconds max
time.sleep(5)
try:
check = requests.get(
"https://api.sunoapi.org/api/v1/lyrics/record-info",
headers={"Authorization": f"Bearer {SUNO_KEY}"},
params={"taskId": task_id},
timeout=30
)
if check.status_code == 200:
check_data = check.json()
status = check_data["data"].get("status", "PENDING")
if status == "SUCCESS":
# Success! Extract lyrics
response_data = check_data["data"].get("response", {})
if isinstance(response_data, str):
# Sometimes response is a JSON string
try:
response_data = json.loads(response_data.replace('null', 'None'))
except:
response_data = {"data": []}
lyrics_list = response_data.get("data", [])
if lyrics_list:
output = "🎵 **Lyrics Generated Successfully!**\n\n"
for i, lyric in enumerate(lyrics_list, 1):
title = lyric.get('title', f'Variant {i}')
text = lyric.get('text', 'No lyrics')
output += f"## Variant {i}: {title}\n"
output += "```\n"
output += text
output += "\n```\n"
output += "---\n\n"
output += f"⏱️ Generated in about {(attempt + 1) * 5} seconds\n\n"
output += "📝 **Now you can:**\n"
output += "1. Select a variant below\n"
output += "2. Edit the text if needed\n"
output += "3. Generate a song!"
# Prepare data for state
lyrics_data = {
"variants": lyrics_list,
"original_prompt": prompt
}
# Update dropdown with variants
variant_choices = [f"{i+1}: {lyric.get('title', f'Variant {i+1}')}"
for i, lyric in enumerate(lyrics_list)]
# Get first variant text for editing
first_text = lyrics_list[0].get('text', '') if lyrics_list else ""
yield output, lyrics_data, variant_choices, first_text
else:
yield "✅ Completed but no lyrics found in response", None, [], ""
return
elif status == "FAILED":
error = check_data["data"].get("errorMessage", "Unknown error")
yield f"❌ Task failed: {error}", None, [], ""
return
else:
# PENDING or PROCESSING
yield (f"⏳ Status: {status}\n"
f"Attempt: {attempt + 1}/30\n"
f"Task ID: `{task_id}`\n\n"
f"Still processing... (Usually takes 30-90 seconds)"), None, [], ""
else:
yield f"⚠️ Check error: HTTP {check.status_code}", None, [], ""
except Exception as e:
yield f"⚠️ Error checking status: {str(e)}", None, [], ""
yield "⏰ Timeout after 150 seconds. Try checking again later.", None, [], ""
except Exception as e:
yield f"❌ Error: {str(e)}", None, [], []
def generate_song_from_text(lyrics_text, style, title, instrumental, model, custom_mode):
"""Generate a song from text"""
if not SUNO_KEY:
return "❌ Error: SunoKey not configured in environment variables"
if not lyrics_text.strip():
return "❌ Error: Please provide lyrics text"
try:
# Prepare request data based on custom mode
request_data = {
"customMode": custom_mode,
"instrumental": instrumental,
"model": model,
"callBackUrl": "http://dummy.com/callback", # Required but not used
}
if custom_mode:
# Custom mode requires style and title
if not style.strip():
return "❌ Error: Style is required in Custom Mode"
if not title.strip():
return "❌ Error: Title is required in Custom Mode"
request_data["style"] = style
request_data["title"] = title
if not instrumental:
# Non-instrumental requires lyrics as prompt
if len(lyrics_text) > 5000 and model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"]:
lyrics_text = lyrics_text[:5000]
elif len(lyrics_text) > 3000 and model == "V4":
lyrics_text = lyrics_text[:3000]
request_data["prompt"] = lyrics_text
else:
# Non-custom mode only requires prompt (max 500 chars)
if len(lyrics_text) > 500:
lyrics_text = lyrics_text[:497] + "..."
request_data["prompt"] = lyrics_text
# Clear other fields for non-custom mode
request_data["style"] = ""
request_data["title"] = ""
# 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}"
data = resp.json()
if data.get("code") != 200:
return f"❌ API error: {data.get('msg', 'Unknown')}"
task_id = data["data"]["taskId"]
# Poll for results
for attempt in range(60): # 60 attempts * 5 seconds = 300 seconds max (5 minutes)
time.sleep(5)
try:
check = requests.get(
"https://api.sunoapi.org/api/v1/music-info",
headers={"Authorization": f"Bearer {SUNO_KEY}"},
params={"taskId": task_id},
timeout=30
)
if check.status_code == 200:
check_data = check.json()
status = check_data["data"].get("status", "PENDING")
if status == "COMPLETE":
# Success! Extract song URLs
songs = check_data["data"].get("songs", [])
if songs:
output = "🎶 **Song Generation Complete!**\n\n"
for i, song in enumerate(songs, 1):
song_title = song.get('title', f'Song {i}')
stream_url = song.get('streamUrl', 'No URL')
download_url = song.get('downloadUrl', 'No URL')
output += f"## Song {i}: {song_title}\n"
output += f"**Stream URL:** {stream_url}\n\n"
output += f"**Download URL:** {download_url}\n\n"
output += f"**Listen Now:** [Click to Stream]({stream_url})\n\n"
output += "---\n\n"
output += f"⏱️ Generated in about {(attempt + 1) * 5} seconds\n\n"
output += "⚠️ **Note:** Files are retained for 15 days"
return output
else:
return "✅ Completed but no songs found in response"
elif status == "FAILED":
error = check_data["data"].get("errorMessage", "Unknown error")
return f"❌ Task failed: {error}"
else:
# Still processing
if attempt % 6 == 0: # Update every 30 seconds
yield f"⏳ Status: {status}\nAttempt: {attempt + 1}/60\nTask ID: `{task_id}`\n\nStill processing... (Usually takes 30-180 seconds)"
else:
yield f"⚠️ Check error: HTTP {check.status_code}"
except Exception as e:
yield f"⚠️ Error checking status: {str(e)}"
return "⏰ Timeout after 300 seconds. Try checking again later."
except Exception as e:
return f"❌ Error: {str(e)}"
def update_text_from_variant(lyrics_data, variant_choice):
"""Update the text area when a variant is selected"""
if lyrics_data and lyrics_data.get("variants") and variant_choice:
try:
# Extract variant index from choice (e.g., "1: Title" -> 0)
variant_idx = int(variant_choice.split(":")[0].strip()) - 1
variants = lyrics_data["variants"]
if 0 <= variant_idx < len(variants):
text = variants[variant_idx].get('text', '')
return text
except:
pass
return ""
def download_text_file(text):
"""Create a downloadable text file"""
if not text.strip():
return None
# Create a temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
f.write(text)
temp_path = f.name
return temp_path
def upload_text_file(file):
"""Read text from uploaded file"""
if file is None:
return ""
try:
with open(file.name, 'r', encoding='utf-8') as f:
content = f.read()
return content
except:
try:
with open(file.name, 'r', encoding='latin-1') as f:
content = f.read()
return content
except Exception as e:
return f"❌ Error reading file: {str(e)}"
# Create the app
with gr.Blocks(title="Suno Lyrics Generator", theme="soft") as app:
gr.Markdown("# 🎵 Suno Lyrics Generator")
gr.Markdown("Generate song lyrics and turn them into full songs using Suno AI")
# Store lyrics data between steps
lyrics_state = gr.State()
with gr.Row():
with gr.Column(scale=1):
# Lyrics Generation Section
gr.Markdown("### Step 1: Generate or Upload Lyrics")
with gr.Tab("Generate Lyrics"):
prompt = gr.Textbox(
label="Lyrics Prompt",
placeholder="Example: A happy song about sunshine and rainbows",
lines=3
)
lyrics_btn = gr.Button("🎵 Generate Lyrics", variant="primary")
with gr.Tab("Upload Lyrics"):
file_upload = gr.File(
label="Upload Text File",
file_types=[".txt"],
type="filepath"
)
upload_btn = gr.Button("📁 Load from File", variant="secondary")
upload_text = gr.Textbox(
label="Uploaded Text",
lines=10,
interactive=False
)
# Lyrics Selection & Editing Section
gr.Markdown("### Step 2: Select & Edit Lyrics")
variant_choice = gr.Dropdown(
label="Select Lyrics Variant",
choices=[],
interactive=True
)
lyrics_text = gr.Textbox(
label="Lyrics Text (Editable)",
placeholder="Your lyrics will appear here...",
lines=15,
interactive=True
)
with gr.Row():
download_btn = gr.Button("💾 Download Text", variant="secondary")
clear_btn = gr.Button("🗑️ Clear", variant="secondary")
# Song Generation Section
gr.Markdown("### Step 3: Create Song")
with gr.Row():
custom_mode = gr.Checkbox(
label="Custom Mode",
value=False,
interactive=True
)
instrumental = gr.Checkbox(
label="Instrumental",
value=False,
interactive=True
)
with gr.Row():
style = gr.Textbox(
label="Music Style",
placeholder="Pop, Rock, Jazz, Classical",
interactive=True,
value="Pop"
)
title = gr.Textbox(
label="Song Title",
placeholder="My Awesome Song",
interactive=True,
value="Generated Song"
)
model = gr.Dropdown(
label="Model Version",
choices=["V5", "V4_5PLUS", "V4_5ALL", "V4_5", "V4"],
value="V4_5ALL",
interactive=True
)
song_btn = gr.Button("🎶 Generate Song", variant="primary")
with gr.Column(scale=2):
output = gr.Markdown(
label="Results",
value="Your generated lyrics and songs will appear here..."
)
# Hidden file download component
file_output = gr.File(label="Download Lyrics", visible=False)
gr.Markdown("---")
gr.Markdown(
"""
<div style="text-align: center; padding: 20px;">
<p>Powered by <a href="https://suno.ai" target="_blank">Suno AI</a> •
<a href="https://sunoapi.org" target="_blank">Suno API Docs</a> •
<a href="https://github.com" target="_blank">GitHub</a></p>
<p><small>This app uses the Suno API to generate AI-powered lyrics and music.</small></p>
</div>
""",
elem_id="footer"
)
# Event handlers
# Generate lyrics from prompt
lyrics_btn.click(
generate_lyrics,
inputs=prompt,
outputs=[output, lyrics_state, variant_choice, lyrics_text]
)
# Upload text from file
upload_btn.click(
upload_text_file,
inputs=file_upload,
outputs=lyrics_text
).then(
lambda: "📁 **Text loaded from file!**\n\nYou can now edit the text above and generate a song.",
outputs=output
)
# Update text when variant is selected
variant_choice.change(
update_text_from_variant,
inputs=[lyrics_state, variant_choice],
outputs=lyrics_text
)
# Download text as file
download_btn.click(
download_text_file,
inputs=lyrics_text,
outputs=file_output
).then(
lambda: "💾 **Text ready for download!**\n\nCheck the download button below.",
outputs=output
)
# Clear text
clear_btn.click(
lambda: ("", ""),
outputs=[lyrics_text, output]
)
# Generate song from text
song_btn.click(
generate_song_from_text,
inputs=[lyrics_text, style, title, instrumental, model, custom_mode],
outputs=output
)
if __name__ == "__main__":
print("🚀 Starting Suno Lyrics Generator")
print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
app.launch(server_name="0.0.0.0", server_port=7860, share=False)