MySafeCode commited on
Commit
7526751
·
verified ·
1 Parent(s): 3dac3e5

Update aa.py

Browse files
Files changed (1) hide show
  1. aa.py +492 -310
aa.py CHANGED
@@ -12,12 +12,12 @@ import tempfile
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
  """
@@ -26,7 +26,6 @@ def generate_ownership_proof(task_id, title, music_id=None, status="pending"):
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)
@@ -43,222 +42,195 @@ def generate_ownership_proof(task_id, title, music_id=None, status="pending"):
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;
87
- }}
88
- .receipt {{
89
- background: white;
90
- border-radius: 15px;
91
- padding: 30px;
92
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
93
- }}
94
- .header {{
95
- text-align: center;
96
- margin-bottom: 30px;
97
- padding-bottom: 20px;
98
- border-bottom: 2px solid #eee;
99
- }}
100
- .header h1 {{
101
- color: #2c3e50;
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;
110
- display: inline-block;
111
- margin: 10px 0;
112
- font-weight: bold;
113
- }}
114
- .section {{
115
- background: #f8f9fa;
116
- border-radius: 10px;
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
- }}
126
- .info-label {{
127
- font-weight: bold;
128
- color: #7f8c8d;
129
- }}
130
- .proof-hash {{
131
- background: #2c3e50;
132
- color: #ecf0f1;
133
- padding: 15px;
134
- border-radius: 5px;
135
- overflow-x: auto;
136
- font-family: monospace;
137
- font-size: 0.9em;
138
- word-break: break-all;
139
- }}
140
- .button {{
141
- display: inline-block;
142
- background: #3498db;
143
- color: white;
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;
151
- }}
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
- }}
161
  </style>
162
  </head>
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>
203
- <div class="proof-hash">
204
- {proof['proof']}
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
-
230
- <script>
231
- function downloadJSON() {{
232
- const receipt = {json.dumps(receipt, indent=2)};
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();
240
- }}
241
- </script>
242
- </body>
243
- </html>"""
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,193 +241,403 @@ def generate_song_from_text(lyrics_text, style, title, instrumental, model):
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)
 
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):
21
  """
22
  Generate a SHA256 hash proof using task_id as seed
23
  """
 
26
  "title": title,
27
  "music_id": music_id,
28
  "timestamp": datetime.utcnow().isoformat(),
 
29
  }
30
 
31
  proof_string = json.dumps(proof_data, sort_keys=True)
 
42
  "version": "1.0"
43
  }
44
 
45
+ def create_simple_receipt(task_id, title):
46
  """
47
+ Create a simple JSON receipt with task ID and title
48
  """
49
+ proof = generate_ownership_proof(task_id, title)
50
 
51
  receipt = {
52
+ "receipt_type": "song_ownership_proof",
53
  "generated": datetime.now().isoformat(),
54
  "task_id": task_id,
55
  "title": title,
56
+ "proof": proof,
 
 
57
  "check_url": f"https://1hit.no/gen/view.php?taskid={task_id}"
58
  }
59
 
60
  return receipt
61
 
62
+ def create_html_receipt(receipt):
63
  """
64
+ Create a simple HTML viewer for the receipt
65
  """
66
+ task_id = receipt['task_id']
67
+ title = receipt['title']
68
+ proof = receipt['proof']
69
 
