MySafeCode commited on
Commit
4ca1338
·
verified ·
1 Parent(s): 85a0812

Update aa.py

Browse files
Files changed (1) hide show
  1. aa.py +191 -646
aa.py CHANGED
@@ -12,29 +12,25 @@ import tempfile
12
  # Suno API key
13
  SUNO_KEY = os.environ.get("SunoKey", "")
14
  # Secret key for ownership proofs
15
- SECRET_SALT = "Salt" # You might want to make this configurable
16
 
17
  if not SUNO_KEY:
18
  print("⚠️ SunoKey not set!")
19
 
20
- def generate_ownership_proof(task_id, title, music_id=None, custom_data=None):
21
  """
22
- Generate a SHA256 hash proof of ownership using the task_id as seed
23
- and "Salt" as the secret key.
24
  """
25
- # Create a unique string to hash
26
  proof_data = {
27
  "task_id": task_id,
28
  "title": title,
29
  "music_id": music_id,
30
  "timestamp": datetime.utcnow().isoformat(),
31
- "custom": custom_data or {}
32
  }
33
 
34
- # Convert to string for hashing
35
  proof_string = json.dumps(proof_data, sort_keys=True)
36
 
37
- # Create HMAC-SHA256 using the secret salt
38
  signature = hmac.new(
39
  SECRET_SALT.encode('utf-8'),
40
  proof_string.encode('utf-8'),
@@ -47,52 +43,44 @@ def generate_ownership_proof(task_id, title, music_id=None, custom_data=None):
47
  "version": "1.0"
48
  }
49
 
50
- def create_json_receipt(song_data, task_id, ownership_proof):
51
  """
52
- Create a JSON receipt for download
53
  """
 
 
54
  receipt = {
55
- "receipt_type": "song_ownership_proof",
56
  "generated": datetime.now().isoformat(),
57
  "task_id": task_id,
58
- "song": {
59
- "id": song_data.get('id', 'N/A'),
60
- "title": song_data.get('title', 'Untitled'),
61
- "duration": song_data.get('duration', 'N/A'),
62
- "created": song_data.get('createTime', 'N/A'),
63
- "style": song_data.get('style', 'N/A'),
64
- "prompt": song_data.get('prompt', 'N/A')[:200] if song_data.get('prompt') else 'N/A',
65
- "audio_url": song_data.get('audioUrl') or song_data.get('audio_url'),
66
- "download_url": song_data.get('downloadUrl') or song_data.get('download_url'),
67
- "stream_url": song_data.get('streamUrl') or song_data.get('stream_url')
68
- },
69
- "ownership_proof": ownership_proof,
70
- "verification_url": f"https://1hit.no/verify?task_id={task_id}&music_id={song_data.get('id', '')}"
71
  }
72
 
73
  return receipt
74
 
75
- def create_html_viewer(json_receipt):
76
  """
77
- Create an HTML viewer for the JSON receipt
78
  """
79
- receipt = json_receipt
80
- song = receipt['song']
81
  proof = receipt['ownership_proof']
82
- is_custom_title = song['title'] not in ["Generated Song", "Untitled"]
83
 
