MySafeCode commited on
Commit
eaa9265
·
verified ·
1 Parent(s): 3430a4e

Create ups.py

Browse files
Files changed (1) hide show
  1. ups.py +450 -0
ups.py ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import gradio as gr
4
+ from byteplussdkarkruntime import Ark
5
+ import requests
6
+ from datetime import datetime
7
+
8
+ # Get API key from Hugging Face secret "Key" and CLEAN it
9
+ API_KEY = os.environ.get("Key", "").strip()
10
+ print(f"✅ Key loaded, length: {len(API_KEY)}")
11
+
12
+ # Initialize client with proxy
13
+ client = Ark(
14
+ base_url="https://1hit.no/proxy/proxy.php",
15
+ api_key=API_KEY,
16
+ timeout=30.0,
17
+ max_retries=3,
18
+ )
19
+
20
+ # Fresh new prompts
21
+ DEFAULT_PROMPTS = {
22
+ "Cinematic Nature": "Majestic drone shot soaring above misty mountains at golden hour, sunlight breaking through clouds, cinematic 4k quality, smooth motion --duration 5 --camerafixed false",
23
+ "Urban Exploration": "Dynamic drone flight through narrow alleyways of a neon-lit Tokyo street at night, rain-slicked surfaces reflecting lights, cyberpunk aesthetic --duration 5 --camerafixed false",
24
+ "Action Sequence": "High-speed drone chasing a sports car through a winding coastal road, dramatic cliffside views, action movie style --duration 5 --camerafixed false",
25
+ "Abstract Art": "Surreal drone flight through floating geometric shapes in a dreamlike void, pastel colors, ethereal atmosphere --duration 5 --camerafixed false",
26
+ "Wildlife Documentary": "Drone following a herd of elephants across the African savanna at sunset, National Geographic style, majestic wildlife cinematography --duration 5 --camerafixed false",
27
+ "Sports Highlight": "Drone racing alongside professional skiers carving through fresh powder in the Alps, high-energy sports broadcast style --duration 5 --camerafixed false",
28
+ "Beach Paradise": "Drone gliding over turquoise waters and white sand beaches, palm trees swaying, tropical paradise aerial view --duration 5 --camerafixed false",
29
+ "Ancient Temple": "Drone circling around ancient Angkor Wat temple at dawn, mystical atmosphere, historical documentary style --duration 5 --camerafixed false"
30
+ }
31
+
32
+ def poll_via_json(task_id):
33
+ """Check io.json for task status"""
34
+ json_url = "https://1hit.no/proxy/io.json"
35
+ try:
36
+ response = requests.get(json_url)
37
+ data = response.json()
38
+ if task_id in data:
39
+ task_data = data[task_id]
40
+ status = task_data.get('status')
41
+ if status == 'succeeded':
42
+ return task_data.get('video_url')
43
+ elif status == 'failed':
44
+ return "FAILED"
45
+ else:
46
+ return "PROCESSING"
47
+ except:
48
+ pass
49
+ return None
50
+
51
+ def generate_video(image_path, manual_url, prompt_text, progress=gr.Progress()):
52
+ """Generate video with proper polling and URL override"""
53
+
54
+ if not API_KEY:
55
+ yield "❌ API Key not configured. Please add 'Key' secret.", None
56
+ return
57
+
58
+ # Determine which URL to use
59
+ if manual_url and manual_url.strip():
60
+ image_url = manual_url.strip()
61
+ source = "manual URL"
62
+ elif image_path:
63
+ image_url = f"/file={image_path}"
64
+ source = "uploaded image"
65
+ else:
66
+ yield "⚠️ Please upload an image or enter a URL", None
67
+ return
68
+
69
+ try:
70
+ progress(0, desc="Preparing image...")
71
+ print(f"Using {source}: {image_url}")
72
+ yield f"✅ Using {source}...", None
73
+
74
+ progress(0.2, desc="Creating request...")
75
+
76
+ # Create task
77
+ try:
78
+ create_result = client.content_generation.tasks.create(
79
+ model="seedance-1-5-pro-251215",
80
+ content=[
81
+ {"type": "text", "text": prompt_text},
82
+ {"type": "image_url", "image_url": {"url": image_url}}
83
+ ]
84
+ )
85
+ except Exception as e:
86
+ yield f"❌ API Error: {str(e)}", None
87
+ return
88
+
89
+ task_id = create_result.id
90
+ print(f"Task created: {task_id}")
91
+ yield f"✅ Task created: {task_id}", None
92
+
93
+ progress(0.3, desc="Polling for results...")
94
+
95
+ # Poll for results - TRY SDK FIRST, then fallback to JSON
96
+ attempts = 0
97
+ max_attempts = 120
98
+ used_fallback = False
99
+
100
+ while attempts < max_attempts:
101
+ try:
102
+ # Try SDK method first
103
+ get_result = client.content_generation.tasks.get(task_id=task_id)
104
+ status = get_result.status
105
+
106
+ if status == "succeeded":
107
+ progress(1.0, desc="Complete!")
108
+ video_url = get_result.content.video_url if hasattr(get_result, 'content') else None
109
+ yield "✅ Video generated successfully!", video_url
110
+ return
111
+
112
+ elif status == "failed":
113
+ error_msg = get_result.error if hasattr(get_result, 'error') else "Unknown error"
114
+ yield f"❌ Failed: {error_msg}", None
115
+ return
116
+ else:
117
+ progress(0.3 + (attempts/max_attempts)*0.7, desc=f"Status: {status}")
118
+ yield f"⏳ Status: {status}... (attempt {attempts + 1})", None
119
+ time.sleep(2)
120
+ attempts += 1
121
+
122
+ except Exception as e:
123
+ # If SDK fails, use JSON fallback
124
+ if not used_fallback:
125
+ print("SDK polling failed, using JSON fallback...")
126
+ used_fallback = True
127
+
128
+ # Poll JSON every 5 seconds
129
+ result = poll_via_json(task_id)
130
+ if result == "FAILED":
131
+ yield "❌ Task failed (via JSON)", None
132
+ return
133
+ elif result and result != "PROCESSING":
134
+ yield "✅ Video generated successfully! (via JSON)", result
135
+ return
136
+ else:
137
+ progress(0.3 + (attempts/max_attempts)*0.7, desc="Status: processing (JSON)")
138
+ yield f"⏳ Status: processing... (JSON fallback, attempt {attempts + 1})", None
139
+ time.sleep(5)
140
+ attempts += 1
141
+
142
+ yield "⏰ Timeout after 2 minutes", None
143
+
144
+ except Exception as e:
145
+ yield f"❌ Error: {str(e)}", None
146
+
147
+ def update_prompt(choice):
148
+ return DEFAULT_PROMPTS.get(choice, "")
149
+
150
+ # MANUAL POLLING FUNCTIONS
151
+ def manual_poll(task_id):
152
+ """Manually poll a specific task and update io.json"""
153
+ if not task_id:
154
+ return "❌ Please enter a task ID"
155
+
156
+ try:
157
+ # Call proxy to poll this specific task
158
+ url = f"https://1hit.no/proxy/proxy.php?task_id={task_id}"
159
+ headers = {"Authorization": f"Bearer {API_KEY}"}
160
+
161
+ response = requests.get(url, headers=headers)
162
+
163
+ # Also fetch updated io.json
164
+ io_response = requests.get("https://1hit.no/proxy/io.json")
165
+ io_data = io_response.json()
166
+
167
+ if task_id in io_data:
168
+ status = io_data[task_id].get('status')
169
+ video_url = io_data[task_id].get('video_url')
170
+
171
+ result = f"✅ Task {task_id}\n"
172
+ result += f"Status: {status}\n"
173
+ if video_url:
174
+ result += f"Video URL: {video_url}\n"
175
+ if 'response' in io_data[task_id]:
176
+ result += f"Full response: {io_data[task_id]['response']}\n"
177
+ return result
178
+ else:
179
+ return f"❌ Task {task_id} not found in io.json"
180
+
181
+ except Exception as e:
182
+ return f"❌ Error: {str(e)}"
183
+
184
+ def get_raw_json():
185
+ """Fetch raw io.json"""
186
+ try:
187
+ r = requests.get("https://1hit.no/proxy/io.json")
188
+ return r.json()
189
+ except:
190
+ return {"error": "Could not fetch io.json"}
191
+
192
+ # WATCH & DOWNLOAD FUNCTIONS
193
+ def get_task_list():
194
+ """Get list of all tasks from io.json"""
195
+ try:
196
+ r = requests.get("https://1hit.no/proxy/io.json")
197
+ data = r.json()
198
+ tasks = []
199
+ for task_id, task_data in data.items():
200
+ status = task_data.get('status', 'unknown')
201
+ created = task_data.get('created', 0)
202
+ # Format time
203
+ time_str = datetime.fromtimestamp(created).strftime('%Y-%m-%d %H:%M:%S')
204
+
205
+ # Get prompt preview
206
+ prompt = task_data.get('request', {}).get('content', [{}])[0].get('text', 'No prompt')
207
+ prompt_preview = prompt[:50] + '...' if len(prompt) > 50 else prompt
208
+
209
+ tasks.append({
210
+ "id": task_id,
211
+ "status": status,
212
+ "created": time_str,
213
+ "created_ts": created,
214
+ "video_url": task_data.get('video_url', ''),
215
+ "prompt": prompt_preview
216
+ })
217
+ # Sort by created time, newest first
218
+ tasks.sort(key=lambda x: x['created_ts'], reverse=True)
219
+ return tasks
220
+ except Exception as e:
221
+ print(f"Error getting task list: {e}")
222
+ return []
223
+
224
+ def refresh_task_list():
225
+ """Refresh the task list dropdown"""
226
+ tasks = get_task_list()
227
+ choices = [f"{t['id']} | {t['status']} | {t['created']}" for t in tasks]
228
+ return gr.Dropdown(choices=choices, value=choices[0] if choices else None)
229
+
230
+ def load_task(selection):
231
+ """Load selected task details"""
232
+ if not selection:
233
+ return "No task selected", None, None
234
+
235
+ task_id = selection.split(' | ')[0]
236
+ try:
237
+ r = requests.get("https://1hit.no/proxy/io.json")
238
+ data = r.json()
239
+ task = data.get(task_id, {})
240
+
241
+ video_url = task.get('video_url', '')
242
+ status = task.get('status', 'unknown')
243
+
244
+ # Get full prompt
245
+ prompt = task.get('request', {}).get('content', [{}])[0].get('text', 'No prompt')
246
+
247
+ # Get created time
248
+ created = task.get('created', 0)
249
+ created_str = datetime.fromtimestamp(created).strftime('%Y-%m-%d %H:%M:%S')
250
+
251
+ # Get response data if available
252
+ response = task.get('response', {})
253
+
254
+ info = f"### Task Details\n\n"
255
+ info += f"**Task ID:** `{task_id}`\n"
256
+ info += f"**Status:** `{status}`\n"
257
+ info += f"**Created:** {created_str}\n"
258
+ info += f"**Prompt:** {prompt}\n\n"
259
+
260
+ if status == 'succeeded' and video_url:
261
+ info += f"✅ **Video ready!**\n"
262
+ info += f"**Download:** [Click here]({video_url})\n"
263
+ elif status == 'failed':
264
+ error = task.get('response', {}).get('error', 'Unknown error')
265
+ info += f"❌ **Failed:** {error}\n"
266
+
267
+ return info, video_url, video_url
268
+ except Exception as e:
269
+ return f"Error loading task: {e}", None, None
270
+
271
+ def update_url_display(image_path, manual_url):
272
+ """Update the URL display based on inputs"""
273
+ if manual_url and manual_url.strip():
274
+ return manual_url.strip()
275
+ elif image_path:
276
+ return f"/file={image_path}"
277
+ else:
278
+ return "No image selected"
279
+
280
+ # Create the interface
281
+ with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
282
+
283
+ gr.Markdown("# 🎥 BytePlus Video Generator")
284
+
285
+ with gr.Row():
286
+ if API_KEY:
287
+ gr.Markdown("✅ **API Key:** Configured")
288
+ else:
289
+ gr.Markdown("❌ **API Key:** Not configured - please add 'Key' secret")
290
+
291
+ # Create tabs
292
+ with gr.Tabs():
293
+ # Tab 1: Generate Video (UPDATED with URL display)
294
+ with gr.TabItem("🎬 Generate Video"):
295
+ with gr.Row():
296
+ with gr.Column():
297
+ image_input = gr.Image(
298
+ label="Upload Starting Image",
299
+ type="filepath",
300
+ height=300
301
+ )
302
+
303
+ # URL override section
304
+ with gr.Accordion("🔗 Image URL Override", open=False):
305
+ image_url_input = gr.Textbox(
306
+ label="Enter image URL directly",
307
+ placeholder="https://example.com/image.jpg",
308
+ info="Leave empty to use uploaded image"
309
+ )
310
+ gr.Markdown("💡 *Using a URL will override the uploaded image*")
311
+
312
+ # Current URL display
313
+ with gr.Row():
314
+ current_url_display = gr.Textbox(
315
+ label="Current Image URL",
316
+ value="No image selected",
317
+ interactive=False,
318
+ lines=2
319
+ )
320
+
321
+ shot_type = gr.Dropdown(
322
+ choices=list(DEFAULT_PROMPTS.keys()),
323
+ label="🎬 Shot Type",
324
+ value="Cinematic Nature"
325
+ )
326
+
327
+ prompt = gr.Textbox(
328
+ label="📝 Custom Prompt",
329
+ lines=3,
330
+ value=DEFAULT_PROMPTS["Cinematic Nature"]
331
+ )
332
+
333
+ shot_type.change(fn=update_prompt, inputs=shot_type, outputs=prompt)
334
+
335
+ generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg")
336
+
337
+ with gr.Column():
338
+ status = gr.Textbox(label="Status", lines=6)
339
+ video_output = gr.Video(label="Generated Video")
340
+
341
+ # Quick shot buttons
342
+ gr.Markdown("### Quick Shot Select")
343
+ with gr.Row():
344
+ gr.Button("🏔️ Nature").click(fn=lambda: "Cinematic Nature", outputs=shot_type)
345
+ gr.Button("🌃 City").click(fn=lambda: "Urban Exploration", outputs=shot_type)
346
+ gr.Button("🏎️ Action").click(fn=lambda: "Action Sequence", outputs=shot_type)
347
+ gr.Button("🎨 Abstract").click(fn=lambda: "Abstract Art", outputs=shot_type)
348
+ gr.Button("🦁 Wildlife").click(fn=lambda: "Wildlife Documentary", outputs=shot_type)
349
+ gr.Button("⛷️ Sports").click(fn=lambda: "Sports Highlight", outputs=shot_type)
350
+
351
+ # Update URL display when inputs change
352
+ image_input.change(
353
+ fn=update_url_display,
354
+ inputs=[image_input, image_url_input],
355
+ outputs=current_url_display
356
+ )
357
+
358
+ image_url_input.change(
359
+ fn=update_url_display,
360
+ inputs=[image_input, image_url_input],
361
+ outputs=current_url_display
362
+ )
363
+
364
+ # Tab 2: Manual Polling
365
+ with gr.TabItem("🔧 Manual Polling"):
366
+ gr.Markdown("### 🔧 Manual Polling & Debug")
367
+
368
+ with gr.Row():
369
+ with gr.Column():
370
+ gr.Markdown("#### Poll Specific Task")
371
+ task_id_input = gr.Textbox(
372
+ label="Task ID",
373
+ placeholder="Enter task ID (cgt-...)"
374
+ )
375
+ poll_btn = gr.Button("🔄 Poll Now", variant="primary")
376
+ poll_result = gr.Textbox(label="Poll Result", lines=10)
377
+
378
+ poll_btn.click(
379
+ fn=manual_poll,
380
+ inputs=task_id_input,
381
+ outputs=poll_result
382
+ )
383
+
384
+ with gr.Column():
385
+ gr.Markdown("#### Current io.json")
386
+ refresh_btn = gr.Button("🔄 Refresh io.json", variant="secondary")
387
+ raw_json = gr.JSON(label="io.json Contents")
388
+
389
+ refresh_btn.click(
390
+ fn=get_raw_json,
391
+ outputs=raw_json
392
+ )
393
+
394
+ gr.Markdown("""
395
+ ### 📝 Instructions
396
+ 1. Copy a task ID from above (like `cgt-20260217072358-hszt9`)
397
+ 2. Paste it in the Task ID field
398
+ 3. Click "Poll Now" to force an update
399
+ 4. Check io.json to see if status changed
400
+ """)
401
+
402
+ # Tab 3: Watch & Download
403
+ with gr.TabItem("📺 Watch & Download"):
404
+ gr.Markdown("### 📺 Browse and Download Generated Videos")
405
+
406
+ with gr.Row():
407
+ with gr.Column(scale=1):
408
+ refresh_list_btn = gr.Button("🔄 Refresh Task List", variant="primary")
409
+ task_list = gr.Dropdown(
410
+ label="Select Task",
411
+ choices=[],
412
+ interactive=True
413
+ )
414
+
415
+ with gr.Column(scale=2):
416
+ task_info = gr.Markdown("Select a task to view details")
417
+
418
+ with gr.Row():
419
+ with gr.Column():
420
+ video_player = gr.Video(label="Video Player")
421
+ download_link = gr.File(label="Download Video")
422
+
423
+ # Load task list on button click
424
+ refresh_list_btn.click(
425
+ fn=refresh_task_list,
426
+ outputs=task_list
427
+ )
428
+
429
+ # Load task when selected
430
+ task_list.change(
431
+ fn=load_task,
432
+ inputs=task_list,
433
+ outputs=[task_info, video_player, download_link]
434
+ )
435
+
436
+ # Auto-load task list when tab is opened
437
+ demo.load(
438
+ fn=refresh_task_list,
439
+ outputs=task_list
440
+ )
441
+
442
+ # Connect generate button with updated inputs
443
+ generate_btn.click(
444
+ fn=generate_video,
445
+ inputs=[image_input, image_url_input, prompt],
446
+ outputs=[status, video_output]
447
+ )
448
+
449
+ if __name__ == "__main__":
450
+ demo.launch(server_name="0.0.0.0")