70
  html = f"""<!DOCTYPE html>
71
+ <html>
72
  <head>
73
+ <title>Song Receipt - {title}</title>
 
 
74
  <style>
75
+ body {{ font-family: Arial; max-width: 800px; margin: 40px auto; padding: 20px; }}
76
+ .receipt {{ border: 2px solid #333; padding: 20px; border-radius: 10px; }}
77
+ .proof {{ background: #f0f0f0; padding: 10px; word-break: break-all; font-family: monospace; }}
78
+ h1 {{ color: #2c3e50; }}
79
+ .button {{ background: #3498db; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  </style>
81
  </head>
82
  <body>
83
  <div class="receipt">
84
+ <h1>🎵 Song Ownership Receipt</h1>
85
+ <p><strong>Generated:</strong> {receipt['generated']}</p>
86
+ <p><strong>Title:</strong> {title}</p>
87
+ <p><strong>Task ID:</strong> <code>{task_id}</code></p>
 
88
 
89
+ <h3>🔐 Proof</h3>
90
+ <div class="proof">
91
+ <strong>Hash:</strong> {proof['proof']}<br>
92
+ <strong>Signed Data:</strong> {json.dumps(proof['data'])}
93
  </div>
94
 
95
+ <p><strong>Check Status:</strong> <a href="{receipt['check_url']}">{receipt['check_url']}</a></p>
96
+
97
+ <p><small>Save this receipt to prove you created this song request.</small></p>
98
+ </div>
99
+ </body>
100
+ </html>"""
101
+
102
+ return html
103
+
104
+ def get_task_info(task_id):
105
+ """Manually check any Suno task status"""
106
+ if not task_id:
107
+ return "❌ Please enter a Task ID"
108
+
109
+ try:
110
+ resp = requests.get(
111
+ "https://api.sunoapi.org/api/v1/generate/record-info",
112
+ headers={"Authorization": f"Bearer {SUNO_KEY}"},
113
+ params={"taskId": task_id},
114
+ timeout=30
115
+ )
116
+
117
+ if resp.status_code != 200:
118
+ return f"❌ HTTP Error {resp.status_code}\n\n{resp.text}"
119
+
120
+ data = resp.json()
121
+
122
+ # Format the response for display
123
+ output = f"## 🔍 Task Status: `{task_id}`\n\n"
124
+
125
+ if data.get("code") == 200:
126
+ task_data = data.get("data", {})
127
+ status = task_data.get("status", "UNKNOWN")
128
+
129
+ output += f"**Status:** {status}\n"
130
+ output += f"**Task ID:** `{task_data.get('taskId', 'N/A')}`\n"
131
+ output += f"**Music ID:** `{task_data.get('musicId', 'N/A')}`\n"
132
+ output += f"**Created:** {task_data.get('createTime', 'N/A')}\n"
133
+
134
+ if status == "SUCCESS" or status == "TEXT_SUCCESS":
135
+ response_data = task_data.get("response", {})
136
 
137
+ # Try to parse response (could be string or dict)
138
+ if isinstance(response_data, str):
139
+ try:
140
+ response_data = json.loads(response_data)
141
+ except:
142
+ output += f"\n**Raw Response:**\n```\n{response_data}\n```\n"
143
+ response_data = {}
144
 
145
+ # Check for song data
146
+ songs = []
147
+ if isinstance(response_data, dict):
148
+ songs = response_data.get("sunoData", [])
149
+ if not songs:
150
+ songs = response_data.get("data", [])
151
+ elif isinstance(response_data, list):
152
+ songs = response_data
 
 
 
153
 
154
+ if songs:
155
+ output += f"\n## 🎵 Generated Songs ({len(songs)})\n\n"
156
+
157
+ for i, song in enumerate(songs, 1):
158
+ if isinstance(song, dict):
159
+ output += f"### Song {i}\n"
160
+ output += f"**Title:** {song.get('title', 'Untitled')}\n"
161
+ output += f"**ID:** `{song.get('id', 'N/A')}`\n"
162
+
163
+ # Audio URLs
164
+ audio_url = song.get('audioUrl') or song.get('audio_url')
165
+ stream_url = song.get('streamUrl') or song.get('stream_url')
166
+ download_url = song.get('downloadUrl') or song.get('download_url')
167
+
168
+ if audio_url:
169
+ output += f"**Audio:** [Play]({audio_url}) | [Download]({audio_url})\n"
170
+ elif stream_url:
171
+ output += f"**Stream:** [Play]({stream_url})\n"
172
+
173
+ if download_url:
174
+ output += f"**Download:** [MP3]({download_url})\n"
175
+
176
+ # Audio player
177
+ play_url = audio_url or stream_url
178
+ if play_url:
179
+ output += f"""\n<audio controls style="width: 100%; margin: 10px 0;">
180
+ <source src="{play_url}" type="audio/mpeg">
181
+ Your browser does not support audio.
182
+ </audio>\n"""
183
+
184
+ output += f"**Prompt:** {song.get('prompt', 'N/A')[:100]}...\n"
185
+ output += f"**Duration:** {song.get('duration', 'N/A')}s\n"
186
+ output += f"**Created:** {song.get('createTime', 'N/A')}\n\n"
187
+ output += "---\n\n"
188
+ else:
189
+ output += "\n**No song data found in response.**\n"
190
+
191
+ elif status == "FAILED":
192
+ error_msg = task_data.get("errorMessage", "Unknown error")
193
+ output += f"\n**Error:** {error_msg}\n"
194
 
