MySafeCode commited on
Commit
b6f49f7
·
verified ·
1 Parent(s): a74cea0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -345
app.py CHANGED
@@ -1,19 +1,13 @@
1
  import gradio as gr
2
  import requests
3
  import os
4
- import time
5
  import json
6
- import threading
7
 
8
  # Suno API key
9
  SUNO_KEY = os.environ.get("SunoKey", "")
10
  if not SUNO_KEY:
11
  print("⚠️ SunoKey not set!")
12
 
13
- # Store task info for auto-fill and polling
14
- current_task_info = {}
15
- polling_active = {}
16
-
17
  def get_audio_files(task_id):
18
  """Get audio files from Suno task ID"""
19
  if not SUNO_KEY:
@@ -54,12 +48,8 @@ def get_audio_files(task_id):
54
  audio_id = audio.get("id", f"audio_{i}")
55
  title = audio.get("title", f"Track {i+1}")
56
  duration = audio.get("duration", 0)
57
- prompt = audio.get("prompt", "")[:50]
58
 
59
  display = f"{i+1}. {title} ({duration:.1f}s)"
60
- if prompt:
61
- display += f" - {prompt}..."
62
-
63
  audio_options.append((display, audio_id))
64
 
65
  return f"✅ Found {len(audio_options)} audio file(s)", audio_options
@@ -67,15 +57,15 @@ def get_audio_files(task_id):
67
  except Exception as e:
68
  return f"❌ Error: {str(e)}", []
69
 
70
- def submit_separation_task(task_id, audio_id, separation_type):
71
- """Submit separation task"""
72
  try:
