import os import requests import gradio as gr from dotenv import load_dotenv import json import uuid import tempfile from datetime import datetime # Load environment variables from .env file load_dotenv() # Get Suno API key from environment variable (loaded from secrets) SUNO_API_KEY = os.environ.get("SunoKey") # Fixed callback URL FIXED_CALLBACK_URL = "https://1hit.no/cover/cb.php" # API endpoint SUNO_API_URL = "https://api.sunoapi.org/api/v1/generate/upload-cover" def generate_suno_music( prompt, title, style, upload_url_type, custom_upload_url, instrumental=True, model="V4_5ALL", persona_id="", negative_tags="", vocal_gender="m", style_weight=0.65, weirdness_constraint=0.65, audio_weight=0.65, custom_mode=True ): """ Generate music using Suno API with fixed callback URL """ # Check if API key is available if not SUNO_API_KEY: return f"Error: Suno API key not found. Please set the 'SunoKey' environment variable or secret." # Determine upload URL based on selection if upload_url_type == "auto": # Generate a temporary upload URL (simulated - in practice this would be your storage service) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") unique_id = str(uuid.uuid4())[:8] upload_url = f"https://storage.temp.example.com/uploads/{timestamp}_{unique_id}.mp3" else: # Use custom URL provided by user upload_url = custom_upload_url.strip() if not upload_url: return "Error: Please provide a custom upload URL or select 'Auto-generate temporary URL'" # Prepare payload with fixed callback URL payload = { "uploadUrl": upload_url, "customMode": custom_mode, "instrumental": instrumental, "model": model, "callBackUrl": FIXED_CALLBACK_URL, # Always use fixed callback URL "prompt": prompt, "style": style, "title": title, "personaId": persona_id, "negativeTags": negative_tags, "vocalGender": vocal_gender, "styleWeight": style_weight, "weirdnessConstraint": weirdness_constraint, "audioWeight": audio_weight } # Remove empty fields payload = {k: v for k, v in payload.items() if v not in ["", None]} # Prepare headers headers = { "Authorization": f"Bearer {SUNO_API_KEY}", "Content-Type": "application/json" } try: # Make API request response = requests.post(SUNO_API_URL, json=payload, headers=headers) # Check response status if response.status_code == 200: result = response.json() if result.get("code") == 200: task_id = result.get("data", {}).get("taskId", "Unknown") generated_url = upload_url if upload_url_type == "auto" else "Custom URL provided" return f"""✅ Success! Task ID: {task_id} Upload URL: {generated_url} Callback URL: {FIXED_CALLBACK_URL} 📋 Full API Response: {json.dumps(result, indent=2)}""" else: return f"""❌ API Error: {result.get('msg', 'Unknown error')} 📋 Full API Response: {json.dumps(result, indent=2)}""" else: return f"""❌ HTTP Error {response.status_code} Response: {response.text}""" except requests.exceptions.RequestException as e: return f"❌ Request failed: {str(e)}" except json.JSONDecodeError as e: return f"❌ Failed to parse response: {str(e)}\n\nResponse text: {response.text}" except Exception as e: return f"❌ Unexpected error: {str(e)}" # Create Gradio interface with gr.Blocks(title="Suno Music Generator") as app: gr.Markdown("# 🎵 Suno Music Generator") gr.Markdown(f"Generate music using Suno API. Callback URL is fixed to: `{FIXED_CALLBACK_URL}`") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### API Status") api_status = gr.Textbox( label="API Key Status", value=f"API Key: {'✅ Loaded' if SUNO_API_KEY else '❌ Not found'}" if SUNO_API_KEY else "❌ API Key not found. Please set 'SunoKey' environment variable.", interactive=False ) with gr.Column(scale=2): gr.Markdown("### About") gr.Markdown(f""" This app generates music using the Suno API. **Fixed Callback URL:** `{FIXED_CALLBACK_URL}` **Required setup:** 1. The basis for song generation is an mp3 url. 2. The setup here is weird. You click the automatic - not working button. 3. Then the other button. 4. Add URL. Typically from suno, udio or soundcloud download from other libraries. """) with gr.Tabs(): with gr.TabItem("Basic Settings"): with gr.Row(): with gr.Column(): prompt = gr.Textbox( label="Music Prompt", value="A calm and relaxing piano track with soft melodies", placeholder="Describe the music you want to generate...", lines=3 ) title = gr.Textbox( label="Title", value="Peaceful Piano Meditation", placeholder="Title for your music track" ) style = gr.Textbox( label="Style", value="Classical", placeholder="Music style (e.g., Classical, Pop, Rock, Jazz)" ) with gr.Column(): gr.Markdown("### Upload URL Settings") upload_url_type = gr.Radio( label="Upload URL Type", choices=[ ("Auto - not working", "auto"), ("Use custom URL", "custom") ], value="custom", info="Auto-generate creates a temporary URL, or provide your own" ) custom_upload_url = gr.Textbox( label="Custom Upload URL", value="https://storage.example.com/upload", placeholder="Enter your custom upload URL here", visible=False ) # Show/hide custom URL field based on radio selection def toggle_upload_url(selection): return gr.update(visible=selection == "custom") upload_url_type.change( toggle_upload_url, inputs=upload_url_type, outputs=custom_upload_url ) with gr.TabItem("Advanced Settings"): with gr.Row(): with gr.Column(): model = gr.Dropdown( label="Model", choices=["V5", "V4_5ALL", "V4", "V3", "V2"], value="V4_5ALL" ) instrumental = gr.Checkbox( label="Instrumental", value=True ) custom_mode = gr.Checkbox( label="Custom Mode", value=True ) with gr.Column(): persona_id = gr.Textbox( label="Persona ID (Optional)", value="persona_123", placeholder="Leave empty for no persona" ) negative_tags = gr.Textbox( label="Negative Tags (Optional)", value="Heavy Metal, Upbeat Drums", placeholder="Tags to avoid in the music" ) vocal_gender = gr.Dropdown( label="Vocal Gender", choices=["m", "f", "none"], value="m" ) with gr.TabItem("Weight Settings"): with gr.Row(): with gr.Column(): style_weight = gr.Slider( label="Style Weight", minimum=0.0, maximum=1.0, value=0.65, step=0.05 ) weirdness_constraint = gr.Slider( label="Weirdness Constraint", minimum=0.0, maximum=1.0, value=0.65, step=0.05 ) audio_weight = gr.Slider( label="Audio Weight", minimum=0.0, maximum=1.0, value=0.65, step=0.05 ) # Submit button submit_btn = gr.Button("🎶 Generate Music", variant="primary", size="lg") # Output output = gr.Textbox( label="Generation Result", lines=12, interactive=False ) # Example button example_btn = gr.Button("📋 Load Example", variant="secondary") # Define button actions def load_example(): return { prompt: "A calm and relaxing piano track with soft melodies", title: "Peaceful Piano Meditation", style: "Classical", upload_url_type: "auto", custom_upload_url: "https://storage.example.com/upload", model: "V4_5ALL", instrumental: True, custom_mode: True, persona_id: "persona_123", negative_tags: "Heavy Metal, Upbeat Drums", vocal_gender: "m", style_weight: 0.65, weirdness_constraint: 0.65, audio_weight: 0.65 } # Connect buttons example_btn.click( load_example, outputs=[ prompt, title, style, upload_url_type, custom_upload_url, model, instrumental, custom_mode, persona_id, negative_tags, vocal_gender, style_weight, weirdness_constraint, audio_weight ] ) submit_btn.click( generate_suno_music, inputs=[ prompt, title, style, upload_url_type, custom_upload_url, instrumental, model, persona_id, negative_tags, vocal_gender, style_weight, weirdness_constraint, audio_weight, custom_mode ], outputs=output ) # Footer gr.Markdown("---") # Launch the app if __name__ == "__main__": # Check if API key is available if not SUNO_API_KEY: print("⚠️ Warning: Suno API key not found.") print("Please set the 'SunoKey' environment variable:") print(" - For Hugging Face Spaces: Add as Repository Secret") print(" - For local development: Create a .env file with SunoKey=your_key") print(" - Or set it directly: export SunoKey=your_key") app.launch( server_name="0.0.0.0" if os.environ.get("GRADIO_SERVER_NAME") else None, server_port=int(os.environ.get("GRADIO_SERVER_PORT", 7860)), share=os.environ.get("GRADIO_SHARE", "False").lower() == "true", theme=gr.themes.Soft() )