haraberget commited on
Commit
67f3270
·
verified ·
1 Parent(s): e2478fd

Upload 7 files

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