195
+ elif status in ["PENDING", "PROCESSING", "RUNNING"]:
196
+ output += f"\n**Task is still processing...**\n"
197
+ output += f"Check again in 30 seconds.\n"
 
198
 
199
+ else:
200
+ output += f"\n**Unknown status:** {status}\n"
 
201
 
202
+ else:
203
+ output += f"**API Error:** {data.get('msg', 'Unknown')}\n"
 
 
 
 
 
204
 
205
+ # Show raw JSON for debugging
206
+ output += "\n## 📋 Raw Response\n"
207
+ output += f"```json\n{json.dumps(data, indent=2)}\n```"
 
208
 
209
+ return output
210
+
211
+ except Exception as e:
212
+ return f"❌ Error checking task: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  def generate_song_from_text(lyrics_text, style, title, instrumental, model):
215
+ """Generate a song from lyrics text"""
216
  if not SUNO_KEY:
217
+ yield "❌ Error: SunoKey not configured in environment variables"
218
+ return
219
+
220
+ if not lyrics_text.strip() and not instrumental:
221
+ yield "❌ Error: Please provide lyrics or select instrumental"
222
+ return
223
+
224
+ if not style.strip():
225
+ yield "❌ Error: Please provide a music style"
226
  return
227
 
228
+ if not title.strip():
229
+ yield " Error: Please provide a song title"
230
+ return
 
 
 
231
 
232
  try:
233
+ # Prepare request data
234
  request_data = {
235
  "customMode": True,
236
  "instrumental": instrumental,
 
241
  }
242
 
243
  if not instrumental:
244
+ # Apply character limits
245
+ if model == "V4" and len(lyrics_text) > 3000:
246
+ lyrics_text = lyrics_text[:3000]
247
+ yield f"⚠️ Lyrics truncated to 3000 characters for V4 model\n\n"
248
+ elif model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"] and len(lyrics_text) > 5000:
249
+ lyrics_text = lyrics_text[:5000]
250
+ yield f"⚠️ Lyrics truncated to 5000 characters for {model} model\n\n"
251
+
252
  request_data["prompt"] = lyrics_text
253
  else:
254
  request_data["prompt"] = ""
255
 
256
+ # Apply style length limits
257
+ if model == "V4" and len(style) > 200:
258
+ style = style[:200]
259
+ yield f"⚠️ Style truncated to 200 characters for V4 model\n\n"
260
+ elif model in ["V4_5", "V4_5PLUS", "V4_5ALL", "V5"] and len(style) > 1000:
261
+ style = style[:1000]
262
+ yield f"⚠️ Style truncated to 1000 characters for {model} model\n\n"
263
 
264
+ # Apply title length limits
265
+ if model in ["V4", "V4_5ALL"] and len(title) > 80:
266
+ title = title[:80]
267
+ yield f"⚠️ Title truncated to 80 characters for {model} model\n\n"
268
+ elif model in ["V4_5", "V4_5PLUS", "V5"] and len(title) > 100:
269
+ title = title[:100]
270
+ yield f"⚠️ Title truncated to 100 characters for {model} model\n\n"
271
 
272
+ request_data["style"] = style
273
+ request_data["title"] = title
274
 
