MySafeCode commited on
Commit
6382c2b
·
verified ·
1 Parent(s): daf51c5

Create final.py

Browse files
Files changed (1) hide show
  1. final.py +407 -0
final.py ADDED
@@ -0,0 +1,407 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, prompt_text, progress=gr.Progress()):
52
+ """Generate video with proper polling"""
53
+
54
+ if not API_KEY:
55
+ yield "❌ API Key not configured. Please add 'Key' secret.", None
56
+ return
57
+
58
+ if image_path is None:
59
+ yield "⚠️ Please upload an image first", None
60
+ return
61
+
62
+ try:
63
+ progress(0, desc="Preparing image...")
64
+
65
+ # FOR TESTING: Use working image URL
66
+ image_url = "https://ark-doc.tos-ap-southeast-1.bytepluses.com/seepro_i2v%20.png"
67
+ print(f"Using image URL: {image_url}")
68
+
69
+ yield "✅ Image ready! Creating video request...", None
70
+
71
+ progress(0.2, desc="Creating request...")
72
+
73
+ # Create task
74
+ try:
75
+ create_result = client.content_generation.tasks.create(
76
+ model="seedance-1-5-pro-251215",
77
+ content=[
78
+ {"type": "text", "text": prompt_text},
79
+ {"type": "image_url", "image_url": {"url": image_url}}
80
+ ]
81
+ )
82
+ except Exception as e:
83
+ yield f"❌ API Error: {str(e)}", None
84
+ return
85
+
86
+ task_id = create_result.id
87
+ print(f"Task created: {task_id}")
88
+ yield f"✅ Task created: {task_id}", None
89
+
90
+ progress(0.3, desc="Polling for results...")
91
+
92
+ # Poll for results - TRY SDK FIRST, then fallback to JSON
93
+ attempts = 0
94
+ max_attempts = 120
95
+ used_fallback = False
96
+
97
+ while attempts < max_attempts:
98
+ try:
99
+ # Try SDK method first
100
+ get_result = client.content_generation.tasks.get(task_id=task_id)
101
+ status = get_result.status
102
+
103
+ if status == "succeeded":
104
+ progress(1.0, desc="Complete!")
105
+ video_url = get_result.content.video_url if hasattr(get_result, 'content') else None
106
+ yield "✅ Video generated successfully!", video_url
107
+ return
108
+
109
+ elif status == "failed":
110
+ error_msg = get_result.error if hasattr(get_result, 'error') else "Unknown error"
111
+ yield f"❌ Failed: {error_msg}", None
112
+ return
113
+ else:
114
+ progress(0.3 + (attempts/max_attempts)*0.7, desc=f"Status: {status}")
115
+ yield f"⏳ Status: {status}... (attempt {attempts + 1})", None
116
+ time.sleep(2)
117
+ attempts += 1
118
+
119
+ except Exception as e:
120
+ # If SDK fails, use JSON fallback
121
+ if not used_fallback:
122
+ print("SDK polling failed, using JSON fallback...")
123
+ used_fallback = True
124
+
125
+ # Poll JSON every 5 seconds
126
+ result = poll_via_json(task_id)
127
+ if result == "FAILED":
128
+ yield "❌ Task failed (via JSON)", None
129
+ return
130
+ elif result and result != "PROCESSING":
131
+ yield "✅ Video generated successfully! (via JSON)", result
132
+ return
133
+ else:
134
+ progress(0.3 + (attempts/max_attempts)*0.7, desc="Status: processing (JSON)")
135
+ yield f"⏳ Status: processing... (JSON fallback, attempt {attempts + 1})", None
136
+ time.sleep(5)
137
+ attempts += 1
138
+
139
+ yield "⏰ Timeout after 2 minutes", None
140
+
141
+ except Exception as e:
142
+ yield f"❌ Error: {str(e)}", None
143
+
144
+ def update_prompt(choice):
145
+ return DEFAULT_PROMPTS.get(choice, "")
146
+
147
+ # MANUAL POLLING FUNCTIONS
148
+ def manual_poll(task_id):
149
+ """Manually poll a specific task and update io.json"""
150
+ if not task_id:
151
+ return "❌ Please enter a task ID"
152
+
153
+ try:
154
+ # Call proxy to poll this specific task
155
+ url = f"https://1hit.no/proxy/proxy.php?task_id={task_id}"
156
+ headers = {"Authorization": f"Bearer {API_KEY}"}
157
+
158
+ response = requests.get(url, headers=headers)
159
+
160
+ # Also fetch updated io.json
161
+ io_response = requests.get("https://1hit.no/proxy/io.json")
162
+ io_data = io_response.json()
163
+
164
+ if task_id in io_data:
165
+ status = io_data[task_id].get('status')
166
+ video_url = io_data[task_id].get('video_url')
167
+
168
+ result = f"✅ Task {task_id}\n"
169
+ result += f"Status: {status}\n"
170
+ if video_url:
171
+ result += f"Video URL: {video_url}\n"
172
+ if 'response' in io_data[task_id]:
173
+ result += f"Full response: {io_data[task_id]['response']}\n"
174
+ return result
175
+ else:
176
+ return f"❌ Task {task_id} not found in io.json"
177
+
178
+ except Exception as e:
179
+ return f"❌ Error: {str(e)}"
180
+
181
+ def get_raw_json():
182
+ """Fetch raw io.json"""
183
+ try:
184
+ r = requests.get("https://1hit.no/proxy/io.json")
185
+ return r.json()
186
+ except:
187
+ return {"error": "Could not fetch io.json"}
188
+
189
+ # WATCH & DOWNLOAD FUNCTIONS
190
+ def get_task_list():
191
+ """Get list of all tasks from io.json"""
192
+ try:
193
+ r = requests.get("https://1hit.no/proxy/io.json")
194
+ data = r.json()
195
+ tasks = []
196
+ for task_id, task_data in data.items():
197
+ status = task_data.get('status', 'unknown')
198
+ created = task_data.get('created', 0)
199
+ # Format time
200
+ time_str = datetime.fromtimestamp(created).strftime('%Y-%m-%d %H:%M:%S')
201
+
202
+ # Get prompt preview
203
+ prompt = task_data.get('request', {}).get('content', [{}])[0].get('text', 'No prompt')
204
+ prompt_preview = prompt[:50] + '...' if len(prompt) > 50 else prompt
205
+
206
+ tasks.append({
207
+ "id": task_id,
208
+ "status": status,
209
+ "created": time_str,
210
+ "created_ts": created,
211
+ "video_url": task_data.get('video_url', ''),
212
+ "prompt": prompt_preview
213
+ })
214
+ # Sort by created time, newest first
215
+ tasks.sort(key=lambda x: x['created_ts'], reverse=True)
216
+ return tasks
217
+ except Exception as e:
218
+ print(f"Error getting task list: {e}")
219
+ return []
220
+
221
+ def refresh_task_list():
222
+ """Refresh the task list dropdown"""
223
+ tasks = get_task_list()
224
+ choices = [f"{t['id']} | {t['status']} | {t['created']}" for t in tasks]
225
+ return gr.Dropdown(choices=choices, value=choices[0] if choices else None)
226
+
227
+ def load_task(selection):
228
+ """Load selected task details"""
229
+ if not selection:
230
+ return "No task selected", None, None
231
+
232
+ task_id = selection.split(' | ')[0]
233
+ try:
234
+ r = requests.get("https://1hit.no/proxy/io.json")
235
+ data = r.json()
236
+ task = data.get(task_id, {})
237
+
238
+ video_url = task.get('video_url', '')
239
+ status = task.get('status', 'unknown')
240
+
241
+ # Get full prompt
242
+ prompt = task.get('request', {}).get('content', [{}])[0].get('text', 'No prompt')
243
+
244
+ # Get created time
245
+ created = task.get('created', 0)
246
+ created_str = datetime.fromtimestamp(created).strftime('%Y-%m-%d %H:%M:%S')
247
+
248
+ # Get response data if available
249
+ response = task.get('response', {})
250
+
251
+ info = f"### Task Details\n\n"
252
+ info += f"**Task ID:** `{task_id}`\n"
253
+ info += f"**Status:** `{status}`\n"
254
+ info += f"**Created:** {created_str}\n"
255
+ info += f"**Prompt:** {prompt}\n\n"
256
+
257
+ if status == 'succeeded' and video_url:
258
+ info += f"✅ **Video ready!**\n"
259
+ info += f"**Download:** [Click here]({video_url})\n"
260
+ elif status == 'failed':
261
+ error = task.get('response', {}).get('error', 'Unknown error')
262
+ info += f"❌ **Failed:** {error}\n"
263
+
264
+ return info, video_url, video_url
265
+ except Exception as e:
266
+ return f"Error loading task: {e}", None, None
267
+
268
+ # Create the interface
269
+ with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
270
+
271
+ gr.Markdown("# 🎥 BytePlus Video Generator")
272
+
273
+ with gr.Row():
274
+ if API_KEY:
275
+ gr.Markdown("✅ **API Key:** Configured")
276
+ else:
277
+ gr.Markdown("❌ **API Key:** Not configured - please add 'Key' secret")
278
+
279
+ # Create tabs
280
+ with gr.Tabs():
281
+ # Tab 1: Generate Video
282
+ with gr.TabItem("🎬 Generate Video"):
283
+ with gr.Row():
284
+ with gr.Column():
285
+ image_input = gr.Image(
286
+ label="Upload Starting Image",
287
+ type="filepath",
288
+ height=300
289
+ )
290
+
291
+ shot_type = gr.Dropdown(
292
+ choices=list(DEFAULT_PROMPTS.keys()),
293
+ label="🎬 Shot Type",
294
+ value="Cinematic Nature"
295
+ )
296
+
297
+ prompt = gr.Textbox(
298
+ label="📝 Custom Prompt",
299
+ lines=3,
300
+ value=DEFAULT_PROMPTS["Cinematic Nature"]
301
+ )
302
+
303
+ shot_type.change(fn=update_prompt, inputs=shot_type, outputs=prompt)
304
+
305
+ generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg")
306
+
307
+ with gr.Column():
308
+ status = gr.Textbox(label="Status", lines=6)
309
+ video_output = gr.Video(label="Generated Video")
310
+
311
+ # Quick shot buttons
312
+ gr.Markdown("### Quick Shot Select")
313
+ with gr.Row():
314
+ gr.Button("🏔️ Nature").click(fn=lambda: "Cinematic Nature", outputs=shot_type)
315
+ gr.Button("🌃 City").click(fn=lambda: "Urban Exploration", outputs=shot_type)
316
+ gr.Button("🏎️ Action").click(fn=lambda: "Action Sequence", outputs=shot_type)
317
+ gr.Button("🎨 Abstract").click(fn=lambda: "Abstract Art", outputs=shot_type)
318
+ gr.Button("🦁 Wildlife").click(fn=lambda: "Wildlife Documentary", outputs=shot_type)
319
+ gr.Button("⛷️ Sports").click(fn=lambda: "Sports Highlight", outputs=shot_type)
320
+
321
+ # Tab 2: Manual Polling
322
+ with gr.TabItem("🔧 Manual Polling"):
323
+ gr.Markdown("### 🔧 Manual Polling & Debug")
324
+
325
+ with gr.Row():
326
+ with gr.Column():
327
+ gr.Markdown("#### Poll Specific Task")
328
+ task_id_input = gr.Textbox(
329
+ label="Task ID",
330
+ placeholder="Enter task ID (cgt-...)"
331
+ )
332
+ poll_btn = gr.Button("🔄 Poll Now", variant="primary")
333
+ poll_result = gr.Textbox(label="Poll Result", lines=10)
334
+
335
+ poll_btn.click(
336
+ fn=manual_poll,
337
+ inputs=task_id_input,
338
+ outputs=poll_result
339
+ )
340
+
341
+ with gr.Column():
342
+ gr.Markdown("#### Current io.json")
343
+ refresh_btn = gr.Button("🔄 Refresh io.json", variant="secondary")
344
+ raw_json = gr.JSON(label="io.json Contents")
345
+
346
+ refresh_btn.click(
347
+ fn=get_raw_json,
348
+ outputs=raw_json
349
+ )
350
+
351
+ gr.Markdown("""
352
+ ### 📝 Instructions
353
+ 1. Copy a task ID from above (like `cgt-20260217072358-hszt9`)
354
+ 2. Paste it in the Task ID field
355
+ 3. Click "Poll Now" to force an update
356
+ 4. Check io.json to see if status changed
357
+ """)
358
+
359
+ # Tab 3: Watch & Download
360
+ with gr.TabItem("📺 Watch & Download"):
361
+ gr.Markdown("### 📺 Browse and Download Generated Videos")
362
+
363
+ with gr.Row():
364
+ with gr.Column(scale=1):
365
+ refresh_list_btn = gr.Button("🔄 Refresh Task List", variant="primary")
366
+ task_list = gr.Dropdown(
367
+ label="Select Task",
368
+ choices=[],
369
+ interactive=True
370
+ )
371
+
372
+ with gr.Column(scale=2):
373
+ task_info = gr.Markdown("Select a task to view details")
374
+
375
+ with gr.Row():
376
+ with gr.Column():
377
+ video_player = gr.Video(label="Video Player")
378
+ download_link = gr.File(label="Download Video")
379
+
380
+ # Load task list on button click
381
+ refresh_list_btn.click(
382
+ fn=refresh_task_list,
383
+ outputs=task_list
384
+ )
385
+
386
+ # Load task when selected
387
+ task_list.change(
388
+ fn=load_task,
389
+ inputs=task_list,
390
+ outputs=[task_info, video_player, download_link]
391
+ )
392
+
393
+ # Auto-load task list when tab is opened
394
+ demo.load(
395
+ fn=refresh_task_list,
396
+ outputs=task_list
397
+ )
398
+
399
+ # Connect generate button
400
+ generate_btn.click(
401
+ fn=generate_video,
402
+ inputs=[image_input, prompt],
403
+ outputs=[status, video_output]
404
+ )
405
+
406
+ if __name__ == "__main__":
407
+ demo.launch(server_name="0.0.0.0")