MySafeCode commited on
Commit
1ff9b8a
·
verified ·
1 Parent(s): 5fe6d91

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -414
app.py CHANGED
@@ -3,106 +3,22 @@ import requests
3
  import os
4
  import time
5
  import json
6
- import uuid
7
- from datetime import datetime
8
 
9
  # Suno API key
10
  SUNO_KEY = os.environ.get("SunoKey", "")
11
  if not SUNO_KEY:
12
  print("⚠️ SunoKey not set!")
13
 
14
- # Store ongoing separation tasks (in-memory for demo)
15
- # In production, use a database or Redis
16
- separation_tasks = {}
17
-
18
- def get_audio_info(task_id):
19
- """Get audio information from Suno task ID"""
20
- if not SUNO_KEY:
21
- return "❌ Error: SunoKey not configured in environment variables", []
22
-
23
- if not task_id.strip():
24
- return "❌ Error: Please enter Task ID", []
25
-
26
- try:
27
- resp = requests.get(
28
- "https://api.sunoapi.org/api/v1/generate/record-info",
29
- headers={"Authorization": f"Bearer {SUNO_KEY}"},
30
- params={"taskId": task_id.strip()},
31
- timeout=30
32
- )
33
-
34
- if resp.status_code != 200:
35
- return f"❌ Request failed: HTTP {resp.status_code}", []
36
-
37
- data = resp.json()
38
- if data.get("code") != 200:
39
- return f"❌ API error: {data.get('msg', 'Unknown')}", []
40
-
41
- # Check if task is complete
42
- status = data["data"].get("status", "PENDING")
43
- if status != "SUCCESS":
44
- return f"⏳ Task status: {status}. Please wait for generation to complete.", []
45
-
46
- # Parse sunoData to get audio options
47
- suno_data = data["data"]["response"].get("sunoData", [])
48
- if not suno_data:
49
- return "❌ No audio data found in response", []
50
-
51
- # Create audio options for dropdown
52
- audio_options = []
53
- for i, audio in enumerate(suno_data):
54
- audio_id = audio.get("id", "")
55
- prompt = audio.get("prompt", "No prompt")
56
- title = audio.get("title", "Untitled")
57
- duration = audio.get("duration", 0)
58
-
59
- # Truncate long prompts for display
60
- display_prompt = prompt[:50] + "..." if len(prompt) > 50 else prompt
61
-
62
- # Create display text and store full data
63
- display_text = f"Track {i+1}: {title} ({duration:.1f}s) - {display_prompt}"
64
- audio_options.append((display_text, audio_id, audio))
65
-
66
- if not audio_options:
67
- return "❌ No valid audio tracks found", []
68
-
69
- # Format output message
70
- output = f"✅ **Task Found!**\n"
71
- output += f"**Task ID:** `{task_id}`\n"
72
- output += f"**Status:** {status}\n"
73
- output += f"**Found {len(audio_options)} audio track(s):**\n\n"
74
-
75
- for i, (display_text, audio_id, audio) in enumerate(audio_options):
76
- output += f"**Track {i+1}:**\n"
77
- output += f"- **ID:** `{audio_id}`\n"
78
- output += f"- **Title:** {audio.get('title', 'Untitled')}\n"
79
- output += f"- **Prompt:** {audio.get('prompt', 'No prompt')[:100]}...\n"
80
- output += f"- **Duration:** {audio.get('duration', 0):.1f}s\n"
81
- if audio.get('audioUrl'):
82
- output += f"- **Audio:** [Listen]({audio.get('audioUrl')})\n"
83
- output += "\n"
84
-
85
- output += "👇 **Select a track from the dropdown below to separate stems**"
86
-
87
- return output, audio_options
88
-
89
- except Exception as e:
90
- return f"❌ Error: {str(e)}", []
91
-
92
  def submit_separation_task(task_id, audio_id, separation_type):
93
- """Submit stem separation task and return task ID"""
94
  try:
