SUNO-API-2 / app.py
MySafeCode's picture
Update app.py
671e25d verified
raw
history blame
9.68 kB
import gradio as gr
import requests
import os
import time
import json
# Suno API key
SUNO_KEY = os.environ.get("SunoKey", "")
if not SUNO_KEY:
print("⚠️ SunoKey not set!")
def separate_vocals(task_id, audio_id, separation_type):
"""Separate vocals and instruments from Suno tracks"""
if not SUNO_KEY:
yield "❌ Error: SunoKey not configured in environment variables"
return
if not task_id.strip() or not audio_id.strip():
yield "❌ Error: Please enter both Task ID and Audio ID"
return
# Validate separation type
if separation_type not in ["separate_vocal", "split_stem"]:
yield "❌ Error: Invalid separation type"
return
# Submit separation task
try:
resp = requests.post(
"https://api.sunoapi.org/api/v1/vocal-removal/generate",
json={
"taskId": task_id,
"audioId": audio_id,
"type": separation_type,
"callBackUrl": "https://1hit.no/callback.php" # Required but not used for polling
},
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
separation_task_id = data["data"]["taskId"]
yield f"✅ **Submitted!**\nSeparation Task ID: `{separation_task_id}`\n\n⏳ Processing separation...\n"
# Poll for results
for attempt in range(40): # 40 attempts * 5 seconds = 200 seconds max
time.sleep(5)
try:
check = requests.get(
"https://api.sunoapi.org/api/v1/vocal-removal/record-info",
headers={"Authorization": f"Bearer {SUNO_KEY}"},
params={"taskId": separation_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 separation results
separation_info = check_data["data"].get("vocal_removal_info", {})
if not separation_info:
yield "✅ Completed but no separation results found"
return
# Format output based on separation type
output = "🎵 **Separation Complete!**\n\n"
if separation_type == "separate_vocal":
output += "## 2-Stem Separation\n\n"
output += f"**Vocals:** [Download]({separation_info.get('vocal_url', 'N/A')})\n"
output += f"**Instrumental:** [Download]({separation_info.get('instrumental_url', 'N/A')})\n"
if separation_info.get('origin_url'):
output += f"**Original:** [Download]({separation_info.get('origin_url')})\n"
elif separation_type == "split_stem":
output += "## 12-Stem Separation\n\n"
stems = [
("Vocals", separation_info.get('vocal_url')),
("Backing Vocals", separation_info.get('backing_vocals_url')),
("Drums", separation_info.get('drums_url')),
("Bass", separation_info.get('bass_url')),
("Guitar", separation_info.get('guitar_url')),
("Keyboard", separation_info.get('keyboard_url')),
("Strings", separation_info.get('strings_url')),
("Brass", separation_info.get('brass_url')),
("Woodwinds", separation_info.get('woodwinds_url')),
("Percussion", separation_info.get('percussion_url')),
("Synth", separation_info.get('synth_url')),
("FX/Other", separation_info.get('fx_url')),
("Instrumental", separation_info.get('instrumental_url')),
("Original", separation_info.get('origin_url'))
]
for stem_name, stem_url in stems:
if stem_url:
output += f"**{stem_name}:** [Download]({stem_url})\n"
output += f"\n⏱️ Processed in about {(attempt + 1) * 5} seconds\n"
output += f"⚠️ **Note:** Download links expire in 14 days"
yield output
return
elif status == "FAILED":
error = check_data["data"].get("errorMessage", "Unknown error")
yield f"❌ Separation failed: {error}"
return
else:
# PENDING or PROCESSING
yield (f"⏳ Status: {status}\n"
f"Attempt: {attempt + 1}/40\n"
f"Separation Task ID: `{separation_task_id}`\n\n"
f"Processing... (Usually takes 30-120 seconds)")
else:
yield f"⚠️ Check error: HTTP {check.status_code}"
except Exception as e:
yield f"⚠️ Error checking status: {str(e)}"
yield "⏰ Timeout after 200 seconds. Try checking again later."
except Exception as e:
yield f"❌ Error: {str(e)}"
# Create the app
with gr.Blocks(title="Suno Stem Separator", theme="soft") as app:
gr.Markdown("# 🎵 Suno Stem Separator")
gr.Markdown("Separate Suno AI tracks into vocal and instrument stems")
with gr.Row():
with gr.Column(scale=1):
task_id = gr.Textbox(
label="Original Task ID",
placeholder="Example: 5c79****be8e",
info="The task ID from your Suno music generation"
)
audio_id = gr.Textbox(
label="Audio ID",
placeholder="Example: e231****-****-****-****-****8cadc7dc",
info="The specific audio ID to separate"
)
separation_type = gr.Radio(
label="Separation Type",
choices=[
("separate_vocal (2 stems - 1 credit)", "separate_vocal"),
("split_stem (12 stems - 5 credits)", "split_stem")
],
value="separate_vocal",
info="Choose separation mode"
)
btn = gr.Button("🎵 Separate Stems", variant="primary", scale=1)
gr.Markdown("""
**How it works:**
1. Enter Task ID and Audio ID from your Suno track
2. Choose separation type
3. Click Separate Stems
4. Wait 30-120 seconds
5. Get download links for each stem
**Separation types:**
- 🎤 **separate_vocal**: Vocals + Instrumental (2 stems, 1 credit)
- 🎛️ **split_stem**: 12 detailed stems (5 credits)
**Stem Types in split_stem:**
- Vocals, Backing Vocals, Drums, Bass
- Guitar, Keyboard, Strings, Brass
- Woodwinds, Percussion, Synth, FX/Other
⚠️ **Important:**
- Each request consumes credits
- Links expire in 14 days
""")
with gr.Column(scale=2):
output = gr.Markdown(
label="Separation Results",
value="Your separated stems will appear here..."
)
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://docs.sunoapi.org/separate-vocals" target="_blank">Stem Separation Guide</a></p>
<p><small>This app uses the Suno API to separate tracks into individual stems.</small></p>
<p><small>💡 <strong>Tip:</strong> Find your Task ID and Audio ID in your Suno generation history or API responses.</small></p>
</div>
""",
elem_id="footer"
)
btn.click(separate_vocals, [task_id, audio_id, separation_type], output)
if __name__ == "__main__":
print("🚀 Starting Suno Stem Separator")
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)