84
  html = f"""<!DOCTYPE html>
85
  <html lang="en">
86
  <head>
87
  <meta charset="UTF-8">
88
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
89
- <title>🎵 Song Ownership Receipt - {song['title']}</title>
90
  <style>
91
  body {{
92
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
93
  line-height: 1.6;
94
  color: #333;
95
- max-width: 1000px;
96
  margin: 0 auto;
97
  padding: 20px;
98
  background: #f5f5f5;
@@ -114,8 +102,8 @@ def create_html_viewer(json_receipt):
114
  margin: 0;
115
  font-size: 2.5em;
116
  }}
117
- .proof-badge {{
118
- background: {('#27ae60' if is_custom_title else '#95a5a6')};
119
  color: white;
120
  padding: 8px 15px;
121
  border-radius: 20px;
@@ -129,15 +117,9 @@ def create_html_viewer(json_receipt):
129
  padding: 20px;
130
  margin: 20px 0;
131
  }}
132
- .section h2 {{
133
- margin-top: 0;
134
- color: #2c3e50;
135
- border-bottom: 1px solid #dee2e6;
136
- padding-bottom: 10px;
137
- }}
138
  .info-grid {{
139
  display: grid;
140
- grid-template-columns: 150px 1fr;
141
  gap: 10px;
142
  margin: 15px 0;
143
  }}
@@ -155,16 +137,6 @@ def create_html_viewer(json_receipt):
155
  font-size: 0.9em;
156
  word-break: break-all;
157
  }}
158
- .json-view {{
159
- background: #1e1e1e;
160
- color: #d4d4d4;
161
- padding: 20px;
162
- border-radius: 5px;
163
- overflow-x: auto;
164
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
165
- font-size: 0.9em;
166
- line-height: 1.4;
167
- }}
168
  .button {{
169
  display: inline-block;
170
  background: #3498db;
@@ -172,7 +144,7 @@ def create_html_viewer(json_receipt):
172
  text-decoration: none;
173
  padding: 10px 20px;
174
  border-radius: 5px;
175
- margin-right: 10px;
176
  border: none;
177
  cursor: pointer;
178
  font-size: 14px;
@@ -180,22 +152,9 @@ def create_html_viewer(json_receipt):
180
  .button:hover {{
181
  background: #2980b9;
182
  }}
183
- .button-group {{
184
- margin: 20px 0;
185
- text-align: center;
186
- }}
187
- .download-links a {{
188
- display: inline-block;
189
- background: #27ae60;
190
- color: white;
191
- text-decoration: none;
192
- padding: 8px 15px;
193
- border-radius: 5px;
194
- margin-right: 10px;
195
- }}
196
  .warning {{
197
- background: #fef9e7;
198
- border-left: 4px solid #f39c12;
199
  padding: 15px;
200
  margin: 20px 0;
201
  }}
@@ -204,50 +163,40 @@ def create_html_viewer(json_receipt):
204
  <body>
205
  <div class="receipt">
206
  <div class="header">
207
- <h1>🎵 Song Ownership Receipt</h1>
208
- <div class="proof-badge">
209
- {('🔒 Verified Ownership - Custom Title' if is_custom_title else '⚠️ Default Title - No Proof')}
210
- </div>
211
  <p>Generated: {receipt['generated']}</p>
212
- <p><strong>Task ID:</strong> <code>{receipt['task_id']}</code></p>
 
 
 
 
 
213
  </div>
214
 
215
  <div class="section">
216
- <h2>📋 Song Information</h2>
217
  <div class="info-grid">
218
  <span class="info-label">Title:</span>
219
- <span><strong>{song['title']}</strong></span>
220
-
221
- <span class="info-label">Music ID:</span>
222
- <span><code>{song['id']}</code></span>
223
 
224
- <span class="info-label">Duration:</span>
225
- <span>{song['duration']}s</span>
226
 
227
- <span class="info-label">Created:</span>
228
- <span>{song['created']}</span>
229
-
230
- <span class="info-label">Style:</span>
231
- <span>{song['style']}</span>
232
- </div>
233
-
234
- {f'''
235
- <div class="download-links">
236
- <a href="{song['audio_url']}" target="_blank">🎧 Stream Audio</a>
237
- <a href="{song['download_url']}" target="_blank">📥 Download MP3</a>
238
  </div>
239
- ''' if song.get('audio_url') or song.get('download_url') else ''}
240
  </div>
241
 
242
- {f'''
243
  <div class="section">
244
- <h2>🔐 Cryptographic Proof</h2>
 
245
  <div class="info-grid">
246
  <span class="info-label">Proof Version:</span>
247
  <span>{proof['version']}</span>
248
 
249
  <span class="info-label">Algorithm:</span>
250
- <span>HMAC-SHA256 with secret salt</span>
251
  </div>
252
 
253
  <h3>Proof Hash:</h3>
@@ -256,32 +205,25 @@ def create_html_viewer(json_receipt):
256
  </div>
257
 
258
  <h3>Signed Data:</h3>
259
- <pre class="json-view">{json.dumps(proof['data'], indent=2)}</pre>
260
  </div>
261
 
262
  <div class="section">
263
- <h2>🔍 Verification</h2>
264
- <p>To verify this proof, use the following URL:</p>
265
- <div class="proof-hash" style="background: #3498db;">
266
- <a href="{receipt['verification_url']}" style="color: white; text-decoration: none;">{receipt['verification_url']}</a>
267
  </div>
268
- <p><small>Keep this receipt safe. Only you have this proof of ownership.</small></p>
269
- </div>
270
- ''' if is_custom_title else '''
271
- <div class="warning">
272
- <h3>⚠️ No Ownership Proof Generated</h3>
273
- <p>This song uses a default title. To get a cryptographic ownership proof, generate a song with a custom title.</p>
274
  </div>
275
- '''}
276
 
277
- <div class="button-group">
278
  <button class="button" onclick="window.print()">🖨️ Print Receipt</button>
279
  <button class="button" onclick="downloadJSON()">📥 Download JSON</button>
280
  </div>
281
 
282
  <div class="footer" style="text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #7f8c8d;">
283
- <p>📱 This receipt proves you generated this song on {receipt['generated'].split('T')[0]}</p>
284
- <p><small>Receipt Version 1.0 Anonymous Ownership System</small></p>
285
  </div>
286
  </div>
287
 
@@ -291,7 +233,7 @@ def create_html_viewer(json_receipt):
291
  const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(receipt, null, 2));
292
  const downloadAnchor = document.createElement('a');
293
  downloadAnchor.setAttribute("href", dataStr);
294
- downloadAnchor.setAttribute("download", "receipt_{song['id']}.json");
295
  document.body.appendChild(downloadAnchor);
296
  downloadAnchor.click();
297
  downloadAnchor.remove();
@@ -302,122 +244,21 @@ def create_html_viewer(json_receipt):
302
 
303
  return html
304
 
305
- def extract_songs_from_response(data):
306
- """
307
- Properly extract songs data from the API response
308
- Returns a list of song dictionaries
309
- """
310
- songs = []
311
-
312
- try:
313
- if data.get("code") == 200:
314
- task_data = data.get("data", {})
315
-
316
- if task_data.get("status") == "SUCCESS":
317
- response_data = task_data.get("response", {})
318
-
319
- # Handle different response formats
320
- if isinstance(response_data, str):
321
- try:
322
- response_data = json.loads(response_data)
323
- except:
324
- response_data = {}
325
-
326
- # Extract songs from various possible locations
327
- if isinstance(response_data, dict):
328
- songs = response_data.get("sunoData", [])
329
- if not songs:
330
- songs = response_data.get("data", [])
331
- elif isinstance(response_data, list):
332
- songs = response_data
333
-
334
- # Add task_id and other metadata to each song
335
- for song in songs:
336
- if isinstance(song, dict):
337
- song['taskId'] = task_data.get('taskId')
338
- song['status'] = task_data.get('status')
339
-
340
- return songs
341
- except Exception as e:
342
- print(f"Error extracting songs: {e}")
343
- return []
344
-
345
- def create_receipt_files(task_id, songs):
346
- """
347
- Create receipt files for all songs in a task
348
- Returns a list of file paths
349
- """
350
- receipt_files = []
351
-
352
- for i, song in enumerate(songs, 1):
353
- if song.get('title') not in ["Generated Song", "Untitled"]:
354
- # Generate proof
355
- proof = generate_ownership_proof(
356
- task_id,
357
- song.get('title'),
358
- song.get('id')
359
- )
360
-
361
- # Create JSON receipt
362
- json_receipt = create_json_receipt(song, task_id, proof)
363
-
364
- # Create HTML viewer
365
- html_viewer = create_html_viewer(json_receipt)
366
-
367
- # Create temporary files
368
- title_clean = song.get('title', 'song').replace(' ', '_').replace('/', '_')[:30]
369
- music_id = song.get('id', 'unknown')[:8]
370
-
371
- # JSON file
372
- json_path = tempfile.NamedTemporaryFile(
373
- mode='w',
374
- suffix=f'_{title_clean}_{music_id}.json',
375
- delete=False,
376
- encoding='utf-8'
377
- ).name
378
- with open(json_path, 'w', encoding='utf-8') as f:
379
- json.dump(json_receipt, f, indent=2)
380
-
381
- # HTML file
382
- html_path = tempfile.NamedTemporaryFile(
383
- mode='w',
384
- suffix=f'_{title_clean}_{music_id}.html',
385
- delete=False,
386
- encoding='utf-8'
387
- ).name
388
- with open(html_path, 'w', encoding='utf-8') as f:
389
- f.write(html_viewer)
390
-
391
- receipt_files.append({
392
- 'song_index': i,
393
- 'title': song.get('title'),
394
- 'json_path': json_path,
395
- 'html_path': html_path,
396
- 'music_id': music_id
397
- })
398
-
399
- return receipt_files
400
-
401
  def generate_song_from_text(lyrics_text, style, title, instrumental, model):
402
- """Generate a song from lyrics text"""
403
  if not SUNO_KEY:
404
- yield "❌ Error: SunoKey not configured in environment variables"
405
- return
406
-
407
- if not lyrics_text.strip() and not instrumental:
408
- yield "❌ Error: Please provide lyrics or select instrumental"
409
- return
410
-
411
- if not style.strip():
412
- yield "❌ Error: Please provide a music style"
413
  return
414
 
415
- if not title.strip():
416
- yield " Error: Please provide a song title"
417
- return
 
 
 
418
 
419
  try:
420
- # Prepare request data
421
  request_data = {
422
  "customMode": True,
423
  "instrumental": instrumental,
@@ -428,489 +269,193 @@ def generate_song_from_text(lyrics_text, style, title, instrumental, model):
428
  }
429
 
430
  if not instrumental:
431
- # Apply character limits
432
- if model == "V4" and len(lyrics_text) > 3000:
433
- lyrics_text = lyrics_text[:3000]
434
- yield f"⚠️ Lyrics truncated to 3000 characters for V4 model\n\n"
435
- elif model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"] and len(lyrics_text) > 5000:
436
- lyrics_text = lyrics_text[:5000]
437
- yield f"⚠️ Lyrics truncated to 5000 characters for {model} model\n\n"
438
-
439
  request_data["prompt"] = lyrics_text
440
  else:
441
  request_data["prompt"] = ""
442
 
443
- # Apply style length limits
444
- if model == "V4" and len(style) > 200:
445
- style = style[:200]
446
- yield f"⚠️ Style truncated to 200 characters for V4 model\n\n"
447
- elif model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"] and len(style) > 1000:
448
- style = style[:1000]
449
- yield f"⚠️ Style truncated to 1000 characters for {model} model\n\n"
 
 
 
 
450
 
451
- # Apply title length limits
452
- if model in ["V4", "V4_5ALL"] and len(title) > 80:
453
- title = title[:80]
454
- yield f"⚠️ Title truncated to 80 characters for {model} model\n\n"
455
- elif model in ["V4_5", "V4_5PLUS", "V5"] and len(title) > 100:
456
- title = title[:100]
457
- yield f"⚠️ Title truncated to 100 characters for {model} model\n\n"
458
 
459
- request_data["style"] = style
460
- request_data["title"] = title
 
 
 
 
461
 
462
- yield f"## 🚀 Submitting Song Request\n\n"
463
- yield f"**Title:** {title}\n"
464
- yield f"**Style:** {style}\n"
465
- yield f"**Model:** {model}\n"
466
- yield f"**Instrumental:** {'Yes' if instrumental else 'No'}\n"
467
- if not instrumental:
468
- yield f"**Lyrics length:** {len(lyrics_text)} characters\n\n"
469
- yield f"**Callback URL:** https://1hit.no/callback.php\n\n"
470
 
471
- # Submit generation request
472
- try:
473
- resp = requests.post(
474
- "https://api.sunoapi.org/api/v1/generate",
475
- json=request_data,
476
- headers={
477
- "Authorization": f"Bearer {SUNO_KEY}",
478
- "Content-Type": "application/json"
479
- },
480
- timeout=30
481
- )
482
-
483
- if resp.status_code != 200:
484
- yield f"❌ Submission failed: HTTP {resp.status_code}"
485
- yield f"\n**Response:**\n```\n{resp.text}\n```"
486
- return
487
-
488
- data = resp.json()
489
- print(f"Submission response: {json.dumps(data, indent=2)}")
490
-
491
- if data.get("code") != 200:
492
- yield f"❌ API error: {data.get('msg', 'Unknown')}"
493
- return
494
-
495
- # Extract task ID from response
496
- task_id = None
497
- if "taskId" in data:
498
- task_id = data["taskId"]
499
- elif "data" in data and "taskId" in data["data"]:
500
- task_id = data["data"]["taskId"]
501
-
502
- if not task_id:
503
- yield f"❌ Could not extract Task ID from response"
504
- yield f"\n**Raw Response:**\n```json\n{json.dumps(data, indent=2)}\n```"
505
- return
506
-
507
- yield f"## ✅ Request Submitted Successfully!\n\n"
508
- yield f"**🎯 Task ID:** `{task_id}`\n\n"
509
- yield f"**⏳ Status:** Generation started\n"
510
- yield f"**📞 Callback:** https://1hit.no/callback.php\n\n"
511
- yield "**What happens now:**\n"
512
- yield "1. Suno AI generates your song (1-3 minutes)\n"
513
- yield "2. I'll check status in 30 seconds and offer your receipt if successful\n\n"
514
 
515
- # Wait and check
516
- yield "**⏰ Checking status in 30 seconds...**\n"
517
- time.sleep(30)
 
 
518
 
519
- # Check status
520
- status_result, songs = get_task_info(task_id)
 
 
 
521
 
522
- if songs:
523
- yield "\n## ✅ Generation Complete!\n\n"
524
-
525
- # Show songs
526
- for i, song in enumerate(songs, 1):
527
- title = song.get('title', 'Untitled')
528
- is_custom = title not in ["Generated Song", "Untitled"]
529
-
530
- yield f"### {'🔒' if is_custom else '⚠️'} Song {i}: {title}\n"
531
-
532
- # Audio URLs
533
- audio_url = song.get('audioUrl') or song.get('audio_url')
534
- download_url = song.get('downloadUrl') or song.get('download_url')
535
-
536
- if audio_url:
537
- yield f"**Audio:** [Play]({audio_url})\n"
538
- if download_url:
539
- yield f"**Download:** [MP3]({download_url})\n"
540
-
541
- # Audio player
542
- if audio_url:
543
- yield f"""\n<audio controls style="width: 100%; margin: 10px 0;">
544
- <source src="{audio_url}" type="audio/mpeg">
545
- Your browser does not support audio.
546
- </audio>\n"""
547
-
548
- yield "\n"
549
-
550
- # Create receipts for custom titles
551
- custom_songs = [s for s in songs if s.get('title') not in ["Generated Song", "Untitled"]]
552
-
553
- if custom_songs:
554
- yield "## 📦 Your Ownership Receipts\n\n"
555
- yield "Download these receipts to prove you created these songs:\n\n"
556
-
557
- # Create receipt files
558
- receipt_files = create_receipt_files(task_id, songs)
559
-
560
- for receipt in receipt_files:
561
- yield f"### 🎵 {receipt['title']}\n"
562
- yield f"**Music ID:** `{receipt['music_id']}`\n"
563
- yield f"- [📥 Download JSON Receipt]({receipt['json_path']})\n"
564
- yield f"- [🌐 Download HTML Viewer]({receipt['html_path']})\n\n"
565
- yield "---\n\n"
566
-
567
- yield """
568
- **🔐 What's in your receipt:**
569
- - Cryptographic proof (HMAC-SHA256)
570
- - Your song metadata
571
- - Task ID as seed
572
- - Timestamp of generation
573
-
574
- **💾 Save these receipts!** They are your only proof of ownership.
575
- """
576
- else:
577
- yield "\n⚠️ No receipts generated - song uses default title.\n"
578
- yield "To get ownership proofs, use a custom title next time.\n"
579
- else:
580
- yield f"\n## 📊 Status Update\n\n"
581
- yield status_result
582
-
583
- except Exception as e:
584
- yield f"❌ Error submitting request: {str(e)}"
585
- return
586
-
587
- except Exception as e:
588
- yield f"❌ **Unexpected Error:** {str(e)}"
589
 
590
- def get_task_info(task_id):
591
- """Get task info but don't generate receipts (kept for backward compatibility)"""
592
- if not task_id:
593
- return "❌ Please enter a Task ID", []
594
-
595
- try:
596
- resp = requests.get(
597
- "https://api.sunoapi.org/api/v1/generate/record-info",
598
- headers={"Authorization": f"Bearer {SUNO_KEY}"},
599
- params={"taskId": task_id},
600
- timeout=30
601
- )
602
-
603
- if resp.status_code != 200:
604
- return f"❌ HTTP Error {resp.status_code}\n\n{resp.text}", []
 
 
 
 
 
 
605
 
