ginipick commited on
Commit
7ed514f
Β·
verified Β·
1 Parent(s): 754806c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -49
app.py CHANGED
@@ -25,8 +25,8 @@ except ImportError:
25
  print("Warning: cv2 not available. Watermark feature will be disabled.")
26
 
27
  # API keys setup from environment variables
28
- API_KEY_T2V = os.getenv("WS_API_KEY_T2V", "946b77acd6a456dfde349aa0bac7b6d2bdf9c0c995fff072898c6d8734f866c4")
29
- API_KEY_I2V = os.getenv("WS_API_KEY_I2V", "5a90eaa7ded95066c07adf55b91185a83abfa8db3fdc74d12ba5ad66db1d68fe")
30
 
31
  # API endpoints
32
  API_BASE_URL = "https://api.wavespeed.ai/api/v3"
@@ -111,6 +111,43 @@ def add_watermark(input_video_path, output_video_path):
111
  except:
112
  return False
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  def update_prompt_placeholder(mode):
115
  """Update prompt placeholder based on mode"""
116
  if mode == "Text to Video":
@@ -203,24 +240,42 @@ def generate_video(mode, prompt, image_url, image_file, aspect_ratio, seed, api_
203
  # Use provided URL directly
204
  final_image_url = image_url
205
  elif image_file is not None:
206
- # Convert PIL image to data URL
207
- progress(0.1, desc="Converting image to data URL...")
208
-
209
- # Convert to base64
210
- buffered = io.BytesIO()
211
- if isinstance(image_file, str):
212
- with Image.open(image_file) as img:
213
- img.save(buffered, format="PNG")
214
- else:
215
- image_file.save(buffered, format="PNG")
216
-
217
- image_base64 = base64.b64encode(buffered.getvalue()).decode()
218
-
219
- # Try data URL format first
220
- final_image_url = f"data:image/png;base64,{image_base64}"
221
-
222
- # Note: If the API doesn't accept data URLs, you'll need to upload to a CDN
223
- # For now, we'll try the data URL and provide guidance if it fails
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  else:
225
  return None, "❌ No image provided."
226
 
@@ -248,15 +303,31 @@ def generate_video(mode, prompt, image_url, image_file, aspect_ratio, seed, api_
248
  else:
249
  error_detail = response.text
250
  if mode == "Image to Video" and image_file is not None and not image_url:
251
- return None, f"""❌ API Error: {response.status_code}
 
 
252
 
253
- The API may not accept base64 data URLs. Please try one of these options:
254
- 1. Use a direct image URL (e.g., https://example.com/image.jpg)
255
- 2. Upload your image to an image hosting service like:
256
- - imgur.com
257
- - imgbb.com
258
- - postimages.org
259
- 3. Use the image URL in the Image URL field
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
  Error details: {error_detail}"""
262
  else:
@@ -312,17 +383,25 @@ Error details: {error_detail}"""
312
 
313
  # Generation info
314
  watermark_status = "Added" if watermark_added else "Not available (cv2 not installed)" if not VIDEO_PROCESSING_AVAILABLE else "Failed"
 
 
 
 
 
 
 
 
 
315
  info = f"""βœ… Video generated successfully!
316
 
317
  πŸ“Š Generation Info:
318
  - Mode: {mode}
319
- - Aspect Ratio: {aspect_ratio}
320
  - Seed: {seed}
321
  - Duration: 5 seconds
322
  - Resolution: 480p
323
  - Watermark: {watermark_status}
324
  - API: WaveSpeed AI
325
- - Cost: $0.06
326
  - File: output.mp4"""
327
 
328
  return final_video_path, info
@@ -339,9 +418,8 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
339
 
340
  Generate videos from text or images using **WaveSpeed AI API**.
341
 
342
- [![Powered by Ginigen](https://img.shields.io/badge/Powered%20by-WaveSpeed%20AI-blue)](https://ginigen.com/)
343
 
344
- ⚠️ **Note**: Each generation costs $0.06
345
  """)
346
 
347
  with gr.Row():
@@ -381,15 +459,18 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
381
  with gr.Column(visible=False) as image_input_group:
382
  gr.Markdown("### Image Input")
383
  image_url_input = gr.Textbox(
384
- label="πŸ“· Image URL",
385
- placeholder="https://example.com/image.jpg",
386
- info="Enter a direct image URL"
387
  )
388
  gr.Markdown("**OR**")
389
  image_file_input = gr.Image(
390
- label="πŸ“€ Upload Image (Beta) - Converted to data URL (may not work)",
391
  type="pil"
392
  )
 
 
 
 
393
 
394
  # Aspect ratio (only for T2V)
395
  aspect_ratio = gr.Dropdown(
@@ -417,7 +498,6 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
417
  ### πŸ“‹ Fixed Settings
418
  - **Duration**: 5 seconds
419
  - **Resolution**: 480p
420
- - **Cost**: $0.06 per video
421
  - **Watermark**: {watermark_info}
422
  """)
423
 
@@ -431,7 +511,7 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
431
  )
432
 
433
  # Generate button
434
- generate_btn = gr.Button("🎬 Generate Video ($0.06)", variant="primary", size="lg")
435
 
436
  # Results display
437
  with gr.Column():
@@ -483,19 +563,24 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
483
 
484
  ### Image to Video Tips
485
 
486
- For best results with Image to Video:
487
- 1. Use direct image URLs (https://...)
488
- 2. If uploading files doesn't work, upload your image to:
489
- - [imgur.com](https://imgur.com)
490
- - [imgbb.com](https://imgbb.com)
491
- - [postimages.org](https://postimages.org)
492
- 3. Copy the direct image URL and paste it in the Image URL field
 
 
 
 
 
 
 
493
 
494
  ### API Information
495
 
496
- - **Provider**: WaveSpeed AI
497
- - **Models**: ByteDance Seedance v1 Lite
498
- - **Cost**: $0.06 per 5-second video
499
  - **Resolution**: 480p
500
 
501
  ### Prompt Writing Tips
@@ -512,7 +597,7 @@ with gr.Blocks(title="Bytedance Seedance Video Free", theme=gr.themes.Soft()) as
512
  ["Text to Video", "A cat walking gracefully across a sunlit room", "", None, "16:9", -1],
513
  ["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],
514
  ["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],
515
- ["Image to Video", "The camera slowly zooms in while clouds drift across the sky", "https://images.unsplash.com/photo-1506905925346-21bda4d32df4", None, "16:9", -1],
516
  ],
517
  inputs=[mode, prompt, image_url_input, image_file_input, aspect_ratio, seed],
518
  label="Example Prompts"
 
25
  print("Warning: cv2 not available. Watermark feature will be disabled.")
26
 
27
  # API keys setup from environment variables
28
+ API_KEY_T2V = os.getenv("WAVESPEED_API_KEY_T2V", "946b77acd6a456dfde349aa0bac7b6d2bdf9c0c995fff072898c6d8734f866c4")
29
+ API_KEY_I2V = os.getenv("WAVESPEED_API_KEY_I2V", "5a90eaa7ded95066c07adf55b91185a83abfa8db3fdc74d12ba5ad66db1d68fe")
30
 
31
  # API endpoints
32
  API_BASE_URL = "https://api.wavespeed.ai/api/v3"
 
111
  except:
112
  return False
113
 
114
+ def compress_image(image, max_size_mb=2, max_dimension=1920):
115
+ """Compress and resize image to meet size requirements"""
116
+ # Convert to RGB if necessary
117
+ if image.mode in ('RGBA', 'LA'):
118
+ rgb_image = Image.new('RGB', image.size, (255, 255, 255))
119
+ rgb_image.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
120
+ image = rgb_image
121
+ elif image.mode not in ('RGB', 'L'):
122
+ image = image.convert('RGB')
123
+
124
+ # Resize if dimensions are too large
125
+ width, height = image.size
126
+ if width > max_dimension or height > max_dimension:
127
+ ratio = min(max_dimension / width, max_dimension / height)
128
+ new_width = int(width * ratio)
129
+ new_height = int(height * ratio)
130
+ image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
131
+
132
+ # Compress with varying quality to meet size requirement
133
+ quality = 95
134
+ while quality > 20:
135
+ buffered = io.BytesIO()
136
+ image.save(buffered, format="JPEG", quality=quality, optimize=True)
137
+ size_mb = len(buffered.getvalue()) / (1024 * 1024)
138
+
139
+ if size_mb <= max_size_mb:
140
+ return buffered.getvalue()
141
+
142
+ quality -= 5
143
+
144
+ # If still too large, resize more aggressively
145
+ width, height = image.size
146
+ image = image.resize((width // 2, height // 2), Image.Resampling.LANCZOS)
147
+ buffered = io.BytesIO()
148
+ image.save(buffered, format="JPEG", quality=50, optimize=True)
149
+ return buffered.getvalue()
150
+
151
  def update_prompt_placeholder(mode):
152
  """Update prompt placeholder based on mode"""
153
  if mode == "Text to Video":
 
240
  # Use provided URL directly
241
  final_image_url = image_url
242
  elif image_file is not None:
243
+ # Compress image to reduce size
244
+ try:
245
+ # Get original size
246
+ orig_buffered = io.BytesIO()
247
+ image_file.save(orig_buffered, format="PNG")
248
+ orig_size_mb = len(orig_buffered.getvalue()) / (1024 * 1024)
249
+
250
+ progress(0.1, desc=f"Compressing image (Original: {orig_size_mb:.1f}MB)...")
251
+
252
+ compressed_data = compress_image(image_file, max_size_mb=2, max_dimension=1920)
253
+ compressed_size_mb = len(compressed_data) / (1024 * 1024)
254
+
255
+ progress(0.15, desc=f"Image compressed: {orig_size_mb:.1f}MB β†’ {compressed_size_mb:.1f}MB")
256
+
257
+ image_base64 = base64.b64encode(compressed_data).decode()
258
+
259
+ # Check encoded size
260
+ encoded_size_mb = len(image_base64) / (1024 * 1024)
261
+ if encoded_size_mb > 20: # Conservative limit for base64
262
+ return None, f"""❌ Image too large even after compression.
263
+
264
+ Original size: {orig_size_mb:.1f}MB
265
+ Compressed size: {compressed_size_mb:.1f}MB
266
+ Encoded size: {encoded_size_mb:.1f}MB (exceeds API limit)
267
+
268
+ Please use an image hosting service:
269
+ 1. Go to [imgur.com](https://imgur.com)
270
+ 2. Upload your image (drag & drop)
271
+ 3. Right-click uploaded image β†’ "Copy image address"
272
+ 4. Paste URL in the Image URL field above"""
273
+
274
+ # Try data URL format
275
+ final_image_url = f"data:image/jpeg;base64,{image_base64}"
276
+
277
+ except Exception as e:
278
+ return None, f"❌ Error processing image: {str(e)}"
279
  else:
280
  return None, "❌ No image provided."
281
 
 
303
  else:
304
  error_detail = response.text
305
  if mode == "Image to Video" and image_file is not None and not image_url:
306
+ # Check if it's a size limit error
307
+ if "size exceeds" in error_detail or "30MB" in error_detail:
308
+ return None, f"""❌ Image size exceeds API limit (30MB).
309
 
310
+ Your image was compressed but still too large for the API.
311
+
312
+ **Please use an image hosting service instead:**
313
+ 1. Go to [imgur.com](https://imgur.com) (no account needed)
314
+ 2. Click "New post" β†’ Drop your image
315
+ 3. After upload, right-click the image β†’ "Copy image address"
316
+ 4. Paste the URL in the Image URL field above
317
+
318
+ Alternative services:
319
+ - [imgbb.com](https://imgbb.com) - Simple, no account needed
320
+ - [postimages.org](https://postimages.org) - Direct links available
321
+
322
+ Error: {response.status_code}"""
323
+ else:
324
+ return None, f"""❌ API Error: {response.status_code}
325
+
326
+ The API does not accept base64 data URLs. Please use a direct image URL instead:
327
+
328
+ 1. Upload your image to [imgur.com](https://imgur.com)
329
+ 2. Copy the direct image URL (ends with .jpg or .png)
330
+ 3. Paste it in the Image URL field above
331
 
332
  Error details: {error_detail}"""
333
  else:
 
383
 
384
  # Generation info
385
  watermark_status = "Added" if watermark_added else "Not available (cv2 not installed)" if not VIDEO_PROCESSING_AVAILABLE else "Failed"
386
+
387
+ # Add image info if applicable
388
+ image_info = ""
389
+ if mode == "Image to Video":
390
+ if image_url:
391
+ image_info = "\n- Image Source: URL provided"
392
+ elif image_file:
393
+ image_info = "\n- Image Source: Uploaded & compressed"
394
+
395
  info = f"""βœ… Video generated successfully!
396
 
397
  πŸ“Š Generation Info:
398
  - Mode: {mode}
399
+ - Aspect Ratio: {aspect_ratio if mode == "Text to Video" else "N/A"}{image_info}
400
  - Seed: {seed}
401
  - Duration: 5 seconds
402
  - Resolution: 480p
403
  - Watermark: {watermark_status}
404
  - API: WaveSpeed AI
 
405
  - File: output.mp4"""
406
 
407
  return final_video_path, info
 
418
 
419
  Generate videos from text or images using **WaveSpeed AI API**.
420
 
421
+ [![Powered by Ginigen](https://img.shields.io/badge/Powered%20by%20Ginigen)](https://ginigen.com/)
422
 
 
423
  """)
424
 
425
  with gr.Row():
 
459
  with gr.Column(visible=False) as image_input_group:
460
  gr.Markdown("### Image Input")
461
  image_url_input = gr.Textbox(
462
+ label="πŸ“· Image URL (Recommended) - Direct image URL works best",
463
+ placeholder="https://example.com/image.jpg"
 
464
  )
465
  gr.Markdown("**OR**")
466
  image_file_input = gr.Image(
467
+ label="πŸ“€ Upload Image (Will be compressed, max 2MB after compression)",
468
  type="pil"
469
  )
470
+ gr.Markdown("""
471
+ ⚠️ **Note**: API has a 30MB limit. Large images will be compressed.
472
+ If upload fails, use [imgur.com](https://imgur.com) to host your image.
473
+ """)
474
 
475
  # Aspect ratio (only for T2V)
476
  aspect_ratio = gr.Dropdown(
 
498
  ### πŸ“‹ Fixed Settings
499
  - **Duration**: 5 seconds
500
  - **Resolution**: 480p
 
501
  - **Watermark**: {watermark_info}
502
  """)
503
 
 
511
  )
512
 
513
  # Generate button
514
+ generate_btn = gr.Button("🎬 Generate Video", variant="primary", size="lg")
515
 
516
  # Results display
517
  with gr.Column():
 
563
 
564
  ### Image to Video Tips
565
 
566
+ **Recommended: Use Image URLs**
567
+ 1. Upload your image to [imgur.com](https://imgur.com) (easiest, no account needed)
568
+ 2. After upload, right-click image β†’ "Copy image address"
569
+ 3. Paste URL in the Image URL field
570
+
571
+ **Image Upload Limitations:**
572
+ - Uploaded images are compressed to max 2MB
573
+ - Large images (>30MB) may still fail after compression
574
+ - If upload fails, use an image hosting service instead
575
+
576
+ **Free Image Hosting Services:**
577
+ - [imgur.com](https://imgur.com) - No account needed, easiest
578
+ - [imgbb.com](https://imgbb.com) - Simple interface
579
+ - [postimages.org](https://postimages.org) - Direct links
580
 
581
  ### API Information
582
 
583
+ - **Models**: ByteDance Seedance v1
 
 
584
  - **Resolution**: 480p
585
 
586
  ### Prompt Writing Tips
 
597
  ["Text to Video", "A cat walking gracefully across a sunlit room", "", None, "16:9", -1],
598
  ["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],
599
  ["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],
600
+ ["Image to Video", "In a wide shot, the camera slowly zooms in while maintaining focus. Natural movement brings the scene to life.", "https://d2g64w682n9w0w.cloudfront.net/media/images/1750659000869310688_vGUb740X.jpg", None, "16:9", -1],
601
  ],
602
  inputs=[mode, prompt, image_url_input, image_file_input, aspect_ratio, seed],
603
  label="Example Prompts"