MySafeCode commited on
Commit
7116d55
·
verified ·
1 Parent(s): e47de4f

Upload api.py

Browse files
Files changed (1) hide show
  1. api.py +202 -0
api.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Interface
147
+ with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
148
+
149
+ gr.Markdown("# 🎥 BytePlus Video Generator")
150
+
151
+ with gr.Row():
152
+ if API_KEY:
153
+ gr.Markdown("✅ **API Key:** Configured")
154
+ else:
155
+ gr.Markdown("❌ **API Key:** Not configured - please add 'Key' secret")
156
+
157
+ with gr.Row():
158
+ with gr.Column():
159
+ image_input = gr.Image(
160
+ label="Upload Starting Image",
161
+ type="filepath",
162
+ height=300
163
+ )
164
+
165
+ shot_type = gr.Dropdown(
166
+ choices=list(DEFAULT_PROMPTS.keys()),
167
+ label="🎬 Shot Type",
168
+ value="Cinematic Nature"
169
+ )
170
+
171
+ prompt = gr.Textbox(
172
+ label="📝 Custom Prompt",
173
+ lines=3,
174
+ value=DEFAULT_PROMPTS["Cinematic Nature"]
175
+ )
176
+
177
+ shot_type.change(fn=update_prompt, inputs=shot_type, outputs=prompt)
178
+
179
+ generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg")
180
+
181
+ with gr.Column():
182
+ status = gr.Textbox(label="Status", lines=6)
183
+ video_output = gr.Video(label="Generated Video")
184
+
185
+ # Quick shot buttons
186
+ gr.Markdown("### Quick Shot Select")
187
+ with gr.Row():
188
+ gr.Button("🏔️ Nature").click(fn=lambda: "Cinematic Nature", outputs=shot_type)
189
+ gr.Button("🌃 City").click(fn=lambda: "Urban Exploration", outputs=shot_type)
190
+ gr.Button("🏎️ Action").click(fn=lambda: "Action Sequence", outputs=shot_type)
191
+ gr.Button("🎨 Abstract").click(fn=lambda: "Abstract Art", outputs=shot_type)
192
+ gr.Button("🦁 Wildlife").click(fn=lambda: "Wildlife Documentary", outputs=shot_type)
193
+ gr.Button("⛷️ Sports").click(fn=lambda: "Sports Highlight", outputs=shot_type)
194
+
195
+ generate_btn.click(
196
+ fn=generate_video,
197
+ inputs=[image_input, prompt],
198
+ outputs=[status, video_output]
199
+ )
200
+
201
+ if __name__ == "__main__":
202
+ demo.launch(server_name="0.0.0.0")