606
- data = resp.json()
607
- songs = extract_songs_from_response(data)
608
 
609
- # Format basic output
610
- output = f"## 🔍 Task Status: `{task_id}`\n\n"
611
 
612
- if data.get("code") == 200:
613
- task_data = data.get("data", {})
614
- status = task_data.get("status", "UNKNOWN")
615
-
616
- output += f"**Status:** {status}\n"
617
- output += f"**Task ID:** `{task_data.get('taskId', 'N/A')}`\n"
618
 
619
- if status == "SUCCESS" and songs:
620
- output += f"\n## 🎵 Generated Songs ({len(songs)})\n\n"
621
- for i, song in enumerate(songs, 1):
622
- title = song.get('title', 'Untitled')
623
- output += f"### Song {i}: {title}\n"
624
- output += f"**ID:** `{song.get('id', 'N/A')}`\n"
625
-
626
- # Audio URLs
627
- audio_url = song.get('audioUrl') or song.get('audio_url')
628
- if audio_url:
629
- output += f"**Audio:** [Play]({audio_url})\n"
630
-
631
- output += "---\n\n"
632
- else:
633
- output += f"\n**Status details:** {status}\n"
 
 
 
 
 
 
 
634
 
635
- return output, songs
636
 
637
  except Exception as e:
