MySafeCode commited on
Commit
c2e4a34
·
verified ·
1 Parent(s): 3efa312

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +356 -0
app.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import json
3
+ import requests
4
+ import time
5
+ from datetime import datetime
6
+
7
+ st.set_page_config(
8
+ page_title="Suno API Generator",
9
+ page_icon="🎵",
10
+ layout="wide"
11
+ )
12
+
13
+ # Load YOUR secret names
14
+ SUNO_API_KEY = st.secrets.get("SunoKey", "")
15
+ CALLBACK_URL = "https://1hit.no/wav/cb.php"
16
+
17
+ # Initialize session state
18
+ if 'task_id' not in st.session_state:
19
+ st.session_state.task_id = None
20
+ if 'polling' not in st.session_state:
21
+ st.session_state.polling = False
22
+ if 'poll_results' not in st.session_state:
23
+ st.session_state.poll_results = []
24
+ if 'auto_poll' not in st.session_state:
25
+ st.session_state.auto_poll = False
26
+
27
+ st.title("🎵 Suno API Audio Generator")
28
+
29
+ # Info section
30
+ with st.expander("📋 How it works", expanded=True):
31
+ st.markdown("""
32
+ ### Workflow:
33
+ 1. **Step 1**: Submit task to Suno API → Get `taskId`
34
+ 2. **Step 2**: Suno processes audio (async)
35
+ 3. **Step 3**: Suno sends callback to: `https://1hit.no/wav/cb.php`
36
+ 4. **Step 4**: You can view callbacks at: `https://1hit.no/wav/view.php`
37
+ 5. **Step 5**: OR use Poll button to check status via `/api/v1/wav/record-info`
38
+
39
+ ### Polling Endpoint:
40
+ ```javascript
41
+ fetch('https://api.sunoapi.org/api/v1/wav/record-info', {
42
+ method: 'GET',
43
+ headers: {
44
+ 'Authorization': 'Bearer YOUR_TOKEN'
45
+ }
46
+ })
47
+ ```
48
+ """)
49
+
50
+ col1, col2 = st.columns([2, 1])
51
+
52
+ with col1:
53
+ st.header("🚀 Step 1: Submit to Suno API")
54
+
55
+ # Form inputs
56
+ task_id = st.text_input("Task ID", value="5c79****be8e", help="Enter your Suno task ID")
57
+ audio_id = st.text_input("Audio ID", value="e231****-****-****-****-****8cadc7dc", help="Enter your Suno audio ID")
58
+
59
+ if st.button("🎵 Generate Audio", type="primary", use_container_width=True):
60
+ if not SUNO_API_KEY:
61
+ st.error("❌ SunoKey not configured in Hugging Face secrets")
62
+ st.info("Add `SunoKey` secret in Hugging Face Space settings")
63
+ st.stop()
64
+
65
+ if not task_id or not audio_id:
66
+ st.error("❌ Please enter both Task ID and Audio ID")
67
+ st.stop()
68
+
69
+ with st.spinner("Sending request to Suno API..."):
70
+ headers = {
71
+ "Authorization": f"Bearer {SUNO_API_KEY}",
72
+ "Content-Type": "application/json"
73
+ }
74
+
75
+ payload = {
76
+ "taskId": task_id,
77
+ "audioId": audio_id,
78
+ "callBackUrl": CALLBACK_URL
79
+ }
80
+
81
+ try:
82
+ response = requests.post(
83
+ "https://api.sunoapi.org/api/v1/wav/generate",
84
+ headers=headers,
85
+ json=payload,
86
+ timeout=30
87
+ )
88
+
89
+ if response.status_code == 200:
90
+ result = response.json()
91
+
92
+ # Display response
93
+ with st.expander("📋 API Response", expanded=True):
94
+ st.json(result)
95
+
96
+ if result.get("code") == 200 and "data" in result and "taskId" in result["data"]:
97
+ st.session_state.task_id = result["data"]["taskId"]
98
+ st.success(f"✅ Task submitted successfully!")
99
+ st.info(f"**Task ID:** `{st.session_state.task_id}`")
100
+
101
+ # Auto-fill poll input
102
+ st.session_state.poll_task_input = st.session_state.task_id
103
+
104
+ st.markdown("---")
105
+ st.markdown("### 🔄 Next Steps:")
106
+ st.markdown("""
107
+ 1. **Automatic Callback**: Suno will send result to your callback URL
108
+ 2. **Manual Poll**: Use the Poll button to check status
109
+ 3. **View Logs**: Check [Callback Viewer](https://1hit.no/wav/view.php)
110
+ """)
111
+ else:
112
+ st.error("❌ Unexpected response format")
113
+ else:
114
+ st.error(f"❌ API Error: {response.status_code}")
115
+ try:
116
+ error_data = response.json()
117
+ st.json(error_data)
118
+ except:
119
+ st.text(f"Response: {response.text}")
120
+
121
+ except requests.exceptions.Timeout:
122
+ st.error("⏰ Request timed out after 30 seconds")
123
+ except requests.exceptions.ConnectionError:
124
+ st.error("🔌 Connection error - check your internet connection")
125
+ except requests.exceptions.RequestException as e:
126
+ st.error(f"⚠️ Request failed: {str(e)}")
127
+
128
+ with col2:
129
+ st.header("🔍 Step 2: Poll Status")
130
+
131
+ # Manual task ID input for polling
132
+ poll_task_id = st.text_input(
133
+ "Task ID to Poll",
134
+ value=st.session_state.get('poll_task_input', st.session_state.task_id or ""),
135
+ help="Enter task ID to check status",
136
+ key="poll_task_input"
137
+ )
138
+
139
+ col2a, col2b = st.columns(2)
140
+
141
+ with col2a:
142
+ if st.button("🔄 Poll Once", type="secondary", use_container_width=True):
143
+ if not poll_task_id:
144
+ st.error("❌ Please enter a Task ID")
145
+ st.stop()
146
+
147
+ if not SUNO_API_KEY:
148
+ st.error("❌ SunoKey not configured")
149
+ st.stop()
150
+
151
+ # Single poll
152
+ poll_status(poll_task_id, single_poll=True)
153
+
154
+ with col2b:
155
+ if st.button("🔁 Auto Poll (10s)", type="secondary", use_container_width=True):
156
+ if not poll_task_id:
157
+ st.error("❌ Please enter a Task ID")
158
+ st.stop()
159
+
160
+ if not SUNO_API_KEY:
161
+ st.error("❌ SunoKey not configured")
162
+ st.stop()
163
+
164
+ # Start auto-polling
165
+ st.session_state.auto_poll = True
166
+ st.session_state.auto_poll_task_id = poll_task_id
167
+ st.rerun()
168
+
169
+ # Polling function
170
+ def poll_status(task_id, single_poll=False):
171
+ """Poll Suno API for task status using /api/v1/wav/record-info"""
172
+ headers = {
173
+ "Authorization": f"Bearer {SUNO_API_KEY}",
174
+ "Content-Type": "application/json"
175
+ }
176
+
177
+ try:
178
+ with st.spinner(f"Checking status for task: {task_id}..."):
179
+ # Poll using the correct endpoint
180
+ response = requests.get(
181
+ f"https://api.sunoapi.org/api/v1/wav/record-info",
182
+ headers=headers,
183
+ timeout=30
184
+ )
185
+
186
+ if response.status_code == 200:
187
+ result = response.json()
188
+
189
+ # Add to poll results
190
+ poll_entry = {
191
+ "timestamp": datetime.now().strftime("%H:%M:%S"),
192
+ "task_id": task_id,
193
+ "response": result
194
+ }
195
+ st.session_state.poll_results.append(poll_entry)
196
+
197
+ # Display result
198
+ if result.get("code") == 200:
199
+ data = result.get("data", {})
200
+
201
+ if data.get("successFlag") == "SUCCESS":
202
+ # Audio is ready!
203
+ audio_url = data.get("response", {}).get("audioWavUrl")
204
+ if audio_url:
205
+ st.success("✅ Audio ready!")
206
+ st.balloons()
207
+
208
+ # Display audio info
209
+ st.markdown("### 🎵 Audio Information")
210
+ cols = st.columns(2)
211
+ with cols[0]:
212
+ st.metric("Task ID", data.get("taskId", "N/A")[:12] + "...")
213
+ st.metric("Music ID", data.get("musicId", "N/A")[:12] + "...")
214
+ with cols[1]:
215
+ st.metric("Status", data.get("successFlag", "N/A"))
216
+ st.metric("Created", data.get("createTime", "N/A"))
217
+
218
+ # Audio player
219
+ st.markdown("### 🔊 Listen")
220
+ st.audio(audio_url, format="audio/wav")
221
+
222
+ # Download section
223
+ st.markdown("### 📥 Download")
224
+ st.code(audio_url)
225
+
226
+ col_dl1, col_dl2 = st.columns(2)
227
+ with col_dl1:
228
+ st.download_button(
229
+ label="⬇ Download WAV",
230
+ data=requests.get(audio_url).content if audio_url.startswith("http") else b"",
231
+ file_name=f"audio_{data.get('taskId', 'unknown')[:8]}.wav",
232
+ mime="audio/wav",
233
+ key=f"download_{task_id}"
234
+ )
235
+ with col_dl2:
236
+ if st.button("📋 Copy URL", key=f"copy_{task_id}"):
237
+ st.code(audio_url)
238
+ st.success("URL copied to clipboard!")
239
+
240
+ # Stop auto-polling if active
241
+ if st.session_state.get('auto_poll'):
242
+ st.session_state.auto_poll = False
243
+ st.success("✅ Auto-polling stopped (audio ready)")
244
+ else:
245
+ st.info("⏳ Audio still processing...")
246
+ st.json(data)
247
+ else:
248
+ # Still processing or error
249
+ status = data.get("successFlag", "UNKNOWN")
250
+ if status == "PROCESSING":
251
+ st.info(f"⏳ Processing... ({status})")
252
+ elif status == "FAILED":
253
+ st.error(f"❌ Processing failed: {data.get('errorMessage', 'Unknown error')}")
254
+ else:
255
+ st.info(f"ℹ️ Status: {status}")
256
+
257
+ # Show progress info
258
+ st.json(data)
259
+ else:
260
+ st.error(f"❌ API Error: {result.get('msg', 'Unknown error')}")
261
+ st.json(result)
262
+ else:
263
+ st.error(f"❌ Status check failed: {response.status_code}")
264
+ try:
265
+ error_data = response.json()
266
+ st.json(error_data)
267
+ except:
268
+ st.text(f"Response: {response.text}")
269
+
270
+ except Exception as e:
271
+ st.error(f"⚠️ Polling error: {str(e)}")
272
+
273
+ # Auto-polling logic
274
+ if st.session_state.get('auto_poll') and st.session_state.get('auto_poll_task_id'):
275
+ task_id = st.session_state.auto_poll_task_id
276
+
277
+ # Create a placeholder for auto-poll status
278
+ poll_placeholder = st.empty()
279
+
280
+ with poll_placeholder.container():
281
+ st.info(f"🔁 Auto-polling task: `{task_id[:12]}...` (every 10 seconds)")
282
+
283
+ # Poll once
284
+ poll_status(task_id, single_poll=True)
285
+
286
+ # Schedule next poll
287
+ time.sleep(10)
288
+ st.rerun()
289
+
290
+ # Display poll results if any
291
+ if st.session_state.poll_results:
292
+ st.markdown("---")
293
+ st.header("📊 Polling History")
294
+
295
+ # Show last 5 results
296
+ recent_results = list(reversed(st.session_state.poll_results[-5:]))
297
+
298
+ for i, result in enumerate(recent_results):
299
+ with st.expander(f"Poll {i+1} - {result['timestamp']} - Task: {result['task_id'][:12]}...", expanded=(i == len(recent_results)-1)):
300
+ response_data = result["response"]
301
+
302
+ if response_data.get("code") == 200:
303
+ data = response_data.get("data", {})
304
+ status = data.get("successFlag", "UNKNOWN")
305
+
306
+ # Status badge
307
+ if status == "SUCCESS":
308
+ st.success(f"✅ {status}")
309
+ elif status == "PROCESSING":
310
+ st.info(f"⏳ {status}")
311
+ elif status == "FAILED":
312
+ st.error(f"❌ {status}")
313
+ else:
314
+ st.warning(f"⚠️ {status}")
315
+
316
+ # Display key info
317
+ cols = st.columns(3)
318
+ with cols[0]:
319
+ st.metric("Task ID", data.get("taskId", "N/A")[:12] + "...")
320
+ with cols[1]:
321
+ st.metric("Music ID", data.get("musicId", "N/A")[:12] + "...")
322
+ with cols[2]:
323
+ st.metric("Status", status)
324
+
325
+ # Show audio if available
326
+ audio_url = data.get("response", {}).get("audioWavUrl")
327
+ if audio_url:
328
+ st.audio(audio_url, format="audio/wav")
329
+ st.markdown(f"**Audio URL:** `{audio_url}`")
330
+
331
+ # Show full response
332
+ with st.expander("📋 Full Response JSON"):
333
+ st.json(response_data)
334
+ else:
335
+ st.error(f"❌ Error: {response_data.get('msg', 'Unknown error')}")
336
+ st.json(response_data)
337
+
338
+ # Quick links and info
339
+ st.markdown("---")
340
+ st.markdown("### 📝 Quick Links")
341
+ st.markdown("""
342
+ - 🔗 [View Suno Callbacks](https://1hit.no/wav/view.php) - See all callback logs
343
+ - 📚 [Suno API Documentation](https://docs.sunoapi.org/) - Official API docs
344
+ - 🔄 **Poll Endpoint**: `GET https://api.sunoapi.org/api/v1/wav/record-info`
345
+ """)
346
+
347
+ # Secret status
348
+ with st.expander("🔐 Secret Status", expanded=False):
349
+ if SUNO_API_KEY:
350
+ masked_key = f"{SUNO_API_KEY[:8]}...{SUNO_API_KEY[-8:]}" if len(SUNO_API_KEY) > 16 else "••••••••"
351
+ st.success(f"✅ SunoKey loaded ({len(SUNO_API_KEY)} chars)")
352
+ st.code(f"SunoKey: {masked_key}")
353
+ else:
354
+ st.error("❌ SunoKey not found")
355
+
356
+ st.info(f"**Callback URL:** `{CALLBACK_URL}`")