MySafeCode commited on
Commit
3380296
·
verified ·
1 Parent(s): b39985c

Create lapi.py

Browse files
Files changed (1) hide show
  1. lapi.py +289 -0
lapi.py ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import gradio as gr
4
+ from byteplussdkarkruntime import Ark
5
+ import requests
6
+
7
+ # Get API key from Hugging Face secret "Key" and CLEAN it
8
+ API_KEY = os.environ.get("Key", "").strip()
9
+ print(f"✅ Key loaded, length: {len(API_KEY)}")
10
+
11
+ # Initialize client with proxy
12
+ client = Ark(
13
+ base_url="https://1hit.no/proxy/proxy.php",
14
+ api_key=API_KEY,
15
+ timeout=30.0,
16
+ max_retries=3,
17
+ )
18
+
19
+ # Fresh new prompts
20
+ DEFAULT_PROMPTS = {
21
+ "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",
22
+ "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",
23
+ "Action Sequence": "High-speed drone chasing a sports car through a winding coastal road, dramatic cliffside views, action movie style --duration 5 --camerafixed false",
24
+ "Abstract Art": "Surreal drone flight through floating geometric shapes in a dreamlike void, pastel colors, ethereal atmosphere --duration 5 --camerafixed false",
25
+ "Wildlife Documentary": "Drone following a herd of elephants across the African savanna at sunset, National Geographic style, majestic wildlife cinematography --duration 5 --camerafixed false",
26
+ "Sports Highlight": "Drone racing alongside professional skiers carving through fresh powder in the Alps, high-energy sports broadcast style --duration 5 --camerafixed false",
27
+ "Beach Paradise": "Drone gliding over turquoise waters and white sand beaches, palm trees swaying, tropical paradise aerial view --duration 5 --camerafixed false",
28
+ "Ancient Temple": "Drone circling around ancient Angkor Wat temple at dawn, mystical atmosphere, historical documentary style --duration 5 --camerafixed false"
29
+ }
30
+
31
+ def poll_via_json(task_id):
32
+ """Check io.json for task status"""
33
+ json_url = "https://1hit.no/proxy/io.json"
34
+ try:
35
+ response = requests.get(json_url)
36
+ data = response.json()
37
+ if task_id in data:
38
+ task_data = data[task_id]
39
+ status = task_data.get('status')
40
+ if status == 'succeeded':
41
+ return task_data.get('video_url')
42
+ elif status == 'failed':
43
+ return "FAILED"
44
+ else:
45
+ return "PROCESSING"
46
+ except:
47
+ pass
48
+ return None
49
+
50
+ def generate_video(image_path, prompt_text, progress=gr.Progress()):
51
+ """Generate video with proper polling"""
52
+
53
+ if not API_KEY:
54
+ yield "❌ API Key not configured. Please add 'Key' secret.", None
55
+ return
56
+
57
+ if image_path is None:
58
+ yield "⚠️ Please upload an image first", None
59
+ return
60
+
61
+ try:
62
+ progress(0, desc="Preparing image...")
63
+
64
+ # FOR TESTING: Use working image URL
65
+ image_url = "https://ark-doc.tos-ap-southeast-1.bytepluses.com/seepro_i2v%20.png"
66
+ print(f"Using image URL: {image_url}")
67
+
68
+ yield "✅ Image ready! Creating video request...", None
69
+
70
+ progress(0.2, desc="Creating request...")
71
+
72
+ # Create task
73
+ try:
74
+ create_result = client.content_generation.tasks.create(
75
+ model="seedance-1-5-pro-251215",
76
+ content=[
77
+ {"type": "text", "text": prompt_text},
78
+ {"type": "image_url", "image_url": {"url": image_url}}
79
+ ]
80
+ )
81
+ except Exception as e:
82
+ yield f"❌ API Error: {str(e)}", None
83
+ return
84
+
85
+ task_id = create_result.id
86
+ print(f"Task created: {task_id}")
87
+ yield f"✅ Task created: {task_id}", None
88
+
89
+ progress(0.3, desc="Polling for results...")
90
+
91
+ # Poll for results - TRY SDK FIRST, then fallback to JSON
92
+ attempts = 0
93
+ max_attempts = 120
94
+ used_fallback = False
95
+
96
+ while attempts < max_attempts:
97
+ try:
98
+ # Try SDK method first
99
+ get_result = client.content_generation.tasks.get(task_id=task_id)
100
+ status = get_result.status
101
+
102
+ if status == "succeeded":
103
+ progress(1.0, desc="Complete!")
104
+ video_url = get_result.content.video_url if hasattr(get_result, 'content') else None
105
+ yield "✅ Video generated successfully!", video_url
106
+ return
107
+
108
+ elif status == "failed":
109
+ error_msg = get_result.error if hasattr(get_result, 'error') else "Unknown error"
110
+ yield f"❌ Failed: {error_msg}", None
111
+ return
112
+ else:
113
+ progress(0.3 + (attempts/max_attempts)*0.7, desc=f"Status: {status}")
114
+ yield f"⏳ Status: {status}... (attempt {attempts + 1})", None
115
+ time.sleep(2)
116
+ attempts += 1
117
+
118
+ except Exception as e:
119
+ # If SDK fails, use JSON fallback
120
+ if not used_fallback:
121
+ print("SDK polling failed, using JSON fallback...")
122
+ used_fallback = True
123
+
124
+ # Poll JSON every 5 seconds
125
+ result = poll_via_json(task_id)
126
+ if result == "FAILED":
127
+ yield "❌ Task failed (via JSON)", None
128
+ return
129
+ elif result and result != "PROCESSING":
130
+ yield "✅ Video generated successfully! (via JSON)", result
131
+ return
132
+ else:
133
+ progress(0.3 + (attempts/max_attempts)*0.7, desc="Status: processing (JSON)")
134
+ yield f"⏳ Status: processing... (JSON fallback, attempt {attempts + 1})", None
135
+ time.sleep(5)
136
+ attempts += 1
137
+
138
+ yield "⏰ Timeout after 2 minutes", None
139
+
140
+ except Exception as e:
141
+ yield f"❌ Error: {str(e)}", None
142
+
143
+ def update_prompt(choice):
144
+ return DEFAULT_PROMPTS.get(choice, "")
145
+
146
+ # MANUAL POLLING FUNCTIONS
147
+ def manual_poll(task_id):
148
+ """Manually poll a specific task and update io.json"""
149
+ if not task_id:
150
+ return "❌ Please enter a task ID"
151
+
152
+ try:
153
+ # Call proxy to poll this specific task
154
+ url = f"https://1hit.no/proxy/proxy.php?task_id={task_id}"
155
+ headers = {"Authorization": f"Bearer {API_KEY}"}
156
+
157
+ response = requests.get(url, headers=headers)
158
+
159
+ # Also fetch updated io.json
160
+ io_response = requests.get("https://1hit.no/proxy/io.json")
161
+ io_data = io_response.json()
162
+
163
+ if task_id in io_data:
164
+ status = io_data[task_id].get('status')
165
+ video_url = io_data[task_id].get('video_url')
166
+
167
+ result = f"✅ Task {task_id}\n"
168
+ result += f"Status: {status}\n"
169
+ if video_url:
170
+ result += f"Video URL: {video_url}\n"
171
+ if 'response' in io_data[task_id]:
172
+ result += f"Full response: {io_data[task_id]['response']}\n"
173
+ return result
174
+ else:
175
+ return f"❌ Task {task_id} not found in io.json"
176
+
177
+ except Exception as e:
178
+ return f"❌ Error: {str(e)}"
179
+
180
+ def get_raw_json():
181
+ """Fetch raw io.json"""
182
+ try:
183
+ r = requests.get("https://1hit.no/proxy/io.json")
184
+ return r.json()
185
+ except:
186
+ return {"error": "Could not fetch io.json"}
187
+
188
+ # Interface
189
+ with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
190
+
191
+ gr.Markdown("# 🎥 BytePlus Video Generator")
192
+
193
+ with gr.Row():
194
+ if API_KEY:
195
+ gr.Markdown("✅ **API Key:** Configured")
196
+ else:
197
+ gr.Markdown("❌ **API Key:** Not configured - please add 'Key' secret")
198
+
199
+ # Create tabs
200
+ tab1, tab2 = gr.Tabs().render()
201
+
202
+ with tab1:
203
+ gr.Markdown("### Generate Video")
204
+ with gr.Row():
205
+ with gr.Column():
206
+ image_input = gr.Image(
207
+ label="Upload Starting Image",
208
+ type="filepath",
209
+ height=300
210
+ )
211
+
212
+ shot_type = gr.Dropdown(
213
+ choices=list(DEFAULT_PROMPTS.keys()),
214
+ label="🎬 Shot Type",
215
+ value="Cinematic Nature"
216
+ )
217
+
218
+ prompt = gr.Textbox(
219
+ label="📝 Custom Prompt",
220
+ lines=3,
221
+ value=DEFAULT_PROMPTS["Cinematic Nature"]
222
+ )
223
+
224
+ shot_type.change(fn=update_prompt, inputs=shot_type, outputs=prompt)
225
+
226
+ generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg")
227
+
228
+ with gr.Column():
229
+ status = gr.Textbox(label="Status", lines=6)
230
+ video_output = gr.Video(label="Generated Video")
231
+
232
+ # Quick shot buttons
233
+ gr.Markdown("### Quick Shot Select")
234
+ with gr.Row():
235
+ gr.Button("🏔️ Nature").click(fn=lambda: "Cinematic Nature", outputs=shot_type)
236
+ gr.Button("🌃 City").click(fn=lambda: "Urban Exploration", outputs=shot_type)
237
+ gr.Button("🏎️ Action").click(fn=lambda: "Action Sequence", outputs=shot_type)
238
+ gr.Button("🎨 Abstract").click(fn=lambda: "Abstract Art", outputs=shot_type)
239
+ gr.Button("🦁 Wildlife").click(fn=lambda: "Wildlife Documentary", outputs=shot_type)
240
+ gr.Button("⛷️ Sports").click(fn=lambda: "Sports Highlight", outputs=shot_type)
241
+
242
+ with tab2:
243
+ gr.Markdown("### 🔧 Manual Polling & Debug")
244
+
245
+ with gr.Row():
246
+ with gr.Column():
247
+ gr.Markdown("#### Poll Specific Task")
248
+ task_id_input = gr.Textbox(
249
+ label="Task ID",
250
+ placeholder="Enter task ID (cgt-...)"
251
+ )
252
+ poll_btn = gr.Button("🔄 Poll Now", variant="primary")
253
+ poll_result = gr.Textbox(label="Poll Result", lines=10)
254
+
255
+ poll_btn.click(
256
+ fn=manual_poll,
257
+ inputs=task_id_input,
258
+ outputs=poll_result
259
+ )
260
+
261
+ with gr.Column():
262
+ gr.Markdown("#### Current io.json")
263
+ refresh_btn = gr.Button("🔄 Refresh io.json", variant="secondary")
264
+ raw_json = gr.JSON(label="io.json Contents")
265
+
266
+ refresh_btn.click(
267
+ fn=get_raw_json,
268
+ outputs=raw_json
269
+ )
270
+
271
+ gr.Markdown("""
272
+ ### 📝 Instructions
273
+ 1. Copy a task ID from above (like `cgt-20260217072358-hszt9`)
274
+ 2. Paste it in the Task ID field
275
+ 3. Click "Poll Now" to force an update
276
+ 4. Check io.json to see if status changed
277
+
278
+ This manually triggers the proxy to poll BytePlus for updates!
279
+ """)
280
+
281
+ # Connect generate button
282
+ generate_btn.click(
283
+ fn=generate_video,
284
+ inputs=[image_input, prompt],
285
+ outputs=[status, video_output]
286
+ )
287
+
288
+ if __name__ == "__main__":
289
+ demo.launch(server_name="0.0.0.0")