638
- return f"❌ Error checking task: {str(e)}", []
639
-
640
- def parse_url_params(request: gr.Request):
641
- """Parse taskid from URL parameters"""
642
- task_id = None
643
- if request:
644
- try:
645
- query_params = parse_qs(urlparse(request.request.url).query)
646
- if 'taskid' in query_params:
647
- task_id = query_params['taskid'][0]
648
- task_id = task_id.strip()
649
- except Exception as e:
650
- print(f"Error parsing URL params: {e}")
651
-
652
- return task_id
653
 
654
- # Create the app
655
- with gr.Blocks(theme=gr.themes.Soft(), title="Suno Song Generator with Receipts") as app:
656
- gr.Markdown("# 🎵 Suno Song Generator with Anonymous Ownership Proofs")
657
- gr.Markdown("Create songs and get instant ownership receipts at generation time")
658
 
659
- with gr.TabItem("Audio Links & Info"):
660
- gr.HTML("""
661
- <h2>Quick Links</h2>
662
- <ul>
663
- <li><a href="https://1hit.no/gen/audio/images/patchfix.php" target="_blank">Image Cleanup Tool</a></li>
664
- <li><a href="https://1hit.no/gen/audio/mp3/" target="_blank">Audio Library</a></li>
665
- <li><a href="https://1hit.no/gen/xm3u.php" target="_blank">Download m3u (with titles)</a></li>
666
- <li><a href="https://1hit.no/gen/sm3u.php" target="_blank">Download m3u (with task IDs)</a></li>
667
- <li><a href="https://huggingface.co/datasets/MySafeCode/1hit.no-Music-Images/" target="_blank">HuggingFace Backup</a></li>
668
- </ul>
669
- <p><strong>Note:</strong> Receipts are only generated at creation time for custom titles.</p>
670
- """)
671
 
672
- with gr.Tab("🎶 Generate Song & Get Receipt", id="generate_tab"):
673
  with gr.Row():
674
  with gr.Column(scale=1):
