Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -14,57 +14,57 @@ import requests
|
|
14 |
from datetime import datetime
|
15 |
import tempfile
|
16 |
|
17 |
-
# API
|
18 |
api_token = os.getenv("RAPI_TOKEN")
|
19 |
if api_token:
|
20 |
os.environ["REPLICATE_API_TOKEN"] = api_token
|
21 |
|
22 |
-
#
|
23 |
ASPECT_RATIOS = {
|
24 |
-
"16:9": "16:9 (YouTube,
|
25 |
-
"4:3": "4:3 (
|
26 |
-
"1:1": "1:1 (Instagram
|
27 |
-
"3:4": "3:4 (Instagram
|
28 |
-
"9:16": "9:16 (Instagram
|
29 |
-
"21:9": "21:9 (
|
30 |
-
"9:21": "9:21 (
|
31 |
}
|
32 |
|
33 |
def update_prompt_placeholder(mode):
|
34 |
-
"""
|
35 |
-
if mode == "
|
36 |
-
return gr.update(placeholder="
|
37 |
else:
|
38 |
-
return gr.update(placeholder="
|
39 |
|
40 |
def update_image_input(mode):
|
41 |
-
"""
|
42 |
-
if mode == "
|
43 |
return gr.update(visible=True)
|
44 |
else:
|
45 |
return gr.update(visible=False)
|
46 |
|
47 |
def generate_video(mode, prompt, image, aspect_ratio, seed, api_key_input, progress=gr.Progress()):
|
48 |
-
"""
|
49 |
|
50 |
-
# API
|
51 |
token = api_key_input or api_token
|
52 |
if not token:
|
53 |
-
return None, "❌ API
|
54 |
|
55 |
os.environ["REPLICATE_API_TOKEN"] = token
|
56 |
|
57 |
-
#
|
58 |
if not prompt:
|
59 |
-
return None, "❌
|
60 |
|
61 |
-
if mode == "
|
62 |
-
return None, "❌
|
63 |
|
64 |
try:
|
65 |
-
progress(0, desc="
|
66 |
|
67 |
-
#
|
68 |
input_params = {
|
69 |
"prompt": prompt,
|
70 |
"duration": 5,
|
@@ -73,210 +73,210 @@ def generate_video(mode, prompt, image, aspect_ratio, seed, api_key_input, progr
|
|
73 |
"seed": seed
|
74 |
}
|
75 |
|
76 |
-
#
|
77 |
-
if mode == "
|
78 |
-
progress(0.1, desc="
|
79 |
|
80 |
-
# PIL Image
|
81 |
-
if isinstance(image, str): #
|
82 |
with Image.open(image) as img:
|
83 |
buffered = io.BytesIO()
|
84 |
img.save(buffered, format="PNG")
|
85 |
image_base64 = base64.b64encode(buffered.getvalue()).decode()
|
86 |
-
else: # PIL Image
|
87 |
buffered = io.BytesIO()
|
88 |
image.save(buffered, format="PNG")
|
89 |
image_base64 = base64.b64encode(buffered.getvalue()).decode()
|
90 |
|
91 |
input_params["image"] = f"data:image/png;base64,{image_base64}"
|
92 |
|
93 |
-
progress(0.3, desc="Replicate API
|
94 |
|
95 |
-
# Replicate
|
96 |
output = replicate.run(
|
97 |
"bytedance/seedance-1-lite",
|
98 |
input=input_params
|
99 |
)
|
100 |
|
101 |
-
progress(0.7, desc="
|
102 |
|
103 |
-
#
|
104 |
if hasattr(output, 'read'):
|
105 |
video_data = output.read()
|
106 |
else:
|
107 |
-
#
|
108 |
response = requests.get(output)
|
109 |
video_data = response.content
|
110 |
|
111 |
-
#
|
112 |
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as tmp_file:
|
113 |
tmp_file.write(video_data)
|
114 |
video_path = tmp_file.name
|
115 |
|
116 |
-
# output.mp4
|
117 |
with open("output.mp4", "wb") as file:
|
118 |
file.write(video_data)
|
119 |
|
120 |
-
progress(1.0, desc="
|
121 |
|
122 |
-
#
|
123 |
-
info = f"""✅
|
124 |
|
125 |
-
📊
|
126 |
-
-
|
127 |
-
-
|
128 |
- Seed: {seed}
|
129 |
-
-
|
130 |
-
-
|
131 |
-
-
|
132 |
|
133 |
return video_path, info
|
134 |
|
135 |
except Exception as e:
|
136 |
-
error_msg = f"❌
|
137 |
return None, error_msg
|
138 |
|
139 |
-
# Gradio
|
140 |
-
with gr.Blocks(title="
|
141 |
gr.Markdown("""
|
142 |
-
# 🎬
|
143 |
|
144 |
-
|
145 |
|
146 |
[](https://ginigen.com/)
|
147 |
""")
|
148 |
|
149 |
with gr.Row():
|
150 |
with gr.Column(scale=1):
|
151 |
-
# API
|
152 |
-
with gr.Accordion("⚙️ API
|
153 |
if api_token:
|
154 |
-
gr.Markdown("✅ API
|
155 |
api_key_input = gr.Textbox(
|
156 |
-
label="Replicate API Token (
|
157 |
type="password",
|
158 |
-
placeholder="
|
159 |
value=""
|
160 |
)
|
161 |
else:
|
162 |
-
gr.Markdown("⚠️
|
163 |
api_key_input = gr.Textbox(
|
164 |
-
label="Replicate API Token (
|
165 |
type="password",
|
166 |
-
placeholder="Replicate API
|
167 |
value=""
|
168 |
)
|
169 |
|
170 |
-
#
|
171 |
mode = gr.Radio(
|
172 |
-
label="🎯
|
173 |
-
choices=["
|
174 |
-
value="
|
175 |
)
|
176 |
|
177 |
-
#
|
178 |
image_input = gr.Image(
|
179 |
-
label="📷
|
180 |
type="pil",
|
181 |
visible=False
|
182 |
)
|
183 |
|
184 |
-
#
|
185 |
aspect_ratio = gr.Dropdown(
|
186 |
-
label="📐
|
187 |
choices=list(ASPECT_RATIOS.keys()),
|
188 |
value="16:9",
|
189 |
-
info="
|
190 |
)
|
191 |
|
192 |
-
#
|
193 |
-
ratio_info = gr.Markdown(value=f"
|
194 |
|
195 |
-
# Seed
|
196 |
seed = gr.Number(
|
197 |
-
label="🎲
|
198 |
value=42,
|
199 |
precision=0,
|
200 |
-
info="
|
201 |
)
|
202 |
|
203 |
-
#
|
204 |
gr.Markdown("""
|
205 |
-
### 📋
|
206 |
-
-
|
207 |
-
-
|
208 |
""")
|
209 |
|
210 |
with gr.Column(scale=2):
|
211 |
-
#
|
212 |
prompt = gr.Textbox(
|
213 |
-
label="✍️
|
214 |
lines=5,
|
215 |
-
placeholder="
|
216 |
)
|
217 |
|
218 |
-
#
|
219 |
-
generate_btn = gr.Button("🎬
|
220 |
|
221 |
-
#
|
222 |
with gr.Column():
|
223 |
output_video = gr.Video(
|
224 |
-
label="📹
|
225 |
autoplay=True
|
226 |
)
|
227 |
output_info = gr.Textbox(
|
228 |
-
label="
|
229 |
lines=8,
|
230 |
interactive=False
|
231 |
)
|
232 |
|
233 |
-
#
|
234 |
-
with gr.Accordion("📖
|
235 |
gr.Markdown("""
|
236 |
-
###
|
237 |
|
238 |
-
1.
|
239 |
```bash
|
240 |
pip install gradio replicate pillow requests
|
241 |
```
|
242 |
|
243 |
-
2.
|
244 |
```bash
|
245 |
export RAPI_TOKEN="your-replicate-api-token"
|
246 |
```
|
247 |
|
248 |
-
3.
|
249 |
```bash
|
250 |
python app.py
|
251 |
```
|
252 |
|
253 |
-
###
|
254 |
|
255 |
-
-
|
256 |
-
-
|
257 |
-
-
|
258 |
-
- **Seed
|
259 |
|
260 |
-
###
|
261 |
|
262 |
-
-
|
263 |
-
-
|
264 |
-
-
|
265 |
-
-
|
266 |
""")
|
267 |
|
268 |
-
#
|
269 |
gr.Examples(
|
270 |
examples=[
|
271 |
-
["
|
272 |
-
["
|
273 |
-
["
|
274 |
],
|
275 |
inputs=[mode, prompt, image_input, aspect_ratio, seed],
|
276 |
-
label="
|
277 |
)
|
278 |
|
279 |
-
#
|
280 |
mode.change(
|
281 |
fn=update_prompt_placeholder,
|
282 |
inputs=[mode],
|
@@ -290,7 +290,7 @@ with gr.Blocks(title="AI Video Generator", theme=gr.themes.Soft()) as app:
|
|
290 |
)
|
291 |
|
292 |
aspect_ratio.change(
|
293 |
-
fn=lambda x: f"
|
294 |
inputs=[aspect_ratio],
|
295 |
outputs=[ratio_info]
|
296 |
)
|
@@ -301,7 +301,7 @@ with gr.Blocks(title="AI Video Generator", theme=gr.themes.Soft()) as app:
|
|
301 |
outputs=[output_video, output_info]
|
302 |
)
|
303 |
|
304 |
-
#
|
305 |
if __name__ == "__main__":
|
306 |
app.launch(
|
307 |
server_name="0.0.0.0",
|
|
|
14 |
from datetime import datetime
|
15 |
import tempfile
|
16 |
|
17 |
+
# API token setup
|
18 |
api_token = os.getenv("RAPI_TOKEN")
|
19 |
if api_token:
|
20 |
os.environ["REPLICATE_API_TOKEN"] = api_token
|
21 |
|
22 |
+
# Aspect ratio options
|
23 |
ASPECT_RATIOS = {
|
24 |
+
"16:9": "16:9 (YouTube, Standard Video)",
|
25 |
+
"4:3": "4:3 (Traditional TV Format)",
|
26 |
+
"1:1": "1:1 (Instagram Feed)",
|
27 |
+
"3:4": "3:4 (Instagram Portrait)",
|
28 |
+
"9:16": "9:16 (Instagram Reels, TikTok)",
|
29 |
+
"21:9": "21:9 (Cinematic Wide)",
|
30 |
+
"9:21": "9:21 (Ultra Vertical)"
|
31 |
}
|
32 |
|
33 |
def update_prompt_placeholder(mode):
|
34 |
+
"""Update prompt placeholder based on mode"""
|
35 |
+
if mode == "Text to Video":
|
36 |
+
return gr.update(placeholder="Describe the video you want to create.\nExample: The sun rises slowly between tall buildings. [Ground-level follow shot] Bicycle tires roll over a dew-covered street at dawn.")
|
37 |
else:
|
38 |
+
return gr.update(placeholder="Describe how the image should move.\nExample: Camera slowly zooms in while clouds move across the sky. The subject's hair gently moves in the wind.")
|
39 |
|
40 |
def update_image_input(mode):
|
41 |
+
"""Show/hide image input based on mode"""
|
42 |
+
if mode == "Image to Video":
|
43 |
return gr.update(visible=True)
|
44 |
else:
|
45 |
return gr.update(visible=False)
|
46 |
|
47 |
def generate_video(mode, prompt, image, aspect_ratio, seed, api_key_input, progress=gr.Progress()):
|
48 |
+
"""Main video generation function"""
|
49 |
|
50 |
+
# API token check
|
51 |
token = api_key_input or api_token
|
52 |
if not token:
|
53 |
+
return None, "❌ API token required. Please set RAPI_TOKEN environment variable or enter your API key."
|
54 |
|
55 |
os.environ["REPLICATE_API_TOKEN"] = token
|
56 |
|
57 |
+
# Input validation
|
58 |
if not prompt:
|
59 |
+
return None, "❌ Please enter a prompt."
|
60 |
|
61 |
+
if mode == "Image to Video" and image is None:
|
62 |
+
return None, "❌ Please upload an image."
|
63 |
|
64 |
try:
|
65 |
+
progress(0, desc="Preparing video generation...")
|
66 |
|
67 |
+
# Input parameters setup
|
68 |
input_params = {
|
69 |
"prompt": prompt,
|
70 |
"duration": 5,
|
|
|
73 |
"seed": seed
|
74 |
}
|
75 |
|
76 |
+
# Image to video mode
|
77 |
+
if mode == "Image to Video" and image is not None:
|
78 |
+
progress(0.1, desc="Processing image...")
|
79 |
|
80 |
+
# Convert PIL Image to base64
|
81 |
+
if isinstance(image, str): # File path
|
82 |
with Image.open(image) as img:
|
83 |
buffered = io.BytesIO()
|
84 |
img.save(buffered, format="PNG")
|
85 |
image_base64 = base64.b64encode(buffered.getvalue()).decode()
|
86 |
+
else: # PIL Image object
|
87 |
buffered = io.BytesIO()
|
88 |
image.save(buffered, format="PNG")
|
89 |
image_base64 = base64.b64encode(buffered.getvalue()).decode()
|
90 |
|
91 |
input_params["image"] = f"data:image/png;base64,{image_base64}"
|
92 |
|
93 |
+
progress(0.3, desc="Calling Replicate API...")
|
94 |
|
95 |
+
# Run Replicate
|
96 |
output = replicate.run(
|
97 |
"bytedance/seedance-1-lite",
|
98 |
input=input_params
|
99 |
)
|
100 |
|
101 |
+
progress(0.7, desc="Downloading video...")
|
102 |
|
103 |
+
# Get video data
|
104 |
if hasattr(output, 'read'):
|
105 |
video_data = output.read()
|
106 |
else:
|
107 |
+
# Download from URL
|
108 |
response = requests.get(output)
|
109 |
video_data = response.content
|
110 |
|
111 |
+
# Save to temporary file
|
112 |
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as tmp_file:
|
113 |
tmp_file.write(video_data)
|
114 |
video_path = tmp_file.name
|
115 |
|
116 |
+
# Also save as output.mp4
|
117 |
with open("output.mp4", "wb") as file:
|
118 |
file.write(video_data)
|
119 |
|
120 |
+
progress(1.0, desc="Complete!")
|
121 |
|
122 |
+
# Generation info
|
123 |
+
info = f"""✅ Video generated successfully!
|
124 |
|
125 |
+
📊 Generation Info:
|
126 |
+
- Mode: {mode}
|
127 |
+
- Aspect Ratio: {aspect_ratio}
|
128 |
- Seed: {seed}
|
129 |
+
- Duration: 5 seconds
|
130 |
+
- Resolution: 480p
|
131 |
+
- File: output.mp4"""
|
132 |
|
133 |
return video_path, info
|
134 |
|
135 |
except Exception as e:
|
136 |
+
error_msg = f"❌ Error occurred: {str(e)}"
|
137 |
return None, error_msg
|
138 |
|
139 |
+
# Gradio interface
|
140 |
+
with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as app:
|
141 |
gr.Markdown("""
|
142 |
+
# 🎬 Bytedance Seedance Video' Free
|
143 |
|
144 |
+
Generate videos from text or images using **Replicate API**.
|
145 |
|
146 |
[](https://ginigen.com/)
|
147 |
""")
|
148 |
|
149 |
with gr.Row():
|
150 |
with gr.Column(scale=1):
|
151 |
+
# API Settings
|
152 |
+
with gr.Accordion("⚙️ API Settings", open=not bool(api_token)):
|
153 |
if api_token:
|
154 |
+
gr.Markdown("✅ API token loaded from environment variable.")
|
155 |
api_key_input = gr.Textbox(
|
156 |
+
label="Replicate API Token (Optional)",
|
157 |
type="password",
|
158 |
+
placeholder="Enter to override environment variable",
|
159 |
value=""
|
160 |
)
|
161 |
else:
|
162 |
+
gr.Markdown("⚠️ RAPI_TOKEN environment variable not set.")
|
163 |
api_key_input = gr.Textbox(
|
164 |
+
label="Replicate API Token (Required)",
|
165 |
type="password",
|
166 |
+
placeholder="Enter your Replicate API token",
|
167 |
value=""
|
168 |
)
|
169 |
|
170 |
+
# Generation mode
|
171 |
mode = gr.Radio(
|
172 |
+
label="🎯 Generation Mode",
|
173 |
+
choices=["Text to Video", "Image to Video"],
|
174 |
+
value="Text to Video"
|
175 |
)
|
176 |
|
177 |
+
# Image upload
|
178 |
image_input = gr.Image(
|
179 |
+
label="📷 Upload Image",
|
180 |
type="pil",
|
181 |
visible=False
|
182 |
)
|
183 |
|
184 |
+
# Aspect ratio
|
185 |
aspect_ratio = gr.Dropdown(
|
186 |
+
label="📐 Aspect Ratio",
|
187 |
choices=list(ASPECT_RATIOS.keys()),
|
188 |
value="16:9",
|
189 |
+
info="Choose ratio optimized for social media platforms"
|
190 |
)
|
191 |
|
192 |
+
# Ratio description
|
193 |
+
ratio_info = gr.Markdown(value=f"Selected ratio: {ASPECT_RATIOS['16:9']}")
|
194 |
|
195 |
+
# Seed setting
|
196 |
seed = gr.Number(
|
197 |
+
label="🎲 Random Seed",
|
198 |
value=42,
|
199 |
precision=0,
|
200 |
+
info="Use same seed value to reproduce same results"
|
201 |
)
|
202 |
|
203 |
+
# Fixed settings display
|
204 |
gr.Markdown("""
|
205 |
+
### 📋 Fixed Settings
|
206 |
+
- **Duration**: 5 seconds
|
207 |
+
- **Resolution**: 480p
|
208 |
""")
|
209 |
|
210 |
with gr.Column(scale=2):
|
211 |
+
# Prompt input
|
212 |
prompt = gr.Textbox(
|
213 |
+
label="✍️ Prompt",
|
214 |
lines=5,
|
215 |
+
placeholder="Describe the video you want to create.\nExample: The sun rises slowly between tall buildings. [Ground-level follow shot] Bicycle tires roll over a dew-covered street at dawn."
|
216 |
)
|
217 |
|
218 |
+
# Generate button
|
219 |
+
generate_btn = gr.Button("🎬 Generate Video", variant="primary", size="lg")
|
220 |
|
221 |
+
# Results display
|
222 |
with gr.Column():
|
223 |
output_video = gr.Video(
|
224 |
+
label="📹 Generated Video",
|
225 |
autoplay=True
|
226 |
)
|
227 |
output_info = gr.Textbox(
|
228 |
+
label="Information",
|
229 |
lines=8,
|
230 |
interactive=False
|
231 |
)
|
232 |
|
233 |
+
# Usage instructions
|
234 |
+
with gr.Accordion("📖 How to Use", open=False):
|
235 |
gr.Markdown("""
|
236 |
+
### Installation
|
237 |
|
238 |
+
1. **Install required packages**:
|
239 |
```bash
|
240 |
pip install gradio replicate pillow requests
|
241 |
```
|
242 |
|
243 |
+
2. **Set environment variable** (optional):
|
244 |
```bash
|
245 |
export RAPI_TOKEN="your-replicate-api-token"
|
246 |
```
|
247 |
|
248 |
+
3. **Run**:
|
249 |
```bash
|
250 |
python app.py
|
251 |
```
|
252 |
|
253 |
+
### Features
|
254 |
|
255 |
+
- **Text to Video**: Generate video from text description only
|
256 |
+
- **Image to Video**: Transform uploaded image into animated video
|
257 |
+
- **Aspect Ratios**: Choose ratios optimized for various social media platforms
|
258 |
+
- **Seed Value**: Use same seed to reproduce identical results
|
259 |
|
260 |
+
### Prompt Writing Tips
|
261 |
|
262 |
+
- Use specific and detailed descriptions
|
263 |
+
- Specify camera movements (e.g., zoom in, pan left, tracking shot)
|
264 |
+
- Describe lighting and atmosphere (e.g., golden hour, dramatic lighting)
|
265 |
+
- Indicate movement speed (e.g., slowly, rapidly, gently)
|
266 |
""")
|
267 |
|
268 |
+
# Examples
|
269 |
gr.Examples(
|
270 |
examples=[
|
271 |
+
["Text to Video", "A serene lake at sunrise with mist rolling over the water. Camera slowly pans across the landscape as birds fly overhead.", None, "16:9", 42],
|
272 |
+
["Text to Video", "Urban street scene at night with neon lights reflecting on wet pavement. People walking with umbrellas, camera tracking forward.", None, "9:16", 123],
|
273 |
+
["Text to Video", "Close-up of a flower blooming in time-lapse, soft natural lighting, shallow depth of field.", None, "1:1", 789],
|
274 |
],
|
275 |
inputs=[mode, prompt, image_input, aspect_ratio, seed],
|
276 |
+
label="Example Prompts"
|
277 |
)
|
278 |
|
279 |
+
# Event handlers
|
280 |
mode.change(
|
281 |
fn=update_prompt_placeholder,
|
282 |
inputs=[mode],
|
|
|
290 |
)
|
291 |
|
292 |
aspect_ratio.change(
|
293 |
+
fn=lambda x: f"Selected ratio: {ASPECT_RATIOS[x]}",
|
294 |
inputs=[aspect_ratio],
|
295 |
outputs=[ratio_info]
|
296 |
)
|
|
|
301 |
outputs=[output_video, output_info]
|
302 |
)
|
303 |
|
304 |
+
# Run app
|
305 |
if __name__ == "__main__":
|
306 |
app.launch(
|
307 |
server_name="0.0.0.0",
|