275
+ yield f"## 🚀 Submitting Song Request\n\n"
276
+ yield f"**Title:** {title}\n"
277
+ yield f"**Style:** {style}\n"
278
+ yield f"**Model:** {model}\n"
279
+ yield f"**Instrumental:** {'Yes' if instrumental else 'No'}\n"
280
+ if not instrumental:
281
+ yield f"**Lyrics length:** {len(lyrics_text)} characters\n\n"
282
+ yield f"**Callback URL:** https://1hit.no/callback.php\n\n"
 
 
 
 
 
 
283
 
284
+ # Submit generation request
285
+ try:
286
+ resp = requests.post(
287
+ "https://api.sunoapi.org/api/v1/generate",
288
+ json=request_data,
289
+ headers={
290
+ "Authorization": f"Bearer {SUNO_KEY}",
291
+ "Content-Type": "application/json"
292
+ },
293
+ timeout=30
294
+ )
295
+
296
+ if resp.status_code != 200:
297
+ yield f"❌ Submission failed: HTTP {resp.status_code}"
298
+ yield f"\n**Response:**\n```\n{resp.text}\n```"
299
+ return
300
 
301
+ data = resp.json()
302
+ print(f"Submission response: {json.dumps(data, indent=2)}")
 
 
 
303
 
304
+ if data.get("code") != 200:
305
+ yield f"❌ API error: {data.get('msg', 'Unknown')}"
306
+ return
 
 
307
 
308
+ # Extract task ID from response
309
+ task_id = None
310
+ if "taskId" in data:
311
+ task_id = data["taskId"]
312
+ elif "data" in data and "taskId" in data["data"]:
313
+ task_id = data["data"]["taskId"]
314
+ elif data.get("data") and "taskId" in data.get("data", {}):
315
+ task_id = data["data"]["taskId"]
316
+
317
+ if not task_id:
318
+ yield f"❌ Could not extract Task ID from response"
319
+ yield f"\n**Raw Response:**\n```json\n{json.dumps(data, indent=2)}\n```"
320
+ return
321
+
322
+ yield f"## ✅ Request Submitted Successfully!\n\n"
323
+ yield f"**🎯 Task ID:** `{task_id}`\n\n"
324
+
325
+ # === NEW: Generate receipt immediately ===
326
+ if title not in ["Generated Song", "Untitled", ""]:
327
+ # Create receipt
328
+ receipt = create_simple_receipt(task_id, title)
329
+
330
+ # Save to temp files
331
+ json_path = tempfile.NamedTemporaryFile(mode='w', suffix=f'_{task_id[:8]}.json', delete=False).name
332
+ with open(json_path, 'w') as f:
333
+ json.dump(receipt, f, indent=2)
334
+
335
+ html_path = tempfile.NamedTemporaryFile(mode='w', suffix=f'_{task_id[:8]}.html', delete=False).name
336
+ with open(html_path, 'w') as f:
337
+ f.write(create_html_receipt(receipt))
338
+
339
+ yield f"""
340
+ ### 📥 YOUR RECEIPT IS READY!
341
 
342
+ Download it now to prove you created this song request:
343
 
344
  - [📥 Download JSON Receipt]({json_path})
345
+ - [🌐 Download HTML Receipt]({html_path})
 
 
 
 
 
 
346
 
347
+ **Task ID:** `{task_id}`
348
+ **Title:** {title}
349
+ **Time:** {receipt['generated']}
350
+ **Proof:** `{receipt['proof']['proof'][:32]}...`
 
351
 
352
+ Keep this receipt safe - it's your proof of ownership!
353
  """
354
+ else:
355
+ yield "⚠️ No receipt generated - use a custom title for ownership proof\n\n"
356
+
357
+ yield f"** Status:** Generation started\n"
358
+ yield f"**📞 Callback:** https://1hit.no/callback.php\n\n"
359
+ yield "---\n\n"
360
+ yield f"## 🔍 Check Status Manually\n\n"
361
+ yield f"Use this Task ID: `{task_id}` in the Check tab\n\n"
362
+
363
+ # Simple one-time check after 30 seconds
364
+ yield "\n**⏰ Will check once in 30 seconds...**\n"
365
  time.sleep(30)
