MySafeCode commited on
Commit
ee7ac4d
·
verified ·
1 Parent(s): f5b96f5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +353 -77
app.py CHANGED
@@ -1,80 +1,356 @@
1
- <?php
2
- // view.php - View Suno callbacks
3
- $logFile = 'callback_log.json';
4
- $logData = file_exists($logFile) ? json_decode(file_get_contents($logFile), true) : [];
 
5
 
6
- if (isset($_POST['clear'])) {
7
- file_put_contents($logFile, '[]');
8
- header('Location: view.php');
9
- exit();
10
- }
11
- ?>
12
- <!DOCTYPE html>
13
- <html>
14
- <head>
15
- <title>Suno Callbacks</title>
16
- <style>
17
- body { font-family: Arial; padding: 20px; }
18
- table { width: 100%; border-collapse: collapse; margin-top: 20px; }
19
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
20
- th { background-color: #667eea; color: white; }
21
- .success { background: #d4edda; }
22
- .error { background: #f8d7da; }
23
- audio { width: 200px; }
24
- .controls { margin-bottom: 20px; }
25
- button { background: #667eea; color: white; border: none; padding: 10px 20px; cursor: pointer; }
26
- button:hover { background: #5a6fde; }
27
- </style>
28
- </head>
29
- <body>
30
- <h1>🎵 Suno Callbacks (<?php echo count($logData); ?>)</h1>
31
 
32
- <div class="controls">
33
- <form method="POST">
34
- <button type="submit" name="clear">🗑️ Clear All Logs</button>
35
- </form>
36
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
- <?php if (empty($logData)): ?>
39
- <p>No callbacks received yet. Callbacks will appear here when Suno API sends them.</p>
40
- <p>Callback URL: <code>https://1hit.no/wav/cb.php</code></p>
41
- <?php else: ?>
42
- <table>
43
- <tr>
44
- <th>Time</th>
45
- <th>Task ID</th>
46
- <th>Status</th>
47
- <th>Audio</th>
48
- <th>Actions</th>
49
- </tr>
50
- <?php foreach(array_reverse($logData) as $log): ?>
51
- <tr class="<?php echo (isset($log['callback_data']['code']) && $log['callback_data']['code'] == 200) ? 'success' : 'error'; ?>">
52
- <td><?php echo htmlspecialchars($log['timestamp']); ?></td>
53
- <td><?php echo htmlspecialchars($log['callback_data']['data']['taskId'] ?? 'N/A'); ?></td>
54
- <td>
55
- <?php
56
- if (isset($log['callback_data']['code']) && $log['callback_data']['code'] == 200) {
57
- echo '✅ Success';
58
- } else {
59
- echo '❌ Error';
60
- }
61
- ?>
62
- </td>
63
- <td>
64
- <?php if(isset($log['callback_data']['data']['response']['audioWavUrl'])): ?>
65
- <audio controls src="<?php echo htmlspecialchars($log['callback_data']['data']['response']['audioWavUrl']); ?>"></audio>
66
- <?php else: ?>
67
- No audio
68
- <?php endif; ?>
69
- </td>
70
- <td>
71
- <?php if(isset($log['callback_data']['data']['response']['audioWavUrl'])): ?>
72
- <a href="<?php echo htmlspecialchars($log['callback_data']['data']['response']['audioWavUrl']); ?>" download>⬇ Download</a>
73
- <?php endif; ?>
74
- </td>
75
- </tr>
76
- <?php endforeach; ?>
77
- </table>
78
- <?php endif; ?>
79
- </body>
80
- </html>
 
1
+ import streamlit as st
2
+ import json
3
+ import requests
4
+ import time
5
+ from datetime import datetime
6
 
7
+ # Polling function - defined FIRST
8
+ def poll_status(task_id, single_poll=False):
9
+ """Poll Suno API for task status using /api/v1/wav/record-info"""
10
+ headers = {
11
+ "Authorization": f"Bearer {st.session_state.suno_api_key}",
12
+ "Content-Type": "application/json"
13
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ try:
16
+ with st.spinner(f"Checking status for task: {task_id}..."):
17
+ # Poll using the correct endpoint
18
+ response = requests.get(
19
+ f"https://api.sunoapi.org/api/v1/wav/record-info",
20
+ headers=headers,
21
+ timeout=30
22
+ )
23
+
24
+ if response.status_code == 200:
25
+ result = response.json()
26
+
27
+ # Add to poll results
28
+ poll_entry = {
29
+ "timestamp": datetime.now().strftime("%H:%M:%S"),
30
+ "task_id": task_id,
31
+ "response": result
32
+ }
33
+ st.session_state.poll_results.append(poll_entry)
34
+
35
+ # Display result
36
+ if result.get("code") == 200:
37
+ data = result.get("data", {})
38
+
39
+ if data.get("successFlag") == "SUCCESS":
40
+ # Audio is ready!
41
+ audio_url = data.get("response", {}).get("audioWavUrl")
42
+ if audio_url:
43
+ st.success("✅ Audio ready!")
44
+ st.balloons()
45
+
46
+ # Display audio info
47
+ st.markdown("### 🎵 Audio Information")
48
+ cols = st.columns(2)
49
+ with cols[0]:
50
+ st.metric("Task ID", data.get("taskId", "N/A")[:12] + "...")
51
+ st.metric("Music ID", data.get("musicId", "N/A")[:12] + "...")
52
+ with cols[1]:
53
+ st.metric("Status", data.get("successFlag", "N/A"))
54
+ st.metric("Created", data.get("createTime", "N/A"))
55
+
56
+ # Audio player
57
+ st.markdown("### 🔊 Listen")
58
+ st.audio(audio_url, format="audio/wav")
59
+
60
+ # Download section
61
+ st.markdown("### 📥 Download")
62
+ st.code(audio_url)
63
+
64
+ col_dl1, col_dl2 = st.columns(2)
65
+ with col_dl1:
66
+ st.download_button(
67
+ label="⬇ Download WAV",
68
+ data=requests.get(audio_url).content if audio_url.startswith("http") else b"",
69
+ file_name=f"audio_{data.get('taskId', 'unknown')[:8]}.wav",
70
+ mime="audio/wav",
71
+ key=f"download_{task_id}"
72
+ )
73
+ with col_dl2:
74
+ if st.button("📋 Copy URL", key=f"copy_{task_id}"):
75
+ st.code(audio_url)
76
+ st.success("URL copied to clipboard!")
77
+
78
+ # Stop auto-polling if active
79
+ if st.session_state.get('auto_poll'):
80
+ st.session_state.auto_poll = False
81
+ st.success("✅ Auto-polling stopped (audio ready)")
82
+ else:
83
+ st.info("⏳ Audio still processing...")
84
+ st.json(data)
85
+ else:
86
+ # Still processing or error
87
+ status = data.get("successFlag", "UNKNOWN")
88
+ if status == "PROCESSING":
89
+ st.info(f"⏳ Processing... ({status})")
90
+ elif status == "FAILED":
91
+ st.error(f"❌ Processing failed: {data.get('errorMessage', 'Unknown error')}")
92
+ else:
93
+ st.info(f"ℹ️ Status: {status}")
94
+
95
+ # Show progress info
96
+ st.json(data)
97
+ else:
98
+ st.error(f"❌ API Error: {result.get('msg', 'Unknown error')}")
99
+ st.json(result)
100
+ else:
101
+ st.error(f"❌ Status check failed: {response.status_code}")
102
+ try:
103
+ error_data = response.json()
104
+ st.json(error_data)
105
+ except:
106
+ st.text(f"Response: {response.text}")
107
+
108
+ except Exception as e:
109
+ st.error(f"⚠️ Polling error: {str(e)}")
110
+
111
+ # Page configuration
112
+ st.set_page_config(
113
+ page_title="Suno API Generator",
114
+ page_icon="🎵",
115
+ layout="wide"
116
+ )
117
+
118
+ # Load YOUR secret names
119
+ SUNO_API_KEY = st.secrets.get("SunoKey", "")
120
+ CALLBACK_URL = "https://1hit.no/wav/cb.php"
121
+
122
+ # Initialize session state
123
+ if 'task_id' not in st.session_state:
124
+ st.session_state.task_id = None
125
+ if 'polling' not in st.session_state:
126
+ st.session_state.polling = False
127
+ if 'poll_results' not in st.session_state:
128
+ st.session_state.poll_results = []
129
+ if 'auto_poll' not in st.session_state:
130
+ st.session_state.auto_poll = False
131
+ if 'suno_api_key' not in st.session_state:
132
+ st.session_state.suno_api_key = SUNO_API_KEY
133
+
134
+ st.title("🎵 Suno API Audio Generator")
135
+
136
+ # Info section
137
+ with st.expander("📋 How it works", expanded=True):
138
+ st.markdown("""
139
+ ### Workflow:
140
+ 1. **Step 1**: Submit task to Suno API → Get `taskId`
141
+ 2. **Step 2**: Suno processes audio (async)
142
+ 3. **Step 3**: Suno sends callback to: `https://1hit.no/wav/cb.php`
143
+ 4. **Step 4**: You can view callbacks at: `https://1hit.no/wav/view.php`
144
+ 5. **Step 5**: OR use Poll button to check status via `/api/v1/wav/record-info`
145
+
146
+ ### Polling Endpoint:
147
+ ```javascript
148
+ fetch('https://api.sunoapi.org/api/v1/wav/record-info', {
149
+ method: 'GET',
150
+ headers: {
151
+ 'Authorization': 'Bearer YOUR_TOKEN'
152
+ }
153
+ })
154
+ ```
155
+ """)
156
+
157
+ col1, col2 = st.columns([2, 1])
158
+
159
+ with col1:
160
+ st.header("🚀 Step 1: Submit to Suno API")
161
+
162
+ # Form inputs
163
+ task_id = st.text_input("Task ID", value="5c79****be8e", help="Enter your Suno task ID", key="input_task_id")
164
+ audio_id = st.text_input("Audio ID", value="e231****-****-****-****-****8cadc7dc", help="Enter your Suno audio ID", key="input_audio_id")
165
+
166
+ if st.button("🎵 Generate Audio", type="primary", use_container_width=True, key="generate_btn"):
167
+ if not SUNO_API_KEY:
168
+ st.error("❌ SunoKey not configured in Hugging Face secrets")
169
+ st.info("Add `SunoKey` secret in Hugging Face Space settings")
170
+ st.stop()
171
+
172
+ if not task_id or not audio_id:
173
+ st.error("❌ Please enter both Task ID and Audio ID")
174
+ st.stop()
175
+
176
+ with st.spinner("Sending request to Suno API..."):
177
+ headers = {
178
+ "Authorization": f"Bearer {SUNO_API_KEY}",
179
+ "Content-Type": "application/json"
180
+ }
181
+
182
+ payload = {
183
+ "taskId": task_id,
184
+ "audioId": audio_id,
185
+ "callBackUrl": CALLBACK_URL
186
+ }
187
+
188
+ try:
189
+ response = requests.post(
190
+ "https://api.sunoapi.org/api/v1/wav/generate",
191
+ headers=headers,
192
+ json=payload,
193
+ timeout=30
194
+ )
195
+
196
+ if response.status_code == 200:
197
+ result = response.json()
198
+
199
+ # Display response
200
+ with st.expander("📋 API Response", expanded=True):
201
+ st.json(result)
202
+
203
+ if result.get("code") == 200 and "data" in result and "taskId" in result["data"]:
204
+ st.session_state.task_id = result["data"]["taskId"]
205
+ st.success(f"✅ Task submitted successfully!")
206
+ st.info(f"**Task ID:** `{st.session_state.task_id}`")
207
+
208
+ # Auto-fill poll input
209
+ st.session_state.poll_task_input = st.session_state.task_id
210
+
211
+ st.markdown("---")
212
+ st.markdown("### 🔄 Next Steps:")
213
+ st.markdown("""
214
+ 1. **Automatic Callback**: Suno will send result to your callback URL
215
+ 2. **Manual Poll**: Use the Poll button to check status
216
+ 3. **View Logs**: Check [Callback Viewer](https://1hit.no/wav/view.php)
217
+ """)
218
+ else:
219
+ st.error("❌ Unexpected response format")
220
+ else:
221
+ st.error(f"❌ API Error: {response.status_code}")
222
+ try:
223
+ error_data = response.json()
224
+ st.json(error_data)
225
+ except:
226
+ st.text(f"Response: {response.text}")
227
+
228
+ except requests.exceptions.Timeout:
229
+ st.error("⏰ Request timed out after 30 seconds")
230
+ except requests.exceptions.ConnectionError:
231
+ st.error("🔌 Connection error - check your internet connection")
232
+ except requests.exceptions.RequestException as e:
233
+ st.error(f"⚠️ Request failed: {str(e)}")
234
+
235
+ with col2:
236
+ st.header("🔍 Step 2: Poll Status")
237
+
238
+ # Manual task ID input for polling
239
+ poll_task_id = st.text_input(
240
+ "Task ID to Poll",
241
+ value=st.session_state.get('poll_task_input', st.session_state.task_id or ""),
242
+ help="Enter task ID to check status",
243
+ key="poll_task_input_field"
244
+ )
245
+
246
+ col2a, col2b = st.columns(2)
247
+
248
+ with col2a:
249
+ poll_once_btn = st.button("🔄 Poll Once", type="secondary", use_container_width=True, key="poll_once_btn")
250
+
251
+ with col2b:
252
+ auto_poll_btn = st.button("🔁 Auto Poll (10s)", type="secondary", use_container_width=True, key="auto_poll_btn")
253
+
254
+ # Handle poll button clicks
255
+ if poll_once_btn:
256
+ if not poll_task_id:
257
+ st.error("❌ Please enter a Task ID")
258
+ elif not SUNO_API_KEY:
259
+ st.error("❌ SunoKey not configured")
260
+ else:
261
+ poll_status(poll_task_id, single_poll=True)
262
+
263
+ if auto_poll_btn:
264
+ if not poll_task_id:
265
+ st.error("❌ Please enter a Task ID")
266
+ elif not SUNO_API_KEY:
267
+ st.error("❌ SunoKey not configured")
268
+ else:
269
+ st.session_state.auto_poll = True
270
+ st.session_state.auto_poll_task_id = poll_task_id
271
+ st.rerun()
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}`")