675
- gr.Markdown("### Step 1: Enter Lyrics")
676
  lyrics_text = gr.Textbox(
677
  label="Lyrics",
678
  placeholder="Paste your lyrics here...",
679
- lines=10,
680
- interactive=True
681
  )
682
 
683
- gr.Markdown("### Step 2: Song Settings")
684
  style = gr.Textbox(
685
  label="Music Style",
686
- placeholder="Example: Pop, Rock, Jazz",
687
- value="Folk soul flamenco glam rock goa trance fusion",
688
  interactive=True
689
  )
690
 
691
  title = gr.Textbox(
692
  label="Song Title (CUSTOM TITLE = RECEIPT)",
693
- placeholder="My Awesome Song",
694
- value="Generated Song",
695
- info="Use a custom title to get an ownership receipt!",
696
  interactive=True
697
  )
698
 
699
  with gr.Row():
700
- instrumental = gr.Checkbox(
701
- label="Instrumental (No Vocals)",
702
- value=False,
703
- interactive=True
704
- )
705
  model = gr.Dropdown(
706
  label="Model",
707
- choices=["V5", "V4_5PLUS", "V4_5ALL", "V4_5", "V4"],
708
- value="V4_5ALL",
709
- interactive=True
710
  )
711
 
712
- generate_btn = gr.Button("🚀 Generate Song & Get Receipt", variant="primary", size="lg")
713
- clear_btn = gr.Button("🗑️ Clear All", variant="secondary")
714
-
715
- gr.Markdown("""
716
- ### 🔐 Ownership Proof System
717
-
718
- **How it works:**
719
- 1. Use a **CUSTOM TITLE** (not "Generated Song")
720
- 2. Generate your song
721
- 3. When complete, you'll get download links for:
722
- - JSON receipt (raw proof data)
723
- - HTML viewer (beautiful receipt)
724
-
725
- **What's in the receipt:**
726
- - Cryptographic HMAC-SHA256 proof
727
- - Task ID used as seed
728
- - Secret salt "Salt" for signing
729
- - Timestamp and song metadata
730
-
731
- **Important:** Save your receipts! They are your only proof of ownership.
732
- """)
733
 
734
  with gr.Column(scale=2):
735
- output = gr.Markdown(
736
- value="### Ready to generate!\n\nEnter your song details and click 'Generate Song & Get Receipt'"
737
- )
738
-
739
- # Receipt download area (appears after generation)
740
- with gr.Group(visible=False) as receipt_group:
741
- gr.Markdown("## 📦 Your Receipts Are Ready!")
742
- receipt_files_state = gr.State([])
743
- receipt_display = gr.Markdown("")
744
 
745
- with gr.Tab("🔍 Check Task Status", id="check_tab"):
746
- gr.Markdown("""
747
- ### ⚠️ Note: Receipts Only at Generation Time
748
-
749
- This tab only shows task status. **Receipts cannot be downloaded for old tasks**
750
- because they are only created at the moment of generation.
751
-
752
- To get a receipt, generate a new song with a custom title.
753
- """)
754
-
755
  with gr.Row():
756
- with gr.Column(scale=1):
757
- check_task_id = gr.Textbox(
758
- label="Task ID",
759
- placeholder="Enter Task ID to check status"
760
- )
761
- check_btn = gr.Button("🔍 Check Status", variant="primary")
762
-
763
- with gr.Column(scale=2):
764
- check_output = gr.Markdown(
765
- value="### Enter a Task ID above to check its status"
766
- )
767
-
768
- with gr.Tab("📚 Instructions", id="instructions_tab"):
769
- gr.Markdown("""
770
- ## 📖 How to Get Your Ownership Receipt
771
-
772
- ### 🎶 Generate Song & Get Receipt
773
-
774
- 1. **Enter your lyrics** (or leave empty for instrumental)
775
- 2. **Set the music style** (e.g., "Pop", "Rock", "Jazz")
776
- 3. **IMPORTANT: Use a CUSTOM TITLE**
777
- - ❌ Don't use "Generated Song" or "Untitled"
778
- - ✅ Use something unique like "My Awesome Song 2024"
779
- 4. **Choose your model** (V4_5ALL recommended)
780
- 5. **Click "Generate Song & Get Receipt"**
781
-
782
- ### 📦 After Generation
783
-
784
- When your song is ready (30-60 seconds), you'll see:
785
-
786
- 1. **Audio player** to listen immediately
787
- 2. **Download links** for MP3
788
- 3. **Receipt download links** for each song with custom title:
789
- - 📥 JSON Receipt (raw proof data)
790
- - 🌐 HTML Viewer (beautiful formatted receipt)
791
-
792
- ### 🔐 What's in Your Receipt
793
-
794
- ```json
795
- {
796
- "receipt_type": "song_ownership_proof",
797
- "task_id": "abc123...",
798
- "song": {
799
- "title": "Your Custom Title",
800
- "id": "music_123...",
801
- ...
802
- },
803
- "ownership_proof": {
804
- "proof": "sha256_hash...",
805
- "data": {
806
- "task_id": "abc123...",
807
- "title": "Your Custom Title",
808
- "timestamp": "..."
809
- }
810
- }
811
- }
812
- ```
813
-
814
- ### 💾 Save Your Receipts!
815
-
816
- - Store both JSON and HTML files safely
817
- - They are your only proof of ownership
818
- - Use them to claim ownership later
819
- - The verification system will check the cryptographic signature
820
-
821
- ### ❓ Why No Receipts for Old Tasks?
822
-
823
- Receipts are cryptographically signed at the moment of generation
824
- and can only be created when you have the task ID and the secret salt.
825
- This ensures that only the original creator can have the receipt.
826
- """)
827
-
828
- gr.Markdown("---")
829
- gr.Markdown(
830
- """
831
- <div style="text-align: center; padding: 20px;">
832
- <p>Powered by <a href="https://suno.ai" target="_blank">Suno AI</a> •
833
- <a href="https://sunoapi.org" target="_blank">Suno API Docs</a></p>
834
- <p><small>Anonymous Ownership Proofs • Receipts Only at Generation Time</small></p>
835
- </div>
836
- """
837
- )
838
 
839
  # Event handlers