366
 
367
+ # Single status check
368
+ status_result = get_task_info(task_id)
369
+ yield "\n## 📊 Status Check (30s)\n\n"
370
+ yield status_result
371
+
372
+ except Exception as e:
373
+ yield f"❌ Error submitting request: {str(e)}"
374
+ return
375
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  except Exception as e:
377
+ yield f"❌ **Unexpected Error:** {str(e)}"
378
+
379
+ # Function to handle URL parameters
380
+ def parse_url_params(request: gr.Request):
381
+ """Parse taskid from URL parameters"""
382
+ task_id = None
383
+ if request:
384
+ try:
385
+ query_params = parse_qs(urlparse(request.request.url).query)
386
+ if 'taskid' in query_params:
387
+ task_id = query_params['taskid'][0]
388
+ # Remove any whitespace
389
+ task_id = task_id.strip()
390
+ except Exception as e:
391
+ print(f"Error parsing URL params: {e}")
392
+
393
+ return task_id
394
 
395
+ # Create the app
396
+ with gr.Blocks() as app:
397
+ gr.Markdown("# 🎵 Suno Song Generator")
398
+ gr.Markdown("Create songs from lyrics and style using Suno AI")
399
 
400
+ with gr.TabItem("Audio Link"):
401
+ gr.HTML("""
402
+ <p>Hey gangster kids, plis clean up the site for me, you are making a mess!</p>
403
+ <a href=" https://1hit.no/gen/audio/images/patchfix.php" target="_blank">Open 1hit Image Cleanup</a>
404
+
405
+ <p>Click below to open the audio page:</p>
406
+ <a href="https://1hit.no/gen/audio/mp3/" target="_blank">Open 1hit Audio</a>
407
+ <p>11 feb 2026 - New feature - Minimal m3u file download.</p>
408
+ <a href="https://1hit.no/gen/xm3u.php" target="_blank">Get complete m3u.file of music lib - with titles and duration</a>
409
+ <p>11 feb 2026 - New feature - Minimal m3u file download, better version comes up later?</p>
410
+ <a href="https://1hit.no/gen/sm3u.php" target="_blank">Get complete m3u.file of music lib - with taskid</a>
411
+ <p>Tested with VLC</p>
412
+ <a href=" https://www.videolan.org/vlc/" target="_blank">Download VLC media player</a>
413
+ <p>13 feb 2026 - Making a backup of dataset available, but made to many commits. :)</p>
414
+ <a href="https://huggingface.co/datasets/MySafeCode/1hit.no-Music-Images/" target="_blank">https://huggingface.co/datasets/MySafeCode/1hit.no-Music-Images/</a>
415
+
416
+ """)
417
 
418
+
419
+ with gr.Tab("🎶 Generate Song", id="generate_tab") as tab_generate:
420
  with gr.Row():
421
  with gr.Column(scale=1):
422
+ # Lyrics Input
423
+ gr.Markdown("### Step 1: Enter Lyrics")
424
+
425
  lyrics_text = gr.Textbox(
426
  label="Lyrics",
427
+ placeholder="Paste your lyrics here...\n\nExample:\n(Verse 1)\nSun is shining, sky is blue\nBirds are singing, just for you...",
428
+ lines=10,
429
+ interactive=True
430
  )
431
 
432
+ # Song Settings
433
+ gr.Markdown("### Step 2: Song Settings")
434
+
435
  style = gr.Textbox(
436
  label="Music Style",
437
+ placeholder="Example: Pop, Rock, Jazz, Classical, Electronic, Hip Hop, Country",
438
+ value="Folk soul flamenco glam rock goa trance fusion",
439
  interactive=True
440
  )
441
 
442
  title = gr.Textbox(
443
+ label="Song Title (use custom title for receipt)",
444
+ placeholder="My Awesome Song",
445
+ value="Generated Song",
446
+ info="✅ Custom title = you get an ownership receipt!",
447
  interactive=True
448
  )