95
- # Generate a unique callback URL for this request
96
- callback_id = str(uuid.uuid4())
97
- callback_url = f"https://1hit.no/callback.php?callback_id={callback_id}"
98
-
99
  resp = requests.post(
100
  "https://api.sunoapi.org/api/v1/vocal-removal/generate",
101
  json={
102
  "taskId": task_id,
103
  "audioId": audio_id,
104
  "type": separation_type,
105
- "callBackUrl": callback_url # Using your callback endpoint
106
  },
107
  headers={
108
  "Authorization": f"Bearer {SUNO_KEY}",
@@ -111,376 +27,241 @@ def submit_separation_task(task_id, audio_id, separation_type):
111
  timeout=30
112
  )
113
 
114
- print(f"Separation request response: {resp.status_code}")
115
- print(f"Response text: {resp.text}")
116
-
117
- if resp.status_code != 200:
118
- return None, f"❌ Submission failed: HTTP {resp.status_code}"
119
-
120
- data = resp.json()
121
- print(f"Parsed response: {data}")
122
-
123
- # Check for different response formats
124
- if data.get("code") == 200:
125
- # New format with data object
126
- separation_task_id = data.get("data", {}).get("taskId")
127
- music_id = data.get("data", {}).get("musicId")
128
 
129
- if separation_task_id:
130
- # Store task info for polling
131
- separation_tasks[separation_task_id] = {
132
- "task_id": task_id,
133
- "audio_id": audio_id,
134
- "separation_type": separation_type,
135
- "submitted_at": datetime.now().isoformat(),
136
- "callback_id": callback_id,
137
- "music_id": music_id
138
- }
139
-
140
- return separation_task_id, None
141
  else:
142
- return None, " No taskId in response"
143
-
144
- # Try alternative response format
145
- elif "taskId" in data:
146
- separation_task_id = data.get("taskId")
147
- music_id = data.get("musicId")
148
-
149
- separation_tasks[separation_task_id] = {
150
- "task_id": task_id,
151
- "audio_id": audio_id,
152
- "separation_type": separation_type,
153
- "submitted_at": datetime.now().isoformat(),
154
- "callback_id": callback_id,
155
- "music_id": music_id
156
- }
157
-
158
- return separation_task_id, None
159
-
160
  else:
161
- error_msg = data.get("msg", data.get("error", "Unknown error"))
162
- return None, f"❌ API error: {error_msg}"
163
 
164
  except Exception as e:
165
- return None, f"Error submitting task: {str(e)}"
166
 
167
- def poll_separation_result(separation_task_id):
168
- """Poll for separation results using the vocal-removal/record-info endpoint"""
169
  try:
170
- check = requests.get(
171
  "https://api.sunoapi.org/api/v1/vocal-removal/record-info",
172
  headers={"Authorization": f"Bearer {SUNO_KEY}"},
173
- params={"taskId": separation_task_id},
174
  timeout=30
175
  )
176
 
177
- if check.status_code == 200:
178
- data = check.json()
179
- print(f"Polling response: {data}")
180
 
181
- if data.get("code") == 200:
182
- status = data.get("data", {}).get("status", "PENDING")
183
- return status, data.get("data", {})
184
- else:
185
- return "ERROR", {"error": data.get("msg", "Unknown error")}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  else:
187
- return "ERROR", {"error": f"HTTP {check.status_code}"}
188
 
189
  except Exception as e:
190
- return "ERROR", {"error": str(e)}
191
 
192
- def separate_vocals(task_id, audio_id, separation_type):
193
- """Separate vocals and instruments from Suno tracks"""
194
- if not SUNO_KEY:
195
- yield " Error: SunoKey not configured in environment variables"
196
- return
197
 
198
- if not task_id.strip() or not audio_id.strip():
199
- yield "❌ Error: Please enter both Task ID and Audio ID"
200
- return
 
 
201
 
202
- # Validate separation type
203
- if separation_type not in ["separate_vocal", "split_stem"]:
204
- yield "❌ Error: Invalid separation type"
205
- return
206
-
207
- # Step 1: Submit separation task
208
- yield "⏳ **Submitting separation request...**\n"
209
-
210
- separation_task_id, error = submit_separation_task(task_id, audio_id, separation_type)
211
 
212
- if error:
213
- yield error
214
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- if not separation_task_id:
217
- yield "❌ Failed to get separation task ID"
218
- return
 
 
 
 
 
 
 
 
219
 
220
- # Store the separation task ID
221
- separation_task_info = separation_tasks.get(separation_task_id, {})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- yield f"✅ **Separation Task Submitted!**\n\n"
224
- yield f"**Separation Task ID:** `{separation_task_id}`\n"
225
- if separation_task_info.get("music_id"):
226
- yield f"**Music ID:** `{separation_task_info.get('music_id')}`\n"
227
- yield f"**Callback URL:** `{separation_task_info.get('callback_url', 'https://1hit.no/callback.php')}`\n\n"
228
- yield "⏳ **Processing separation (this may take 1-3 minutes)...**\n"
229
- yield "_Polling API for results..._\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
- # Step 2: Poll for results
232
- for attempt in range(60): # 60 attempts * 5 seconds = 5 minutes max
233
- time.sleep(5)
 
234
 
235
- status, result_data = poll_separation_result(separation_task_id)
 
 
 
236
 
237
  if status == "SUCCESS":
238
- # Parse successful result
239
- separation_info = result_data.get("vocal_removal_info", {})
240
-
241
- if not separation_info:
242
- # Try alternative field names
243
- separation_info = result_data
244
-
245
- if not separation_info:
246
- yield "✅ Separation completed but no download links found in response\n"
247
- yield f"Check your callback endpoint: {separation_task_info.get('callback_url')}"
248
- return
249
-
250
- # Format output based on separation type
251
- output = "🎵 **Separation Complete!** 🎵\n\n"
252
- output += f"**Separation Task ID:** `{separation_task_id}`\n"
253
- output += f"**Original Task ID:** `{task_id}`\n"
254
- output += f"**Audio ID:** `{audio_id}`\n\n"
255
-
256
- if separation_type == "separate_vocal":
257
- output += "## 🎤 2-Stem Separation Results\n\n"
258
-
259
- # Try different possible field names
260
- vocal_url = (separation_info.get('vocal_url') or
261
- separation_info.get('vocal') or
262
- separation_info.get('vocals_url'))
263
- instrumental_url = (separation_info.get('instrumental_url') or
264
- separation_info.get('instrumental') or
265
- separation_info.get('accompaniment_url'))
266
- origin_url = separation_info.get('origin_url') or separation_info.get('original_url')
267
 
268
- if vocal_url:
269
- output += f"**🎤 Vocals:** [Download MP3]({vocal_url})\n"
270
- if instrumental_url:
271
- output += f"**🎵 Instrumental:** [Download MP3]({instrumental_url})\n"
272
- if origin_url:
273
- output += f"**📁 Original:** [Download MP3]({origin_url})\n"
274
 
275
- elif separation_type == "split_stem":
276
- output += "## 🎛️ 12-Stem Separation Results\n\n"
277
 
278
- # Map of possible stem field names
279
- stem_fields = {
280
- "Vocals": ["vocal_url", "vocals", "vocal", "vocals_url"],
281
- "Backing Vocals": ["backing_vocals_url", "backing_vocals", "backing"],
282
- "Drums": ["drums_url", "drums", "drum"],
283
- "Bass": ["bass_url", "bass"],
284
- "Guitar": ["guitar_url", "guitar"],
285
- "Keyboard": ["keyboard_url", "keyboard", "piano"],
286
- "Strings": ["strings_url", "strings"],
287
- "Brass": ["brass_url", "brass"],
288
- "Woodwinds": ["woodwinds_url", "woodwinds"],
289
- "Percussion": ["percussion_url", "percussion"],
290
- "Synth": ["synth_url", "synth", "synthesizer"],
291
- "FX/Other": ["fx_url", "fx", "other", "effects"],
292
- "Instrumental": ["instrumental_url", "instrumental", "accompaniment_url"],
293
- "Original": ["origin_url", "original_url", "original"]
294
- }
295
 
296
- found_stems = 0
297
- for stem_name, possible_fields in stem_fields.items():
298
- url = None
299
- for field in possible_fields:
300
- if field in separation_info and separation_info[field]:
301
- url = separation_info[field]
302
- break
303
-
304
- if url:
305
- found_stems += 1
306
- output += f"**{stem_name}:** [Download MP3]({url})\n"
307
 
308
- if found_stems == 0:
309
- # If no stems found, show raw data for debugging
310
- output += "⚠️ **No stem URLs found in response. Raw data:**\n"
311
- output += f"```json\n{json.dumps(separation_info, indent=2)}\n```\n"
312
-
313
- output += f"\n⏱️ **Processing time:** {(attempt + 1) * 5} seconds\n"
314
- output += "⚠️ **Note:** Download links may expire after some time\n"
315
- output += f"📋 **Callback ID:** `{separation_task_info.get('callback_id', 'N/A')}`\n"
316
 
317
- yield output
318
- return
319
 
 
 
 
320
  elif status == "FAILED":
321
- error_msg = result_data.get("error", result_data.get("errorMessage", "Unknown error"))
322
- yield f"**Separation failed:** {error_msg}\n"
323
- yield f"**Task ID:** `{separation_task_id}`\n"
324
- return
325
-
326
- elif status == "ERROR":
327
- error_msg = result_data.get("error", "Unknown error")
328
- yield f"⚠️ **Polling error:** {error_msg}\n"
329
- yield f"**Task ID:** `{separation_task_id}`\n"
330
- # Continue polling despite error
331
- else:
332
- # PENDING or PROCESSING
333
- yield (f"⏳ **Status:** {status}\n"
334
- f"**Attempt:** {attempt + 1}/60\n"
335
- f"**Task ID:** `{separation_task_id}`\n\n"
336
- f"_Still processing... (usually takes 1-3 minutes)_\n")
337
-
338
- yield "⏰ **Timeout after 5 minutes.**\n"
339
- yield f"The separation task is still processing.\n"
340
- yield f"**Task ID:** `{separation_task_id}`\n"
341
- yield f"**Music ID:** `{separation_task_info.get('music_id', 'N/A')}`\n"
342
- yield "You can check the status later using this Task ID.\n"
343
- yield f"Or check your callback endpoint: {separation_task_info.get('callback_url')}"
344
-
345
- # Create the app
346
- with gr.Blocks(title="Suno Stem Separator", theme="soft") as app:
347
- gr.Markdown("# 🎵 Suno Stem Separator")
348
- gr.Markdown("Separate Suno AI tracks into vocal and instrument stems")
349
-
350
- with gr.Row():
351
- with gr.Column(scale=1):
352
- # Step 1: Enter Task ID
353
- task_id_input = gr.Textbox(
354
- label="Original Task ID",
355
- placeholder="Example: 5c79****be8e",
356
- info="Enter your Suno generation task ID",
357
- elem_id="task_id_input"
358
- )
359
-
360
- get_audio_btn = gr.Button("🔍 Find Audio Tracks", variant="secondary")
361
 
362
- # Will be updated after getting audio info
363
- audio_dropdown = gr.Dropdown(
364
- label="Select Audio Track to Separate",
365
- choices=[],
366
- info="Choose which audio track to process",
367
- visible=False,
368
- interactive=True
369
- )
370
 
371
- separation_type = gr.Radio(
372
- label="Separation Type",
373
- choices=[
374
- ("separate_vocal (2 stems - 1 credit)", "separate_vocal"),
375
- ("split_stem (12 stems - 5 credits)", "split_stem")
376
- ],
377
- value="separate_vocal",
378
- info="Choose separation mode",
379
- visible=False
380
- )
381
-
382
- separate_btn = gr.Button("🎵 Separate Stems", variant="primary", visible=False)
383
-
384
- gr.Markdown("""
385
- ### 📋 Workflow:
386
- 1. **Enter Task ID** → Click "Find Audio Tracks"
387
- 2. **Select audio track** from dropdown
388
- 3. **Choose separation type**
389
- 4. **Click Separate Stems**
390
- 5. **Wait 1-3 minutes** for processing
391
- 6. **Get download links** for each stem
392
-
393
- ### 💡 Tips:
394
- - Task IDs are from your Suno generation history
395
- - Each separation consumes API credits
396
- - Links may expire after some time
397
- - Processing time varies based on audio length
398
 
399
- ### 🔍 Check Status:
400
- You'll receive a Separation Task ID that you can use to:
401
- - Check status via API
402
- - Receive callback on your endpoint
403
- - Retry if needed
404
- """)
405
-
406
- with gr.Column(scale=2):
407
- status_output = gr.Markdown(
408
- label="Status",
409
- value="### 👋 Welcome!\nEnter a Task ID above to get started..."
410
- )
411
 
412
- results_output = gr.Markdown(
413
- label="Separation Results",
414
- visible=False
415
- )
416
-
417
- gr.Markdown("---")
418
- gr.Markdown(
419
- """
420
- <div style="text-align: center; padding: 20px;">
421
- <p>Powered by <a href="https://suno.ai" target="_blank">Suno AI</a> •
422
- <a href="https://sunoapi.org" target="_blank">Suno API Docs</a> •
423
- <a href="https://docs.sunoapi.org/separate-vocals" target="_blank">Stem Separation Guide</a></p>
424
- <p><small>Track separation powered by Suno API. Processing may take 1-3 minutes.</small></p>
425
- </div>
426
- """,
427
- elem_id="footer"
428
- )
429
-
430
- # Step 1: Get audio info when button is clicked
431
- def on_get_audio(task_id):
432
- if not task_id:
433
- return (
434
- "❌ **Please enter a Task ID**",
435
- gr.Dropdown(choices=[], visible=False),
436
- gr.Radio(visible=False),
437
- gr.Button(visible=False),
438
- gr.Markdown(visible=False)
439
- )
440
-
441
- output, audio_options = get_audio_info(task_id)
442
-
443
- if not audio_options:
444
- # No audio found, show error
445
- return (
446
- output,
447
- gr.Dropdown(choices=[], visible=False),
448
- gr.Radio(visible=False),
449
- gr.Button(visible=False),
450
- gr.Markdown(visible=False)
451
- )
452
-
453
- # Create choices for dropdown
454
- choices = [(display_text, audio_id) for display_text, audio_id, _ in audio_options]
455
-
456
- return (
457
- output,
458
- gr.Dropdown(choices=choices, value=choices[0][1] if choices else None, visible=True),
459
- gr.Radio(visible=True),
460
- gr.Button(visible=True),
461
- gr.Markdown(visible=False)
462
- )
463
-
464
- # Step 2: Separate stems when button is clicked
465
- def on_separate_click(task_id, audio_id, separation_type):
466
- # Clear previous results
467
- yield gr.Markdown(value="", visible=False)
468
-
469
- # Start separation process
470
- for update in separate_vocals(task_id, audio_id, separation_type):
471
- yield gr.Markdown(value=update, visible=True)
472
 
473
- # Connect events
474
- get_audio_btn.click(
475
- fn=on_get_audio,
476
- inputs=[task_id_input],
477
- outputs=[status_output, audio_dropdown, separation_type, separate_btn, results_output]
478
  )
479
 
480
- separate_btn.click(
481
- fn=on_separate_click,
482
- inputs=[task_id_input, audio_dropdown, separation_type],
483
- outputs=[results_output]
484
  )
485
 
486
  if __name__ == "__main__":
 
3
  import os
4
  import time
5
  import json
 
 
6
 
7
  # Suno API key
8
  SUNO_KEY = os.environ.get("SunoKey", "")
9
  if not SUNO_KEY:
10
  print("⚠️ SunoKey not set!")
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def submit_separation_task(task_id, audio_id, separation_type):
13
+ """Submit stem separation task - SIMPLE VERSION"""
14
  try:
 
 
 
 
15
  resp = requests.post(
16
  "https://api.sunoapi.org/api/v1/vocal-removal/generate",
17
  json={
18
  "taskId": task_id,
19
  "audioId": audio_id,
20
  "type": separation_type,
21
+ "callBackUrl": "https://1hit.no/callback.php"
22
  },
23
  headers={
24
  "Authorization": f"Bearer {SUNO_KEY}",
 
27
  timeout=30
28
  )
29
 
30
+ if resp.status_code == 200:
31
+ data = resp.json()
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ # Try different response formats
34
+ if "taskId" in data:
35
+ return data["taskId"], None # New format
36
+ elif data.get("code") == 200 and "data" in data and "taskId" in data["data"]:
37
+ return data["data"]["taskId"], None # Nested format
 
 
 
 
 
 
 
38
  else:
39
+ return None, f"Unexpected response format: {data}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  else:
41
+ return None, f"HTTP Error: {resp.status_code}"
 
42
 
43
  except Exception as e:
44
+ return None, f"Error: {str(e)}"
45
 
46
+ def poll_task_status(task_id):
47
+ """Poll separation task status - SIMPLE VERSION"""
48
  try:
49
+ resp = requests.get(
50
  "https://api.sunoapi.org/api/v1/vocal-removal/record-info",
51
  headers={"Authorization": f"Bearer {SUNO_KEY}"},
52
+ params={"taskId": task_id},
53
  timeout=30
54
  )
55
 
56
+ if resp.status_code == 200:
57
+ data = resp.json()
 
58
 
59
+ # Simple status extraction
60
+ status = "UNKNOWN"
61
+ if "status" in data:
62
+ status = data["status"]
63
+ elif "data" in data and "status" in data["data"]:
64
+ status = data["data"]["status"]
65
+ elif data.get("code") == 200:
66
+ status = "SUCCESS" # Assume success if code is 200
67
+
68
+ # Simple result extraction
69
+ results = {}
70
+ if "vocal_removal_info" in data:
71
+ results = data["vocal_removal_info"]
72
+ elif "data" in data and "vocal_removal_info" in data["data"]:
73
+ results = data["data"]["vocal_removal_info"]
74
+ elif data.get("code") == 200 and "data" in data:
75
+ # Maybe results are directly in data
76
+ results = {k: v for k, v in data["data"].items() if k.endswith("_url")}
77
+
78
+ return status, results, None
79
  else:
80
+ return "ERROR", {}, f"HTTP {resp.status_code}"
81
 
82
  except Exception as e:
83
+ return "ERROR", {}, str(e)
84
 
85
+ def format_results(results):
86
+ """Format results as simple markdown"""
87
+ if not results:
88
+ return "No results found"
 
89
 
90
+ output = "## 🎵 Download Links\n\n"
91
+ for key, url in results.items():
92
+ if url and "url" in key.lower():
93
+ stem_name = key.replace("_url", "").replace("_", " ").title()
94
+ output += f"**{stem_name}:** [Download]({url})\n"
95
 
96
+ return output
97
+
98
+ # Create the app
99
+ with gr.Blocks(title="Suno Stem Separator", theme="soft") as app:
100
+ gr.Markdown("# 🎵 Suno Stem Separator")
101
+ gr.Markdown("Separate Suno tracks into stems")
 
 
 
102
 
103
+ with gr.Tab("Submit New Task"):
104
+ with gr.Row():
105
+ with gr.Column():
106
+ task_id_input = gr.Textbox(
107
+ label="Original Task ID",
108
+ placeholder="From your Suno generation"
109
+ )
110
+ audio_id_input = gr.Textbox(
111
+ label="Audio ID",
112
+ placeholder="From your Suno generation"
113
+ )
114
+ separation_type = gr.Radio(
115
+ choices=["separate_vocal", "split_stem"],
116
+ label="Separation Type",
117
+ value="separate_vocal"
118
+ )
119
+ submit_btn = gr.Button("Submit Separation", variant="primary")
120
+
121
+ with gr.Column():
122
+ task_output = gr.Markdown("## Task Status")
123
 
124
+ with gr.Tab("Poll Existing Task"):
125
+ with gr.Row():
126
+ with gr.Column():
127
+ poll_task_id = gr.Textbox(
128
+ label="Separation Task ID",
129
+ placeholder="Enter task ID to poll"
130
+ )
131
+ poll_btn = gr.Button("Check Status", variant="secondary")
132
+
133
+ with gr.Column():
134
+ poll_output = gr.Markdown("## Poll Results")
135
 
136
+ with gr.Tab("Instructions"):
137
+ gr.Markdown("""
138
+ ## 📋 How to Use
139
+
140
+ 1. **Get Task ID & Audio ID:**
141
+ - Go to your Suno generation history
142
+ - Copy the Task ID and Audio ID
143
+
144
+ 2. **Submit Separation:**
145
+ - Enter Task ID and Audio ID
146
+ - Choose separation type
147
+ - Click "Submit Separation"
148
+ - You'll get a new Separation Task ID
149
+
150
+ 3. **Get Results:**
151
+ - Wait 1-3 minutes
152
+ - Use the Poll tab to check status
153
+ - OR wait for callback to https://1hit.no/callback.php
154
+
155
+ 4. **View Results:**
156
+ - Visit: https://1hit.no/viewer.php?task_id=YOUR_TASK_ID
157
+
158
+ ## ⚠️ Notes
159
+ - Processing takes 1-3 minutes
160
+ - Each separation uses credits
161
+ - Links expire after some time
162
+ """)
163
 
164
+ # Submit task function
165
+ def on_submit(task_id, audio_id, sep_type):
166
+ if not task_id or not audio_id:
167
+ return " Please enter both Task ID and Audio ID"
168
+
169
+ result = submit_separation_task(task_id, audio_id, sep_type)
170
+
171
+ if result[1]: # Error
172
+ return f"❌ {result[1]}"
173
+
174
+ separation_task_id = result[0]
175
+
176
+ output = f"""
177
+ ## ✅ Task Submitted!
178
+
179
+ **Separation Task ID:** `{separation_task_id}`
180
+
181
+ **Next Steps:**
182
+ 1. Wait 1-3 minutes for processing
183
+ 2. Check status in the **Poll** tab using this Task ID
184
+ 3. Results will also be sent to callback endpoint
185
+
186
+ **Poll this task:** Use ID: `{separation_task_id}`
187
+ """
188
+
189
+ return output
190
 
191
+ # Poll task function
192
+ def on_poll(task_id):
193
+ if not task_id:
194
+ return "❌ Please enter a Task ID"
195
 
196
+ status, results, error = poll_task_status(task_id)
197
+
198
+ if error:
199
+ return f"❌ Error: {error}"
200
 
201
  if status == "SUCCESS":
202
+ if results:
203
+ return f"""
204
+ ## ✅ Separation Complete!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
+ **Status:** {status}
207
+ **Task ID:** `{task_id}`
 
 
 
 
208
 
209
+ {format_results(results)}
 
210
 
211
+ **Also check:** https://1hit.no/viewer.php?task_id={task_id}
212
+ """
213
+ else:
214
+ return f"""
215
+ ## Separation Complete!
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ **Status:** {status}
218
+ **Task ID:** `{task_id}`
 
 
 
 
 
 
 
 
 
219
 
220
+ No direct links found in response.
221
+ Check callback results: https://1hit.no/viewer.php?task_id={task_id}
222
+ """
223
+ elif status in ["PENDING", "PROCESSING", "RUNNING"]:
224
+ return f"""
225
+ ## Still Processing
 
 
226
 
227
+ **Status:** {status}
228
+ **Task ID:** `{task_id}`
229
 
230
+ Processing usually takes 1-3 minutes.
231
+ Check again in a minute.
232
+ """
233
  elif status == "FAILED":
234
+ return f"""
235
+ ## ❌ Separation Failed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ **Status:** {status}
238
+ **Task ID:** `{task_id}`
 
 
 
 
 
 
239
 
240
+ The separation task failed.
241
+ Please check your inputs and try again.
242
+ """
243
+ else:
244
+ return f"""
245
+ ## 🔄 Status Check
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ **Status:** {status}
248
+ **Task ID:** `{task_id}`
 
 
 
 
 
 
 
 
 
 
249
 
250
+ Current status: {status}
251
+ Keep checking every 30 seconds.
252
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
+ # Connect buttons
255
+ submit_btn.click(
256
+ fn=on_submit,
257
+ inputs=[task_id_input, audio_id_input, separation_type],
258
+ outputs=[task_output]
259
  )
260
 
261
+ poll_btn.click(
262
+ fn=on_poll,
263
+ inputs=[poll_task_id],
264
+ outputs=[poll_output]
265
  )
266
 
267
  if __name__ == "__main__":