73
  resp = requests.post(
74
  "https://api.sunoapi.org/api/v1/vocal-removal/generate",
75
  json={
76
  "taskId": task_id,
77
  "audioId": audio_id,
78
- "type": separation_type,
79
  "callBackUrl": "https://1hit.no/callback.php"
80
  },
81
  headers={
@@ -85,425 +75,212 @@ def submit_separation_task(task_id, audio_id, separation_type):
85
  timeout=30
86
  )
87
 
 
 
 
88
  if resp.status_code == 200:
89
  data = resp.json()
 
90
 
91
- # Get separation task ID
92
  separation_task_id = None
93
  if "taskId" in data:
94
  separation_task_id = data["taskId"]
95
  elif data.get("code") == 200 and "data" in data and "taskId" in data["data"]:
96
  separation_task_id = data["data"]["taskId"]
 
 
97
 
98
  if separation_task_id:
99
- # Store for auto-fill
100
- current_task_info["separation_task_id"] = separation_task_id
101
- current_task_info["original_task_id"] = task_id
102
- current_task_info["audio_id"] = audio_id
103
- current_task_info["separation_type"] = separation_type
104
- current_task_info["start_time"] = time.time()
105
- current_task_info["attempt"] = 0
106
-
107
- return f"✅ Task submitted!\n\n**Separation Task ID:** `{separation_task_id}`\n\n⏳ Starting auto-polling...", separation_task_id
108
  else:
109
- return f"❌ No task ID in response:\n{json.dumps(data, indent=2)}", None
 
 
110
  else:
111
  return f"❌ HTTP Error {resp.status_code}:\n{resp.text}", None
112
 
113
  except Exception as e:
114
  return f"❌ Error: {str(e)}", None
115
 
116
- def poll_separation_status(separation_task_id):
117
- """Poll separation task status"""
 
 
 
118
  try:
119
  resp = requests.get(
120
  "https://api.sunoapi.org/api/v1/vocal-removal/record-info",
121
  headers={"Authorization": f"Bearer {SUNO_KEY}"},
122
- params={"taskId": separation_task_id},
123
  timeout=30
124
  )
125
 
 
 
126
  if resp.status_code == 200:
127
  data = resp.json()
 
128
 
129
- # Get status
130
- status = "UNKNOWN"
131
- if "status" in data:
132
- status = data["status"]
133
- elif "data" in data and "status" in data["data"]:
134
- status = data["data"]["status"]
135
- elif data.get("code") == 200:
136
- status = "SUCCESS"
137
-
138
- # Get results
139
- results = {}
140
- if "vocal_removal_info" in data:
141
- results = data["vocal_removal_info"]
142
- elif "data" in data and "vocal_removal_info" in data["data"]:
143
- results = data["data"]["vocal_removal_info"]
 
 
 
 
 
 
144
 
145
- return status, results, None
 
 
 
 
 
 
 
 
146
  else:
147
- return "ERROR", {}, f"HTTP {resp.status_code}: {resp.text}"
148
 
149
  except Exception as e:
150
- return "ERROR", {}, str(e)
151
-
152
- def format_download_links(results, separation_task_id):
153
- """Format download links from results"""
154
- if not results:
155
- return "No download links found"
156
-
157
- output = "## 🎵 Download Links\n\n"
158
-
159
- # 2-stem separation
160
- if "vocal_url" in results or "instrumental_url" in results:
161
- if results.get("vocal_url"):
162
- output += f"**🎤 Vocals:** [Download MP3]({results['vocal_url']})\n"
163
- if results.get("instrumental_url"):
164
- output += f"**🎵 Instrumental:** [Download MP3]({results['instrumental_url']})\n"
165
-
166
- # 12-stem separation
167
- stem_fields = [
168
- ("backing_vocals_url", "🎤 Backing Vocals"),
169
- ("bass_url", "🎸 Bass"),
170
- ("brass_url", "🎺 Brass"),
171
- ("drums_url", "🥁 Drums"),
172
- ("fx_url", "🎛️ FX/Other"),
173
- ("guitar_url", "🎸 Guitar"),
174
- ("keyboard_url", "🎹 Keyboard"),
175
- ("percussion_url", "🪘 Percussion"),
176
- ("strings_url", "🎻 Strings"),
177
- ("synth_url", "🎹 Synth"),
178
- ("woodwinds_url", "🎷 Woodwinds"),
179
- ("vocal_url", "🎤 Vocals"),
180
- ("instrumental_url", "🎵 Instrumental"),
181
- ]
182
-
183
- for field, name in stem_fields:
184
- if results.get(field):
185
- output += f"**{name}:** [Download MP3]({results[field]})\n"
186
-
187
- output += f"\n**🔗 Viewer:** [Open in Viewer](https://1hit.no/viewer.php?task_id={separation_task_id})"
188
-
189
- return output
190
-
191
- # Manual polling function
192
- def manual_poll(task_id):
193
- if not task_id:
194
- return "❌ Enter Task ID"
195
-
196
- status, results, error = poll_separation_status(task_id)
197
-
198
- if error:
199
- return f"❌ Error: {error}"
200
-
201
- if status == "SUCCESS":
202
- if results:
203
- output = f"✅ **Complete!**\n\n"
204
- output += f"**Task ID:** `{task_id}`\n\n"
205
- output += format_download_links(results, task_id)
206
- else:
207
- output = f"✅ **Complete** (no direct links)\n\n"
208
- output += f"**Task ID:** `{task_id}`\n\n"
209
- output += f"Check: [Viewer](https://1hit.no/viewer.php?task_id={task_id})"
210
-
211
- elif status in ["PENDING", "PROCESSING", "RUNNING"]:
212
- output = f"⏳ **Status:** {status}\n\n"
213
- output += f"**Task ID:** `{task_id}`\n\n"
214
- output += "Still processing. Try again in 30 seconds."
215
-
216
- elif status == "FAILED":
217
- output = f"❌ **Failed**\n\n"
218
- output += f"**Task ID:** `{task_id}`\n"
219
- output += f"**Status:** {status}"
220
-
221
- else:
222
- output = f"🔄 **Status:** {status}\n\n"
223
- output += f"**Task ID:** `{task_id}`"
224
-
225
- return output
226
 
227
  # Create the app
228
  with gr.Blocks() as app:
229
- gr.Markdown("# 🎵 Suno Stem Separator")
 
230
 
231
  with gr.Row():
232
- # Left column: Input and control
233
  with gr.Column(scale=1):
234
  # Step 1: Get audio files
235
- with gr.Group():
236
- gr.Markdown("### 1. Get Audio Files")
237
- original_task_id = gr.Textbox(
238
- label="Original Task ID",
239
- placeholder="Enter Suno generation task ID",
240
- info="From your Suno history"
241
- )
242
- get_audio_btn = gr.Button("📥 Get Audio Files", variant="secondary")
243
- audio_status = gr.Markdown("Enter Task ID above")
244
 
245
  # Step 2: Select audio file
246
- with gr.Group():
247
- gr.Markdown("### 2. Select Audio File")
248
- audio_dropdown = gr.Dropdown(
249
- label="Select Audio",
250
- choices=[],
251
- interactive=True,
252
- visible=False
253
- )
254
 
255
  # Step 3: Start separation
256
- with gr.Group():
257
- gr.Markdown("### 3. Start Separation")
258
- separation_type = gr.Radio(
259
- label="Separation Type",
260
- choices=[
261
- ("🎤 Vocals Only (1 credit)", "separate_vocal"),
262
- ("🎛️ Full Stems (5 credits)", "split_stem")
263
- ],
264
- value="separate_vocal",
265
- visible=False
266
- )
267
- submit_btn = gr.Button("🚀 Start Separation", variant="primary", visible=False)
268
- submission_output = gr.Markdown("Select audio file first", visible=False)
269
 
270
- # Right column: Results and polling
271
- with gr.Column(scale=2):
272
- # Auto-polling section
273
- with gr.Group():
274
- gr.Markdown("### 4. Auto-Polling Status")
275
- auto_poll_status = gr.Markdown("Waiting for task submission...")
276
- auto_poll_progress = gr.Slider(
277
- minimum=0,
278
- maximum=60,
279
- value=0,
280
- label="Polling attempts",
281
- interactive=False,
282
- visible=False
283
- )
284
- stop_poll_btn = gr.Button("⏹️ Stop Auto-Polling", variant="stop", visible=False)
285
-
286
- # Manual polling section
287
- with gr.Group():
288
- gr.Markdown("### 5. Manual Polling")
289
- poll_task_id = gr.Textbox(
290
- label="Separation Task ID",
291
- placeholder="Will auto-fill from current task"
292
- )
293
- poll_btn = gr.Button("🔍 Check Status", variant="secondary")
294
- poll_output = gr.Markdown("Enter Task ID to check")
295
 
296
- # Results section
297
- with gr.Group():
298
- gr.Markdown("### 6. Download Links")
299
- download_output = gr.Markdown("Results will appear here")
300
 
301
- # Viewer link
302
- with gr.Group():
303
- gr.Markdown("### 7. Viewer")
304
- viewer_link = gr.Markdown(
305
- "[Open Viewer](https://1hit.no/viewer.php)",
306
- elem_id="viewer_link"
307
- )
308
-
309
- # Store current separation task ID for auto-polling
310
- current_separation_task_id = gr.State(value="")
311
- polling_active_flag = gr.State(value=False)
312
- polling_thread = None
313
 
314
  # Step 1: Get audio files
315
  def on_get_audio(task_id):
316
  if not task_id:
317
- return "❌ Enter Task ID", gr.Dropdown(choices=[], visible=False), gr.Radio(visible=False), gr.Button(visible=False), gr.Markdown(visible=False)
318
 
319
  status, options = get_audio_files(task_id)
320
 
321
  if not options:
322
- return status, gr.Dropdown(choices=[], visible=False), gr.Radio(visible=False), gr.Button(visible=False), gr.Markdown(visible=False)
323
 
324
  return (
325
  status,
326
  gr.Dropdown(choices=options, value=options[0][1] if options else None, visible=True),
327
- gr.Radio(visible=True),
328
  gr.Button(visible=True),
329
- gr.Markdown("Ready to separate!", visible=True)
330
  )
331
 
332
- # Step 2-3: Submit separation task
333
- def on_submit(task_id, audio_id, sep_type, active_flag):
334
  if not task_id or not audio_id:
335
- return "❌ Missing Task ID or Audio ID", "", "⏳ Waiting...", gr.Slider(visible=False), gr.Button(visible=False), active_flag
336
 
337
- status, separation_task_id = submit_separation_task(task_id, audio_id, sep_type)
338
-
339
- if not separation_task_id:
340
- return status, "", "⏳ Waiting...", gr.Slider(visible=False), gr.Button(visible=False), active_flag
341
-
342
- # Start auto-polling
343
- return (
344
- status,
345
- separation_task_id,
346
- "⏳ Polling started... (attempt 0/60)",
347
- gr.Slider(visible=True, value=0),
348
- gr.Button(visible=True),
349
- True # Set polling active
350
- )
351
-
352
- # Auto-polling update function
353
- def update_auto_poll(separation_task_id, attempt, active_flag):
354
- if not active_flag or not separation_task_id:
355
- return "⏳ Polling stopped", attempt, "", False
356
-
357
- attempt += 1
358
-
359
- # Poll for status
360
- status, results, error = poll_separation_status(separation_task_id)
361
-
362
- if error:
363
- return f"❌ Poll error: {error}", attempt, "", False
364
-
365
- if status == "SUCCESS":
366
- if results:
367
- output = f"✅ **Separation Complete!**\n\n"
368
- output += f"**Task ID:** `{separation_task_id}`\n\n"
369
- output += format_download_links(results, separation_task_id)
370
- return f"✅ Complete! (attempt {attempt})", attempt, output, False
371
- else:
372
- output = f"✅ **Processing Complete**\n\n"
373
- output += f"**Task ID:** `{separation_task_id}`\n\n"
374
- output += "Check callback results: "
375
- output += f"[Viewer](https://1hit.no/viewer.php?task_id={separation_task_id})"
376
- return f"✅ Complete! (attempt {attempt})", attempt, output, False
377
-
378
- elif status in ["PENDING", "PROCESSING", "RUNNING"]:
379
- status_text = f"⏳ **Status:** {status}\n\n"
380
- status_text += f"**Task ID:** `{separation_task_id}`\n"
381
- status_text += f"**Attempt:** {attempt}/60\n"
382
- status_text += f"**Elapsed:** {attempt * 5} seconds\n\n"
383
- status_text += "Polling every 5 seconds..."
384
-
385
- if attempt >= 60:
386
- status_text = f"⏰ **Timeout after 5 minutes**\n\n"
387
- status_text += f"**Task ID:** `{separation_task_id}`\n"
388
- status_text += "Check manually or wait for callback."
389
- return status_text, attempt, "", False
390
-
391
- return status_text, attempt, "", True
392
-
393
- elif status == "FAILED":
394
- status_text = f"❌ **Separation Failed**\n\n"
395
- status_text += f"**Task ID:** `{separation_task_id}`\n"
396
- status_text += f"**Status:** {status}"
397
- return status_text, attempt, "", False
398
 
 
 
399
  else:
400
- status_text = f"🔄 **Status:** {status}\n\n"
401
- status_text += f"**Task ID:** `{separation_task_id}`\n"
402
- status_text += f"**Attempt:** {attempt}/60"
403
-
404
- if attempt >= 60:
405
- status_text = f"⏰ **Timeout after 5 minutes**\n\n"
406
- status_text += f"**Task ID:** `{separation_task_id}`\n"
407
- status_text += "Check manually or wait for callback."
408
- return status_text, attempt, "", False
409
-
410
- return status_text, attempt, "", True
411
 
412
- # Stop polling function
413
- def stop_polling(active_flag):
414
- return "⏹️ Polling stopped", 0, "", False, gr.Button(visible=False)
415
 
416
  # Connect events
417
  get_audio_btn.click(
418
  fn=on_get_audio,
419
  inputs=[original_task_id],
420
- outputs=[audio_status, audio_dropdown, separation_type, submit_btn, submission_output]
421
  )
422
 
423
- # Submit button starts polling
424
  submit_btn.click(
425
  fn=on_submit,
426
- inputs=[original_task_id, audio_dropdown, separation_type, polling_active_flag],
427
- outputs=[submission_output, current_separation_task_id, auto_poll_status, auto_poll_progress, stop_poll_btn, polling_active_flag]
428
- )
429
-
430
- # Create a separate function for polling that runs independently
431
- def start_polling_thread(separation_task_id, attempt, active_flag):
432
- """Background polling thread"""
433
- while active_flag and attempt < 60:
434
- time.sleep(5)
435
- attempt += 1
436
-
437
- status, results, error = poll_separation_status(separation_task_id)
438
-
439
- if status == "SUCCESS" or status == "FAILED" or error:
440
- # Update UI via queue or state
441
- break
442
-
443
- return attempt
444
-
445
- # Manual polling
446
- poll_btn.click(
447
- fn=manual_poll,
448
- inputs=[poll_task_id],
449
- outputs=[poll_output]
450
  )
451
 
452
- # Stop polling button
453
- stop_poll_btn.click(
454
- fn=stop_polling,
455
- inputs=[polling_active_flag],
456
- outputs=[auto_poll_status, auto_poll_progress, download_output, polling_active_flag, stop_poll_btn]
457
  )
458
 
459
- # Auto-fill poll field when separation task is submitted
460
- def update_poll_field(separation_task_id):
461
- if separation_task_id:
462
- return separation_task_id
463
- return ""
464
-
465
- current_separation_task_id.change(
466
- fn=update_poll_field,
467
- inputs=[current_separation_task_id],
468
- outputs=[poll_task_id]
469
- )
470
-
471
- # Simple polling timer (manual refresh)
472
- def manual_refresh(separation_task_id, attempt, active_flag):
473
- if active_flag and separation_task_id:
474
- return update_auto_poll(separation_task_id, attempt, active_flag)
475
- return "⏳ Waiting for task...", 0, "", active_flag
476
-
477
- # Add a refresh button for manual polling updates
478
- refresh_btn = gr.Button("🔄 Refresh Status", variant="secondary", visible=False)
479
-
480
- # Show/hide refresh button based on polling state
481
- def toggle_refresh_button(active_flag):
482
- return gr.Button(visible=active_flag)
483
-
484
- polling_active_flag.change(
485
- fn=toggle_refresh_button,
486
- inputs=[polling_active_flag],
487
- outputs=[refresh_btn]
488
- )
489
 
490
- refresh_btn.click(
491
- fn=manual_refresh,
492
- inputs=[current_separation_task_id, auto_poll_progress, polling_active_flag],
493
- outputs=[auto_poll_status, auto_poll_progress, download_output, polling_active_flag]
494
  )
495
 
496
  # Launch the app
497
  if __name__ == "__main__":
498
- print("🚀 Starting Suno Stem Separator")
499
  print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
500
  print("🌐 Open your browser to: http://localhost:7860")
501
 
502
- # Launch with theme parameter in launch() method
503
  app.launch(
504
  server_name="0.0.0.0",
505
  server_port=7860,
506
- share=False,
507
- title="Suno Stem Separator",
508
- theme="soft"
509
  )
 
1
  import gradio as gr
2
  import requests
3
  import os
 
4
  import json
 
5
 
6
  # Suno API key
7
  SUNO_KEY = os.environ.get("SunoKey", "")
8
  if not SUNO_KEY:
9
  print("⚠️ SunoKey not set!")
10
 
 
 
 
 
11
  def get_audio_files(task_id):
12
  """Get audio files from Suno task ID"""
13
  if not SUNO_KEY:
 
48
  audio_id = audio.get("id", f"audio_{i}")
49
  title = audio.get("title", f"Track {i+1}")
50
  duration = audio.get("duration", 0)
 
51
 
52
  display = f"{i+1}. {title} ({duration:.1f}s)"
 
 
 
53
  audio_options.append((display, audio_id))
54
 
55
  return f"✅ Found {len(audio_options)} audio file(s)", audio_options
 
57
  except Exception as e:
58
  return f"❌ Error: {str(e)}", []
59
 
60
+ def submit_vocal_separation(task_id, audio_id):
61
+ """Submit vocal separation task (2-stem only)"""
62
  try:
63
  resp = requests.post(
64
  "https://api.sunoapi.org/api/v1/vocal-removal/generate",
65
  json={
66
  "taskId": task_id,
67
  "audioId": audio_id,
68
+ "type": "separate_vocal", # Always 2-stem
69
  "callBackUrl": "https://1hit.no/callback.php"
70
  },
71
  headers={
 
75
  timeout=30
76
  )
77
 
78
+ print(f"Submission response: {resp.status_code}")
79
+ print(f"Response: {resp.text}")
80
+
81
  if resp.status_code == 200:
82
  data = resp.json()
83
+ print(f"Parsed data: {data}")
84
 
85
+ # Try to get separation task ID
86
  separation_task_id = None
87
  if "taskId" in data:
88
  separation_task_id = data["taskId"]
89
  elif data.get("code") == 200 and "data" in data and "taskId" in data["data"]:
90
  separation_task_id = data["data"]["taskId"]
91
+ elif data.get("data") and "taskId" in data.get("data", {}):
92
+ separation_task_id = data["data"]["taskId"]
93
 
94
  if separation_task_id:
95
+ return f"✅ **Task submitted!**\n\n**Separation Task ID:** `{separation_task_id}`\n\nCheck status below using this ID.", separation_task_id
 
 
 
 
 
 
 
 
96
  else:
97
+ # Try to extract error or show raw response
98
+ error_msg = data.get("msg", data.get("error", "Unknown error"))
99
+ return f"❌ No task ID in response: {error_msg}\n\nRaw: {json.dumps(data, indent=2)}", None
100
  else:
101
  return f"❌ HTTP Error {resp.status_code}:\n{resp.text}", None
102
 
103
  except Exception as e:
104
  return f"❌ Error: {str(e)}", None
105
 
106
+ def check_separation_status(task_id):
107
+ """Check vocal separation task status"""
108
+ if not task_id:
109
+ return "❌ Enter a Task ID to check"
110
+
111
  try:
112
  resp = requests.get(
113
  "https://api.sunoapi.org/api/v1/vocal-removal/record-info",
114
  headers={"Authorization": f"Bearer {SUNO_KEY}"},
115
+ params={"taskId": task_id},
116
  timeout=30
117
  )
118
 
119
+ print(f"Status check for {task_id}: {resp.status_code}")
120
+
121
  if resp.status_code == 200:
122
  data = resp.json()
123
+ print(f"Status data: {data}")
124
 
125
+ # Check for success
126
+ if data.get("code") == 200:
127
+ # Get vocal removal info
128
+ vocal_info = data.get("data", {}).get("vocal_removal_info", {})
129
+
130
+ if vocal_info:
131
+ # Format results
132
+ output = "✅ **Separation Complete!**\n\n"
133
+ output += f"**Task ID:** `{task_id}`\n\n"
134
+
135
+ if vocal_info.get("vocal_url"):
136
+ output += f"**🎤 Vocals:** [Download MP3]({vocal_info['vocal_url']})\n"
137
+ if vocal_info.get("instrumental_url"):
138
+ output += f"**🎵 Instrumental:** [Download MP3]({vocal_info['instrumental_url']})\n"
139
+ if vocal_info.get("origin_url"):
140
+ output += f"**📁 Original:** [Download MP3]({vocal_info['origin_url']})\n"
141
+
142
+ output += f"\n**🔗 Viewer:** [Open in Viewer](https://1hit.no/viewer.php?task_id={task_id})"
143
+ return output
144
+ else:
145
+ return f"✅ Processing complete but no URLs yet.\n\nCheck callback or try again later.\n\n**Task ID:** `{task_id}`"
146
 
147
+ # Check for other statuses
148
+ status = data.get("data", {}).get("status", "UNKNOWN")
149
+ if status in ["PENDING", "PROCESSING", "RUNNING"]:
150
+ return f"⏳ **Status:** {status}\n\n**Task ID:** `{task_id}`\n\nStill processing. Check again in 30 seconds."
151
+ elif status == "FAILED":
152
+ error = data.get("data", {}).get("errorMessage", "Unknown error")
153
+ return f"❌ **Failed:** {error}\n\n**Task ID:** `{task_id}`"
154
+ else:
155
+ return f"🔄 **Status:** {status}\n\n**Task ID:** `{task_id}`\n\nRaw: {json.dumps(data, indent=2)}"
156
  else:
157
+ return f" HTTP Error {resp.status_code}:\n{resp.text}"
158
 
159
  except Exception as e:
160
+ return f" Error checking status: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  # Create the app
163
  with gr.Blocks() as app:
164
+ gr.Markdown("# 🎵 Suno Vocal Separator")
165
+ gr.Markdown("Separate vocals from Suno tracks (2-stem only)")
166
 
167
  with gr.Row():
168
+ # Left column: Input and submit
169
  with gr.Column(scale=1):
170
  # Step 1: Get audio files
171
+ gr.Markdown("### 1. Get Audio Files")
172
+ original_task_id = gr.Textbox(
173
+ label="Original Task ID",
174
+ placeholder="Enter Suno generation task ID",
175
+ info="From your Suno history"
176
+ )
177
+ get_audio_btn = gr.Button("📥 Get Audio Files", variant="secondary")
178
+ audio_status = gr.Markdown("Enter Task ID above")
 
179
 
180
  # Step 2: Select audio file
181
+ gr.Markdown("### 2. Select Audio File")
182
+ audio_dropdown = gr.Dropdown(
183
+ label="Select Audio",
184
+ choices=[],
185
+ interactive=True,
186
+ visible=False
187
+ )
 
188
 
189
  # Step 3: Start separation
190
+ gr.Markdown("### 3. Start Vocal Separation")
191
+ submit_btn = gr.Button("🚀 Separate Vocals", variant="primary", visible=False)
192
+ submission_output = gr.Markdown("Select audio file first", visible=False)
 
 
 
 
 
 
 
 
 
 
193
 
194
+ # Right column: Check status and results
195
+ with gr.Column(scale=1):
196
+ # Step 4: Check status
197
+ gr.Markdown("### 4. Check Separation Status")
198
+ separation_task_id = gr.Textbox(
199
+ label="Separation Task ID",
200
+ placeholder="Will auto-fill after submission"
201
+ )
202
+ check_btn = gr.Button("🔍 Check Status", variant="secondary")
203
+ status_output = gr.Markdown("Enter Task ID to check")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
+ # Step 5: Results
206
+ gr.Markdown("### 5. Download Links")
207
+ results_output = gr.Markdown("Results will appear here")
 
208
 
209
+ # Step 6: Viewer link
210
+ gr.Markdown("### 6. Viewer")
211
+ gr.Markdown("[Open Viewer](https://1hit.no/viewer.php)")
 
 
 
 
 
 
 
 
 
212
 
213
  # Step 1: Get audio files
214
  def on_get_audio(task_id):
215
  if not task_id:
216
+ return "❌ Enter Task ID", gr.Dropdown(choices=[], visible=False), gr.Button(visible=False), gr.Markdown(visible=False)
217
 
218
  status, options = get_audio_files(task_id)
219
 
220
  if not options:
221
+ return status, gr.Dropdown(choices=[], visible=False), gr.Button(visible=False), gr.Markdown(visible=False)
222
 
223
  return (
224
  status,
225
  gr.Dropdown(choices=options, value=options[0][1] if options else None, visible=True),
 
226
  gr.Button(visible=True),
227
+ gr.Markdown("Ready for vocal separation!", visible=True)
228
  )
229
 
230
+ # Step 2-3: Submit vocal separation
231
+ def on_submit(task_id, audio_id):
232
  if not task_id or not audio_id:
233
+ return "❌ Missing Task ID or Audio ID", "", "⏳ Waiting..."
234
 
235
+ status, sep_task_id = submit_vocal_separation(task_id, audio_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ if sep_task_id:
238
+ return status, sep_task_id, "✅ Task submitted! Check status above."
239
  else:
240
+ return status, "", "❌ Failed to submit task"
 
 
 
 
 
 
 
 
 
 
241
 
242
+ # Step 4: Check status
243
+ def on_check_status(task_id):
244
+ return check_separation_status(task_id)
245
 
246
  # Connect events
247
  get_audio_btn.click(
248
  fn=on_get_audio,
249
  inputs=[original_task_id],
250
+ outputs=[audio_status, audio_dropdown, submit_btn, submission_output]
251
  )
252
 
 
253
  submit_btn.click(
254
  fn=on_submit,
255
+ inputs=[original_task_id, audio_dropdown],
256
+ outputs=[submission_output, separation_task_id, status_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  )
258
 
259
+ check_btn.click(
260
+ fn=on_check_status,
261
+ inputs=[separation_task_id],
262
+ outputs=[results_output]
 
263
  )
264
 
265
+ # Auto-fill separation task ID field after submission
266
+ def auto_fill_sep_id(sep_task_id):
267
+ return sep_task_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ separation_task_id.change(
270
+ fn=auto_fill_sep_id,
271
+ inputs=[separation_task_id],
272
+ outputs=[separation_task_id]
273
  )
274
 
275
  # Launch the app
276
  if __name__ == "__main__":
277
+ print("🚀 Starting Suno Vocal Separator")
278
  print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
279
  print("🌐 Open your browser to: http://localhost:7860")
280
 
281
+ # Simple launch without extra parameters
282
  app.launch(
283
  server_name="0.0.0.0",
284
  server_port=7860,
285
+ share=False
 
 
286
  )