840
- def clear_all():
841
- return ("", "Folk soul flamenco glam rock goa trance fusion", "Generated Song",
842
- False, "V4_5ALL", "### Ready to generate!\n\nEnter your song details and click 'Generate Song & Get Receipt'",
843
- [], gr.Group(visible=False), "")
844
-
845
- clear_btn.click(
846
- clear_all,
847
- outputs=[lyrics_text, style, title, instrumental, model, output,
848
- receipt_files_state, receipt_group, receipt_display]
849
- )
850
-
851
- # Modified generate function to show receipts
852
- def generate_with_receipts(lyrics, style, title, instrumental, model):
853
- messages = []
854
- receipt_files = []
855
-
856
- # Use the generator to collect messages
857
- for message in generate_song_from_text(lyrics, style, title, instrumental, model):
858
- messages.append(message)
859
- yield "".join(messages), receipt_files, gr.Group(visible=False), ""
860
-
861
- # After generation, check if we have receipt files
862
- # Note: In a real implementation, you'd want to capture the receipt files
863
- # This is simplified - you'd need to modify generate_song_from_text to return them
864
- yield "".join(messages), receipt_files, gr.Group(visible=False), ""
865
-
866
  generate_btn.click(
867
- generate_with_receipts,
868
  inputs=[lyrics_text, style, title, instrumental, model],
869
- outputs=[output, receipt_files_state, receipt_group, receipt_display]
870
  )
871
 
872
- # Check task handler (simplified - no receipts)
873
- def check_task_only(task_id):
874
- result, _ = get_task_info(task_id)
875
- return result
876
-
877
- check_btn.click(
878
- check_task_only,
879
- inputs=[check_task_id],
880
- outputs=check_output
881
- )
882
-
883
- # Handle URL parameters on load
884
- def on_page_load(request: gr.Request):
885
- task_id = parse_url_params(request)
886
-
887
- if task_id:
888
- result, _ = get_task_info(task_id)
889
- return (
890
- task_id, # check_task_id
891
- result, # check_output
892
- gr.Tabs(selected="check_tab") # Switch to check tab
893
- )
894
- else:
895
- return (
896
- "", # check_task_id
897
- "### Enter a Task ID above to check its status",
898
- gr.Tabs(selected="generate_tab")
899
  )
 
 
 
 
 
 
900
 
901
- app.load(
902
- fn=on_page_load,
903
- inputs=[],
904
- outputs=[check_task_id, check_output, gr.Tabs()],
905
- queue=False
906
- )
907
 
908
- # Launch the app
909
  if __name__ == "__main__":
910
- print("🚀 Starting Suno Song Generator with Anonymous Ownership Proofs")
911
- print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
912
- print(f"🔐 Secret Salt: {'✅ Configured' if SECRET_SALT else '❌ Not set'}")
913
- print("📦 Receipts: Only generated at creation time for custom titles")
914
- print("🌐 Open your browser to: http://localhost:7860")
915
-
916
  app.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
12
  # Suno API key
13
  SUNO_KEY = os.environ.get("SunoKey", "")
14
  # Secret key for ownership proofs
15
+ SECRET_SALT = "Salt"
16
 
17
  if not SUNO_KEY:
18
  print("⚠️ SunoKey not set!")
19
 
20
+ def generate_ownership_proof(task_id, title, music_id=None, status="pending"):
21
  """
22
+ Generate a SHA256 hash proof using task_id as seed
 
23
  """
 
24
  proof_data = {
25
  "task_id": task_id,
26
  "title": title,
27
  "music_id": music_id,
28
  "timestamp": datetime.utcnow().isoformat(),
29
+ "status": status
30
  }
31
 
 
32
  proof_string = json.dumps(proof_data, sort_keys=True)
33
 
 
34
  signature = hmac.new(
35
  SECRET_SALT.encode('utf-8'),
36
  proof_string.encode('utf-8'),
 
43
  "version": "1.0"
44
  }
45
 
46
+ def create_immediate_receipt(task_id, title):
47
  """
48
+ Create an immediate receipt with just task ID and title
49
  """
50
+ proof = generate_ownership_proof(task_id, title, status="pending")
51
+
52
  receipt = {
53
+ "receipt_type": "song_ownership_proof_pending",
54
  "generated": datetime.now().isoformat(),
55
  "task_id": task_id,
56
+ "title": title,
57
+ "status": "pending",
58
+ "message": "Your song is being generated. This receipt proves you requested this song with this title.",
59
+ "ownership_proof": proof,
60
+ "check_url": f"https://1hit.no/gen/view.php?taskid={task_id}"
 
 
 
 
 
 
 
 
61
  }
62
 
63
  return receipt
64
 
65
+ def create_pending_html_receipt(task_id, title):
66
  """
67
+ Create an HTML receipt for pending generation
68
  """
69
+ receipt = create_immediate_receipt(task_id, title)
 
70
  proof = receipt['ownership_proof']
 
71
 
