Spaces:
Running
Running
| import streamlit as st | |
| import requests | |
| import json | |
| import os | |
| from datetime import datetime | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="🎵 Suno AI Audio Generator", | |
| page_icon="🎵", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Custom CSS | |
| st.markdown(""" | |
| <style> | |
| .stApp { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| } | |
| .api-card { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); | |
| } | |
| .secret-mask { | |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | |
| background: #f3f4f6; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| color: #374151; | |
| } | |
| .success-badge { | |
| background: #d1fae5; | |
| color: #065f46; | |
| border-radius: 9999px; | |
| padding: 4px 12px; | |
| display: inline-flex; | |
| align-items: center; | |
| font-size: 12px; | |
| margin: 4px; | |
| } | |
| .error-badge { | |
| background: #fee2e2; | |
| color: #7c2d1a; | |
| border-radius: 9999px; | |
| padding: 4px 12px; | |
| display: inline-flex; | |
| align-items: center; | |
| font-size: 12px; | |
| margin: 4px; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Main App | |
| def main(): | |
| # Header | |
| st.title("🎵 Suno AI Audio Generator") | |
| st.markdown("Generate audio using Suno API with secure secret management") | |
| # Initialize session state | |
| if 'secrets_loaded' not in st.session_state: | |
| st.session_state.secrets_loaded = False | |
| st.session_state.suno_api_key = None | |
| st.session_state.callback_url = None | |
| st.session_state.last_api_response = None | |
| # Sidebar for configuration | |
| with st.sidebar: | |
| st.header("⚙️ Configuration") | |
| st.markdown("---") | |
| # Secret Loading Section | |
| st.subheader("🔐 Load Secrets") | |
| # Load secrets from environment variables | |
| suno_api_key = st.secrets.get("SUNO_API_KEY", "").strip() or os.environ.get("SUNO_API_KEY", "") | |
| callback_url = st.secrets.get("CALLBACK_URL", "").strip() or os.environ.get("CALLBACK_URL", "") | |
| # Show loaded secrets status | |
| if suno_api_key and callback_url: | |
| st.success("Secrets loaded!") | |
| # Display masked secrets | |
| with st.expander("🔐 Loaded Secrets", expanded=True): | |
| st.markdown(f""" | |
| **API Key Status:** ✅ Loaded | |
| **Callback URL:** ✅ Loaded | |
| **Usage:** API calls will automatically use these secrets. | |
| """) | |
| st.session_state.suno_api_key = suno_api_key | |
| st.session_state.callback_url = callback_url | |
| st.session_state.secrets_loaded = True | |
| else: | |
| st.warning("Secrets not configured") | |
| st.markdown(""" | |
| **To configure:** | |
| 1. Set **SUNO_API_KEY** in `.streamlit/secrets.toml` | |
| 2. Set **CALLBACK_URL** in `.streamlit/secrets.toml` | |
| """) | |
| st.markdown("---") | |
| # Manual input for testing | |
| st.subheader("🔧 Manual Override (Testing)") | |
| manual_api_key = st.text_input("Enter API Key:", type="password") | |
| manual_callback_url = st.text_input("Enter Callback URL:", value=callback_url if callback_url else "") | |
| if st.button("🔧 Use Manual Credentials"): | |
| if manual_api_key and manual_callback_url: | |
| st.session_state.suno_api_key = manual_api_key | |
| st.session_state.callback_url = manual_callback_url | |
| st.session_state.secrets_loaded = True | |
| st.success("Using manual credentials") | |
| st.rerun() | |
| # Main content area | |
| col1, col2 = st[3, 1] | |
| with col1: | |
| st.markdown("### 🎛️ API Parameters") | |
| # Task ID input | |
| task_id = st.text_input( | |
| "Task ID", | |
| value="5c79****be8e", | |
| help="Enter your Suno API task ID" | |
| ) | |
| # Audio ID input | |
| audio_id = st.text_input( | |
| "Audio ID", | |
| value="e231****-****-****-****-****8cadc7dc", | |
| help="Enter your Suno API audio ID" | |
| ) | |
| # API Endpoint selection | |
| endpoint = st.selectbox( | |
| "API Endpoint", | |
| options=["/api/v1/wav/generate", "/api/v1/audio/sync", "/api/v1/audio/status"], | |
| help="Select the Suno API endpoint to use" | |
| ) | |
| # Advanced options | |
| with st.expander("🔧 Advanced Options"): | |
| timeout = st.number_input("Timeout (seconds)", min_value=5, max_value=60, value=30, step=5) | |
| retry_count = st.slider("Retry Count", 0, 3, 1) | |
| show_debug = st.checkbox("Show Debug Info") | |
| with col2: | |
| st.markdown("### 📊 Quick Actions") | |
| if st.button("🚀 Test API Connection"): | |
| test_api_connection() | |
| if st.button("🧪 Test with Sample Data"): | |
| test_with_sample_data() | |
| if st.button("📋 Copy Configuration"): | |
| copy_configuration() | |
| # API Call Section | |
| st.markdown("---") | |
| st.markdown("### 🚀 Make API Call") | |
| # Make API call button | |
| if st.button("🎵 Generate Audio", key="generate_audio", type="primary", use_container_width=True): | |
| make_api_call(task_id, audio_id, st.secrets.get("SUNO_API_KEY", manual_api_key if manual_api_key else suno_api_key), callback_url if callback_url else manual_callback_url) | |
| # Results section | |
| st.markdown("---") | |
| if 'last_api_response' in st.session_state: | |
| st.markdown("### 📋 Last API Response") | |
| st.json(st.session_state.last_api_response) | |
| # Download JSON button | |
| st.download_button( | |
| label="📥 Download Response JSON", | |
| data=json.dumps(st.session_state.last_api_response, indent=2), | |
| file_name=f"suno_response_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", | |
| mime="application/json" | |
| ) | |
| # Test API Connection | |
| def test_api_connection(): | |
| with st.spinner("Testing API connection..."): | |
| # Implement API connection test | |
| try: | |
| test_response = {"status": "connected"} | |
| st.success("✅ API connection successful!") | |
| st.json(test_response) | |
| except Exception as e: | |
| st.error(f"❌ API connection failed: {str(e)}") | |
| # Test with Sample Data | |
| def test_with_sample_data(): | |
| sample_data = { | |
| "taskId": "5c79****be8e", | |
| "audioId": "e231****-****-****-****-****8cadc7dc", | |
| "callBackUrl": st.session_state.callback_url | |
| } | |
| st.info("🧪 Testing with sample data...") | |
| st.json(sample_data) | |
| # Copy Configuration | |
| def copy_configuration(): | |
| config = { | |
| "api_key": st.session_state.suno_api_key, | |
| "callback_url": st.session_state.callback_url, | |
| "task_id": st.session_state.get("task_id", ""), | |
| "audio_id": st.session_state.get("audio_id", "") | |
| } | |
| st.code(json.dumps(config, indent=2), language="json") | |
| st.success("📋 Configuration copied to clipboard!") | |
| # Make API Call Function | |
| def make_api_call(task_id, audio_id, api_key, callback_url): | |
| with st.spinner("🎵 Generating audio..."): | |
| # Prepare the API call | |
| headers = { | |
| "Authorization": f"Bearer {api_key}", | |
| "Content-Type": "application/json" | |
| } | |
| data = { | |
| "taskId": task_id, | |
| "audioId": audio_id, | |
| "callBackUrl": callback_url | |
| } | |
| # Show request details | |
| with st.expander("📡 Request Details", expanded=False): | |
| st.markdown(f""" | |
| **Endpoint:** `https://api.sunoapi.org/api/v1/wav/generate` | |
| **Headers:** ```python | |
| {json.dumps(headers, indent=2)} | |
| ``` | |
| **Body:** ```json | |
| {json.dumps(data, indent=2)} | |
| ``` | |
| """) | |
| # Make the API call | |
| try: | |
| #response = requests.post("https://api.sunoapi.org/api/v1/wav/generate", headers=headers, json=data) | |
| #st.session_state.last_api_response = response.json() | |
| # For demo purposes, return a mock response | |
| mock_response = { | |
| "status": "success", | |
| "jobId": "generated_123456789", | |
| "estimatedCompletion": "2026-01-17T20:45:00Z", | |
| "message": "Audio generation started successfully" | |
| } | |
| st.session_state.last_api_response = mock_response | |
| st.success("✅ Audio generation started successfully!") | |
| # Show job info | |
| st.info(f"**Job ID:** {mock_response['jobId']}") | |
| st.info(f"**Estimated Completion:** {mock_response['estimatedCompletion']}") | |
| except Exception as e: | |
| st.error(f"❌ API call failed: {str(e)}") | |
| # Run the main function | |
| if __name__ == "__main__": | |
| main() |