449
 
450
  with gr.Row():
451
+ instrumental = gr.Checkbox(
452
+ label="Instrumental (No Vocals)",
453
+ value=False,
454
+ interactive=True
455
+ )
456
  model = gr.Dropdown(
457
  label="Model",
458
+ choices=["V5", "V4_5PLUS", "V4_5ALL", "V4_5", "V4"],
459
+ value="V4_5ALL",
460
+ interactive=True
461
  )
462
 
463
+ # Action Buttons
464
+ generate_btn = gr.Button("🚀 Generate Song", variant="primary")
465
+ clear_btn = gr.Button("🗑️ Clear All", variant="secondary")
466
+
467
+ # Instructions
468
+ gr.Markdown("""
469
+ **How to use:**
470
+ 1. Paste lyrics (or leave empty for instrumental)
471
+ 2. Set music style
472
+ 3. Enter song title
473
+ 4. Choose model
474
+ 5. Click Generate!
475
+
476
+ **NEW: Ownership Receipts**
477
+ - Use a **custom title** to get a receipt immediately
478
+ - Receipt contains cryptographic proof of your request
479
+ - Download and save it to prove ownership
480
+ """)
481
 
482
  with gr.Column(scale=2):
483
+ # Output Area
484
+ output = gr.Markdown(
485
+ value="### Ready to generate!\n\nEnter lyrics and settings, then click 'Generate Song'"
486
+ )
487
 
488
+ with gr.Tab("🔍 Check Any Task", id="check_tab") as tab_check:
489
  with gr.Row():
490
+ with gr.Column(scale=1):
491
+ gr.Markdown("### Check Task Status")
492
+ gr.Markdown("Enter any Suno Task ID to check its status")
493
+
494
+ check_task_id = gr.Textbox(
495
+ label="Task ID",
496
+ placeholder="Enter Task ID from generation or separation",
497
+ info="From Song Generator or Vocal Separator"
498
+ )
499
+
500
+ check_btn = gr.Button("🔍 Check Status", variant="primary")
501
+ check_clear_btn = gr.Button("🗑️ Clear", variant="secondary")
502
+
503
+ # URL parameter info
504
+ gr.Markdown("""
505
+ **Quick access via URL:**
506
+ Add `?taskid=YOUR_TASK_ID` to the URL
507
+
508
+ Example:
509
+ `https://1hit.no/gen/view.php?task_id=fa3529d5cbaa93427ee4451976ed5c4b`
510
+ """)
511
+
512
+ with gr.Column(scale=2):
513
+ check_output = gr.Markdown(
514
+ value="### Enter a Task ID above\n\nPaste any Suno Task ID to check its current status and results."
515
+ )
516
+
517
+ with gr.Tab("📚 Instructions", id="instructions_tab"):
518
+ gr.Markdown("""
519
+ ## 📖 How to Use This App
520
+
521
+ ### 🎶 Generate Song Tab
522
+ 1. **Enter Lyrics** (or leave empty for instrumental)
523
+ 2. **Set Music Style** (e.g., "Pop", "Rock", "Jazz")
524
+ 3. **Enter Song Title** (use custom title for receipt)
525
+ 4. **Choose Model** (V4_5ALL recommended)
526
+ 5. **Click "Generate Song"**
527
+
528
+ ### 📦 New: Ownership Receipts
529
+ - When you use a **custom title**, you get an instant receipt
530
+ - Receipt contains cryptographic proof (HMAC-SHA256)
531
+ - Download both JSON and HTML versions
532
+ - Save it to prove you created this song request
533
+
534
+ ### 🔍 Check Any Task Tab
535
+ 1. **Paste any Suno Task ID**
536
+ 2. **Click "Check Status"**
537
+ 3. **View results and download links**
538
+
539
+ **Quick URL Access:**
540
+ - Visit with `?taskid=YOUR_TASK_ID` in the URL
541
+ - Automatically switches to Check tab
542
+ - Shows task status immediately
543
+ """)
544
+
545
+ with gr.Tab("📚 Less Instructions", id="less_instructions_tab"):
546
+ gr.Markdown("""
547
+ ## 📖 Quick Guide
548
+
549
+ ### 🎶 Generate Song
550
+ 1. Enter lyrics
551
+ 2. Set music style
552
+ 3. Enter song title (custom = receipt)
553
+ 4. Click Generate
554
+ 5. Download your receipt!
555
+
556
+ ### 🔍 Check Task
557
+ Add `?taskid=YOUR_TASK_ID` to URL
558
+
559
+ ### 📞 Callback Status
560
+ https://1hit.no/gen/view.php
561
+ """)
562
+
563
+ gr.Markdown("---")
564
+ gr.Markdown(
565
+ """
566
+ <div style="text-align: center; padding: 20px;">
567
+ <p>Powered by <a href="https://suno.ai" target="_blank">Suno AI</a> •
568
+ <a href="https://sunoapi.org" target="_blank">Suno API Docs</a></p>
569
+ <p><small>Create custom songs with ownership receipts</small></p>
570
+ </div>
571
+ """,
572
+ elem_id="footer"
573
+ )
574
+
575
+ # Event handlers for Generate Song tab
576
+ def clear_all():
577
+ return "", "Folk soul flamenco glam rock goa trance fusion", "Generated Song", False, "V4_5ALL", "### Ready to generate!\n\nEnter lyrics and settings, then click 'Generate Song'"
578
+
579
+ clear_btn.click(
580
+ clear_all,
581
+ outputs=[lyrics_text, style, title, instrumental, model, output]
582
+ )
583
 
 
584
  generate_btn.click(
585
  generate_song_from_text,
586
  inputs=[lyrics_text, style, title, instrumental, model],
587
  outputs=output
588
  )