72
  html = f"""<!DOCTYPE html>
73
  <html lang="en">
74
  <head>
75
  <meta charset="UTF-8">
76
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
77
+ <title> Pending Song Receipt - {title}</title>
78
  <style>
79
  body {{
80
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
81
  line-height: 1.6;
82
  color: #333;
83
+ max-width: 800px;
84
  margin: 0 auto;
85
  padding: 20px;
86
  background: #f5f5f5;
 
102
  margin: 0;
103
  font-size: 2.5em;
104
  }}
105
+ .pending-badge {{
106
+ background: #f39c12;
107
  color: white;
108
  padding: 8px 15px;
109
  border-radius: 20px;
 
117
  padding: 20px;
118
  margin: 20px 0;
119
  }}
 
 
 
 
 
 
120
  .info-grid {{
121
  display: grid;
122
+ grid-template-columns: 120px 1fr;
123
  gap: 10px;
124
  margin: 15px 0;
125
  }}
 
137
  font-size: 0.9em;
138
  word-break: break-all;
139
  }}
 
 
 
 
 
 
 
 
 
 
140
  .button {{
141
  display: inline-block;
142
  background: #3498db;
 
144
  text-decoration: none;
145
  padding: 10px 20px;
146
  border-radius: 5px;
147
+ margin: 5px;
148
  border: none;
149
  cursor: pointer;
150
  font-size: 14px;
 
152
  .button:hover {{
153
  background: #2980b9;
154
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  .warning {{
156
+ background: #fff3cd;
157
+ border-left: 4px solid #ffc107;
158
  padding: 15px;
159
  margin: 20px 0;
160
  }}
 
163
  <body>
164
  <div class="receipt">
165
  <div class="header">
166
+ <h1> Pending Song Receipt</h1>
167
+ <div class="pending-badge">🔜 Generation in Progress</div>
 
 
168
  <p>Generated: {receipt['generated']}</p>
169
+ </div>
170
+
171
+ <div class="warning">
172
+ <strong>🎵 Your song is being generated!</strong>
173
+ <p>This receipt proves you requested a song with this title at this time.</p>
174
+ <p>Check back later for the complete receipt with audio links.</p>
175
  </div>
176
 
177
  <div class="section">
178
+ <h2>📋 Request Information</h2>
179
  <div class="info-grid">
180
  <span class="info-label">Title:</span>
181
+ <span><strong>{title}</strong></span>
 
 
 
182
 
183
+ <span class="info-label">Task ID:</span>
184
+ <span><code>{task_id}</code></span>
185
 
186
+ <span class="info-label">Status:</span>
187
+ <span>⏳ Pending</span>
 
 
 
 
 
 
 
 
 
188
  </div>
 
189
  </div>
190
 
 
191
  <div class="section">
192
+ <h2>🔐 Ownership Proof</h2>
193
+ <p><strong>This proof already establishes you created this request:</strong></p>
194
  <div class="info-grid">
195
  <span class="info-label">Proof Version:</span>
196
  <span>{proof['version']}</span>
197
 
198
  <span class="info-label">Algorithm:</span>
199
+ <span>HMAC-SHA256</span>
200
  </div>
201
 
202
  <h3>Proof Hash:</h3>
 
205
  </div>
206
 
207
  <h3>Signed Data:</h3>
208
+ <pre class="proof-hash" style="background: #34495e;">{json.dumps(proof['data'], indent=2)}</pre>
209
  </div>
210
 
211
  <div class="section">
212
+ <h2>🔍 Check Status</h2>
213
+ <p>Use this link to check your song status:</p>
214
+ <div class="proof-hash" style="background: #27ae60;">
215
+ <a href="{receipt['check_url']}" style="color: white; text-decoration: none;" target="_blank">{receipt['check_url']}</a>
216
  </div>
 
 
 
 
 
 
217
  </div>
 
218
 
219
+ <div style="text-align: center; margin-top: 20px;">
220
  <button class="button" onclick="window.print()">🖨️ Print Receipt</button>
221
  <button class="button" onclick="downloadJSON()">📥 Download JSON</button>
222
  </div>
223
 
224
  <div class="footer" style="text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #7f8c8d;">
225
+ <p>📱 Save this receipt now! It proves you requested this song.</p>
226
+ <p><small>You can check back later for the complete receipt with audio links.</small></p>
227
  </div>
228
  </div>
229
 
 
233
  const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(receipt, null, 2));
234
  const downloadAnchor = document.createElement('a');
235
  downloadAnchor.setAttribute("href", dataStr);
236
+ downloadAnchor.setAttribute("download", "pending_receipt_{task_id[:8]}.json");
237
  document.body.appendChild(downloadAnchor);
238
  downloadAnchor.click();
239
  downloadAnchor.remove();
 
244
 
245
  return html
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  def generate_song_from_text(lyrics_text, style, title, instrumental, model):
248
+ """Generate a song and provide immediate receipt"""
249
  if not SUNO_KEY:
250
+ yield "❌ Error: SunoKey not configured"
 
 
 
 
 
 
 
 
251
  return
252
 
253
+ # Initial submission messages
254
+ yield f"## 🚀 Submitting Song Request\n\n"
255
+ yield f"**Title:** {title}\n"
256
+ yield f"**Style:** {style}\n"
257
+ yield f"**Model:** {model}\n"
258
+ yield f"**Instrumental:** {'Yes' if instrumental else 'No'}\n\n"
259
 
260
  try:
261
+ # Prepare request
262
  request_data = {
263
  "customMode": True,
264
  "instrumental": instrumental,
 
269
  }
270
 
271
  if not instrumental:
 
 
 
 
 
 
 
 
272
  request_data["prompt"] = lyrics_text
273
  else:
274
  request_data["prompt"] = ""
275
 
276
+ # Submit request
277
+ resp = requests.post(
278
+ "https://api.sunoapi.org/api/v1/generate",
279
+ json=request_data,
280
+ headers={"Authorization": f"Bearer {SUNO_KEY}"},
281
+ timeout=30
282
+ )
283
+
284
+ if resp.status_code != 200:
285
+ yield f"❌ Submission failed: HTTP {resp.status_code}"
286
+ return
287
 
288
+ data = resp.json()
 
 
 
 
 
 
289
 
290
+ # Extract task ID
291
+ task_id = None
292
+ if "taskId" in data:
293
+ task_id = data["taskId"]
294
+ elif "data" in data and "taskId" in data["data"]:
295
+ task_id = data["data"]["taskId"]
296
 
297
+ if not task_id:
298
+ yield " Could not extract Task ID"
299
+ return
 
 
 
 
 
300
 
301
+ # Show success
302
+ yield f"## ✅ Request Submitted!\n\n"
303
+ yield f"**🎯 Task ID:** `{task_id}`\n\n"
304
+
305
+ # Generate immediate receipt if custom title
306
+ if title not in ["Generated Song", "Untitled", ""]:
307
+ # Create receipt files
308
+ temp_dir = tempfile.mkdtemp()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
 
310
+ # JSON receipt
311
+ json_receipt = create_immediate_receipt(task_id, title)
312
+ json_path = os.path.join(temp_dir, f"receipt_pending_{task_id[:8]}.json")
313
+ with open(json_path, 'w') as f:
314
+ json.dump(json_receipt, f, indent=2)
315
 
316
+ # HTML receipt
317
+ html_content = create_pending_html_receipt(task_id, title)
318
+ html_path = os.path.join(temp_dir, f"receipt_pending_{task_id[:8]}.html")
319
+ with open(html_path, 'w') as f:
320
+ f.write(html_content)
321
 
322
+ # Show receipt download links IMMEDIATELY
323
+ yield f"""
324
+ ### 📥 YOUR INSTANT RECEIPT IS READY!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
+ Download this receipt NOW to prove you created this request:
327
+
328
+ - [📥 Download JSON Receipt]({json_path})
329
+ - [🌐 Download HTML Viewer]({html_path})
330
+
331
+ **🔐 This receipt already contains:**
332
+ - Cryptographic proof linking you to Task ID: `{task_id}`
333
+ - Song title: "{title}"
334
+ - Timestamp of request
335
+ - HMAC-SHA256 signature
336
+
337
+ **⏳ What happens next:**
338
+ 1. Your song is being generated (1-3 minutes)
339
+ 2. Keep this receipt safe
340
+ 3. Check status with the Task ID above
341
+ 4. Later you can get an updated receipt with audio links
342
+
343
+ **💾 SAVE THIS RECEIPT NOW!** It's your proof of creation.
344
+ """
345
+ else:
346
+ yield "⚠️ No receipt generated - use custom title for ownership proof\n\n"
347
 
348
+ yield f"**📊 Check status:** https://1hit.no/gen/view.php?taskid={task_id}\n\n"
349
+ yield "---\n\n"
350
 
351
+ # Poll for completion (optional - still show progress)
352
+ yield "**⏰ Checking generation progress...**\n\n"
353
 
354
+ for attempt in range(1, 7): # Check up to 6 times
355
+ time.sleep(30)
 
 
 
 
356
 
357
+ try:
358
+ resp = requests.get(
359
+ "https://api.sunoapi.org/api/v1/generate/record-info",
360
+ headers={"Authorization": f"Bearer {SUNO_KEY}"},
361
+ params={"taskId": task_id},
362
+ timeout=30
363
+ )
364
+
365
+ if resp.status_code == 200:
366
+ data = resp.json()
367
+ if data.get("code") == 200:
368
+ task_data = data.get("data", {})
369
+ status = task_data.get("status")
370
+
371
+ yield f"**Attempt {attempt}:** Status = {status}\n"
372
+
373
+ # If we have songs, show them
374
+ if status == "TEXT_SUCCESS" or status == "SUCCESS":
375
+ yield "\n✅ **Generation complete! Check the task page for audio links.**\n"
376
+ break
377
+ except:
378
+ yield f"**Attempt {attempt}:** Checking...\n"
379
 
380
+ yield "\n**Done! You can continue checking with the Task ID above.**"
381
 
382
  except Exception as e:
383
+ yield f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
 
385
+ # ... (keep other functions like get_task_info, parse_url_params, etc. from previous version)
 
 
 
386
 
387
+ # Create the app (simplified UI focusing on immediate receipts)
388
+ with gr.Blocks(theme=gr.themes.Soft(), title="Suno Song Generator") as app:
389
+ gr.Markdown("# 🎵 Suno Song Generator with Instant Ownership Proofs")
390
+ gr.Markdown("Get a cryptographic receipt IMMEDIATELY when you submit your song request")
 
 
 
 
 
 
 
 
391
 
392
+ with gr.Tab("🎶 Generate Song"):
393
  with gr.Row():
394
  with gr.Column(scale=1):
 
395
  lyrics_text = gr.Textbox(
396
  label="Lyrics",
397
  placeholder="Paste your lyrics here...",
398
+ lines=10
 
399
  )
400
 
 
401
  style = gr.Textbox(
402
  label="Music Style",
403
+ value="pop rock",
 
404
  interactive=True
405
  )
406
 
407
  title = gr.Textbox(
408
  label="Song Title (CUSTOM TITLE = RECEIPT)",
409
+ value="My Awesome Song",
410
+ info=" Use a custom title to get an instant ownership receipt!",
 
411
  interactive=True
412
  )
413
 
414
  with gr.Row():
415
+ instrumental = gr.Checkbox(label="Instrumental", value=False)
 
 
 
 
416
  model = gr.Dropdown(
417
  label="Model",
418
+ choices=["V5", "V4_5ALL", "V4"],
419
+ value="V5"
 
420
  )
421
 
422
+ generate_btn = gr.Button("🚀 Generate & Get Instant Receipt", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
 
424
  with gr.Column(scale=2):
425
+ output = gr.Markdown("### Ready to generate!\n\nEnter your details and click generate.")
 
 
 
 
 
 
 
 
426
 
427
+ with gr.Tab("🔍 Check Task"):
 
 
 
 
 
 
 
 
 
428
  with gr.Row():
429
+ check_task_id = gr.Textbox(label="Task ID", placeholder="Enter Task ID")
430
+ check_btn = gr.Button("🔍 Check Status")
431
+ check_output = gr.Markdown("### Enter a Task ID to check status")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
 
433
  # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  generate_btn.click(
435
+ generate_song_from_text,
436
  inputs=[lyrics_text, style, title, instrumental, model],
437
+ outputs=output
438
  )
439
 
440
+ def check_task(task_id):
441
+ if not task_id:
442
+ return "Please enter a Task ID"
443
+ try:
444
+ resp = requests.get(
445
+ "https://api.sunoapi.org/api/v1/generate/record-info",
446
+ headers={"Authorization": f"Bearer {SUNO_KEY}"},
447
+ params={"taskId": task_id},
448
+ timeout=30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  )
450
+ if resp.status_code == 200:
451
+ data = resp.json()
452
+ return f"```json\n{json.dumps(data, indent=2)}\n```"
453
+ return f"HTTP Error: {resp.status_code}"
454
+ except Exception as e:
455
+ return f"Error: {str(e)}"
456
 
457
+ check_btn.click(check_task, inputs=[check_task_id], outputs=check_output)
 
 
 
 
 
458
 
 
459
  if __name__ == "__main__":
460
+ print("🚀 Starting with INSTANT RECEIPTS!")
 
 
 
 
 
461
  app.launch(server_name="0.0.0.0", server_port=7860, share=False)