589
 
590
+ # Event handlers for Check Any Task tab
591
+ def clear_check():
592
+ return "", "### Enter a Task ID above\n\nPaste any Suno Task ID to check its current status and results."
593
+
594
+ check_clear_btn.click(
595
+ clear_check,
596
+ outputs=[check_task_id, check_output]
597
+ )
598
+
599
+ check_btn.click(
600
+ get_task_info,
601
+ inputs=[check_task_id],
602
+ outputs=check_output
603
+ )
604
+
605
+ # Function to handle URL parameter on load
606
+ def on_page_load(request: gr.Request):
607
+ """Handle URL parameters when page loads"""
608
+ task_id = parse_url_params(request)
609
+
610
+ if task_id:
611
+ # We have a task ID from URL, return it and fetch results
612
+ task_result = get_task_info(task_id)
613
+ return (
614
+ task_id, # For check_task_id
615
+ task_result, # For check_output
616
+ gr.Tabs(selected="check_tab"), # Switch to check tab
617
+ True # Mark as loaded
618
+ )
619
+ else:
620
+ # No task ID in URL, stay on first tab
621
+ return (
622
+ "", # Empty check_task_id
623
+ "### Enter a Task ID above\n\nPaste any Suno Task ID to check its current status and results.", # Default message
624
+ gr.Tabs(selected="generate_tab"), # Stay on generate tab
625
+ True # Mark as loaded
626
  )
 
 
 
 
 
 
627
 
628
+ # Load URL parameters when the app starts
629
+ app.load(
630
+ fn=on_page_load,
631
+ inputs=[],
632
+ outputs=[check_task_id, check_output, gr.Tabs(), initial_load_done],
633
+ queue=False
634
+ )
635
 
636
+ # Launch the app
637
  if __name__ == "__main__":
638
+ print("🚀 Starting Suno Song Generator with Receipts")
639
+ print(f"🔑 SunoKey: {'✅ Set' if SUNO_KEY else '❌ Not set'}")
640
+ print("📦 Receipts: Generated immediately when you get a Task ID")
641
+ print("🌐 Open your browser to: http://localhost:7860")
642
+
643
  app.launch(server_name="0.0.0.0", server_port=7860, share=False)