Upload app.py
Browse files
app.py
CHANGED
|
@@ -280,213 +280,320 @@ def create_interface():
|
|
| 280 |
|
| 281 |
@spaces.GPU(timeout=120) # 2 minute timeout for ZeroGPU
|
| 282 |
def generate_commercial(
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
"""
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
try:
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
</div>
|
| 306 |
-
|
| 307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
script_prompt=script_text or "Back to '87",
|
| 314 |
-
duration=duration_val,
|
| 315 |
-
voice_style=voice_style,
|
| 316 |
-
seed=seed_val
|
| 317 |
)
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
yield progress_html, None, None, None
|
| 330 |
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
)
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
audio=False,
|
| 355 |
-
verbose=False,
|
| 356 |
-
logger=None
|
| 357 |
)
|
| 358 |
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
# Update progress
|
| 364 |
-
progress_html = """
|
| 365 |
-
<div class="progress-info">
|
| 366 |
-
<strong>🎬 Generating Commercial...</strong><br>
|
| 367 |
-
<div style="margin-top: 0.5rem;">
|
| 368 |
-
<div>✅ Script generated</div>
|
| 369 |
-
<div>✅ Video generated</div>
|
| 370 |
-
<div>🎤 Generating audio...</div>
|
| 371 |
-
</div>
|
| 372 |
-
</div>
|
| 373 |
-
"""
|
| 374 |
-
yield progress_html, None, None, None
|
| 375 |
-
|
| 376 |
-
# Generate audio
|
| 377 |
-
voiceover_text = " ".join([seg.text for seg in generated_script.segments])
|
| 378 |
-
sr_voice, wav_voice = synth_voice(
|
| 379 |
-
text=voiceover_text,
|
| 380 |
-
voice_prompt=voice_style,
|
| 381 |
-
model_name=audio_model,
|
| 382 |
-
device=DEVICE
|
| 383 |
)
|
| 384 |
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
sr_final = sr_voice
|
| 393 |
-
stereo_audio = np.stack([wav_voice, wav_voice], axis=1)
|
| 394 |
-
|
| 395 |
-
# Save audio
|
| 396 |
-
audio_path = os.path.join(tmpdir, "audio.wav")
|
| 397 |
-
write_wav(audio_path, sr_final, stereo_audio)
|
| 398 |
-
|
| 399 |
-
# Update progress
|
| 400 |
-
progress_html = """
|
| 401 |
-
<div class="progress-info">
|
| 402 |
-
<strong>🎬 Generating Commercial...</strong><br>
|
| 403 |
-
<div style="margin-top: 0.5rem;">
|
| 404 |
-
<div>✅ Script generated</div>
|
| 405 |
-
<div>✅ Video generated</div>
|
| 406 |
-
<div>✅ Audio generated</div>
|
| 407 |
-
<div>🔄 Synchronizing audio and video...</div>
|
| 408 |
-
</div>
|
| 409 |
-
</div>
|
| 410 |
-
"""
|
| 411 |
-
yield progress_html, None, None, None
|
| 412 |
-
|
| 413 |
-
# Synchronize audio and video
|
| 414 |
-
# Clean brand name for filename (remove spaces and special characters)
|
| 415 |
-
clean_brand = "".join(c for c in brand_name if c.isalnum() or c in (' ', '-', '_')).rstrip()
|
| 416 |
-
clean_brand = clean_brand.replace(' ', '_')
|
| 417 |
-
if not clean_brand:
|
| 418 |
-
clean_brand = "commercial"
|
| 419 |
-
final_video_path = os.path.join(tmpdir, f"{clean_brand}_commercial.mp4")
|
| 420 |
-
|
| 421 |
-
try:
|
| 422 |
-
sync_manager.synchronize_media(
|
| 423 |
-
video_path=retro_video_path,
|
| 424 |
-
audio_path=audio_path,
|
| 425 |
-
output_path=final_video_path,
|
| 426 |
-
prefer_audio_duration=True
|
| 427 |
-
)
|
| 428 |
-
except Exception as e:
|
| 429 |
-
logger.error(f"Sync failed, using simple mux: {e}")
|
| 430 |
-
# Fallback to simple muxing
|
| 431 |
-
from utils_video import mux_audio
|
| 432 |
-
mux_audio(retro_video_path, audio_path, final_video_path)
|
| 433 |
-
|
| 434 |
-
# Validate sync and file existence
|
| 435 |
-
if os.path.exists(final_video_path):
|
| 436 |
-
is_synced, sync_diff = sync_manager.validate_sync(final_video_path, final_video_path)
|
| 437 |
-
else:
|
| 438 |
-
logger.error("Final video file was not created")
|
| 439 |
-
is_synced, sync_diff = False, float('inf')
|
| 440 |
-
|
| 441 |
-
# Format script output
|
| 442 |
-
script_lines = []
|
| 443 |
-
for i, segment in enumerate(generated_script.segments, 1):
|
| 444 |
-
script_lines.append(f"{i}. {segment.timing_marker} {segment.text}")
|
| 445 |
-
|
| 446 |
-
script_output = "\n".join(script_lines) + f"\n\nTAGLINE: {generated_script.tagline}"
|
| 447 |
-
|
| 448 |
-
# Final progress
|
| 449 |
-
sync_status = "✅ Perfect sync" if is_synced else f"⚠️ Sync diff: {sync_diff:.3f}s"
|
| 450 |
-
progress_html = f"""
|
| 451 |
-
<div class="progress-info">
|
| 452 |
-
<strong>🎉 Commercial Complete!</strong><br>
|
| 453 |
-
<div style="margin-top: 0.5rem;">
|
| 454 |
-
<div>✅ Script generated ({generated_script.word_count} words)</div>
|
| 455 |
-
<div>✅ Video generated ({num_frames} frames)</div>
|
| 456 |
-
<div>✅ Audio generated ({len(stereo_audio)/sr_final:.1f}s)</div>
|
| 457 |
-
<div>{sync_status}</div>
|
| 458 |
-
</div>
|
| 459 |
-
</div>
|
| 460 |
-
"""
|
| 461 |
-
|
| 462 |
-
yield progress_html, final_video_path, script_output, final_video_path
|
| 463 |
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
""
|
| 476 |
-
|
| 477 |
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
],
|
| 491 |
outputs=[progress_info, output_video, output_script, download_btn]
|
| 492 |
)
|
|
|
|
| 280 |
|
| 281 |
@spaces.GPU(timeout=120) # 2 minute timeout for ZeroGPU
|
| 282 |
def generate_commercial(
|
| 283 |
+
brand_name: str,
|
| 284 |
+
structure_text: str,
|
| 285 |
+
script_text: str,
|
| 286 |
+
duration_val: int,
|
| 287 |
+
voice_style: str,
|
| 288 |
+
music_enabled: bool,
|
| 289 |
+
video_model: str,
|
| 290 |
+
audio_model: str,
|
| 291 |
+
vhs_intensity: float,
|
| 292 |
+
seed_val: int
|
| 293 |
+
) -> Tuple[str, str, str]:
|
| 294 |
+
"""
|
| 295 |
+
Generate a complete retro commercial with perfect sync.
|
| 296 |
+
"""
|
| 297 |
+
try:
|
| 298 |
+
# Update progress
|
| 299 |
+
progress_html = """
|
| 300 |
+
<div class="progress-info">
|
| 301 |
+
<strong>🎬 Generating Commercial...</strong><br>
|
| 302 |
+
<div style="margin-top: 0.5rem;">
|
| 303 |
+
<div>📝 Generating script with AI...</div>
|
| 304 |
+
</div>
|
| 305 |
+
</div>
|
| 306 |
+
"""
|
| 307 |
+
yield progress_html, None, None, None
|
| 308 |
+
|
| 309 |
+
# Generate script using LLM
|
| 310 |
+
generated_script = script_generator.generate_script(
|
| 311 |
+
brand=brand_name or "Brand",
|
| 312 |
+
structure=structure_text or "Montage → Close-up → Logo",
|
| 313 |
+
script_prompt=script_text or "Back to '87",
|
| 314 |
+
duration=duration_val,
|
| 315 |
+
voice_style=voice_style,
|
| 316 |
+
seed=seed_val
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
# Update progress
|
| 320 |
+
progress_html = """
|
| 321 |
+
<div class="progress-info">
|
| 322 |
+
<strong>🎬 Generating Commercial...</strong><br>
|
| 323 |
+
<div style="margin-top: 0.5rem;">
|
| 324 |
+
<div>✅ Script generated</div>
|
| 325 |
+
<div>🎥 Generating video...</div>
|
| 326 |
+
</div>
|
| 327 |
+
</div>
|
| 328 |
+
"""
|
| 329 |
+
yield progress_html, None, None, None
|
| 330 |
+
|
| 331 |
+
# Create temporary directory
|
| 332 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 333 |
+
# Generate video
|
| 334 |
+
video_prompt = f"{structure_text}. {script_text}. 1980s commercial, VHS texture, soft lighting, bold retro titles, 4:3, brand {brand_name}"
|
| 335 |
+
|
| 336 |
+
# Calculate optimal frame count
|
| 337 |
+
num_frames = min(sync_manager.get_optimal_frame_count(duration_val, DEFAULT_FPS), 16) # Cap for ZeroGPU
|
| 338 |
+
|
| 339 |
+
clip = synth_t2v(
|
| 340 |
+
prompt=video_prompt,
|
| 341 |
+
seed=seed_val,
|
| 342 |
+
num_frames=num_frames,
|
| 343 |
+
fps=DEFAULT_FPS,
|
| 344 |
+
device=DEVICE,
|
| 345 |
+
model_name=video_model
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
# Save raw video
|
| 349 |
+
raw_video_path = os.path.join(tmpdir, "raw.mp4")
|
| 350 |
+
clip.write_videofile(
|
| 351 |
+
raw_video_path,
|
| 352 |
+
fps=DEFAULT_FPS,
|
| 353 |
+
codec='libx264',
|
| 354 |
+
audio=False,
|
| 355 |
+
verbose=False,
|
| 356 |
+
logger=None
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
# Apply retro filters
|
| 360 |
+
retro_video_path = os.path.join(tmpdir, "retro.mp4")
|
| 361 |
+
apply_retro_filters(raw_video_path, retro_video_path, intensity=vhs_intensity)
|
| 362 |
+
|
| 363 |
+
# Update progress
|
| 364 |
+
progress_html = """
|
| 365 |
+
<div class="progress-info">
|
| 366 |
+
<strong>🎬 Generating Commercial...</strong><br>
|
| 367 |
+
<div style="margin-top: 0.5rem;">
|
| 368 |
+
<div>✅ Script generated</div>
|
| 369 |
+
<div>✅ Video generated</div>
|
| 370 |
+
<div>🎤 Generating audio...</div>
|
| 371 |
+
</div>
|
| 372 |
+
</div>
|
| 373 |
"""
|
| 374 |
+
yield progress_html, None, None, None
|
| 375 |
+
|
| 376 |
+
# Generate audio
|
| 377 |
+
voiceover_text = " ".join([seg.text for seg in generated_script.segments])
|
| 378 |
+
sr_voice, wav_voice = synth_voice(
|
| 379 |
+
text=voiceover_text,
|
| 380 |
+
voice_prompt=voice_style,
|
| 381 |
+
model_name=audio_model,
|
| 382 |
+
device=DEVICE
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
# Add background music if requested
|
| 386 |
+
if music_enabled:
|
| 387 |
+
sr_music, wav_music = retro_bed(clip.duration)
|
| 388 |
+
sr_final, stereo_audio = mix_to_stereo(
|
| 389 |
+
sr_voice, wav_voice, sr_music, wav_music, bed_gain=0.3
|
| 390 |
+
)
|
| 391 |
+
else:
|
| 392 |
+
sr_final = sr_voice
|
| 393 |
+
stereo_audio = np.stack([wav_voice, wav_voice], axis=1)
|
| 394 |
+
|
| 395 |
+
# Save audio
|
| 396 |
+
audio_path = os.path.join(tmpdir, "audio.wav")
|
| 397 |
+
write_wav(audio_path, sr_final, stereo_audio)
|
| 398 |
+
|
| 399 |
+
# Update progress
|
| 400 |
+
progress_html = """
|
| 401 |
+
<div class="progress-info">
|
| 402 |
+
<strong>🎬 Generating Commercial...</strong><br>
|
| 403 |
+
<div style="margin-top: 0.5rem;">
|
| 404 |
+
<div>✅ Script generated</div>
|
| 405 |
+
<div>✅ Video generated</div>
|
| 406 |
+
<div>✅ Audio generated</div>
|
| 407 |
+
<div>🔄 Synchronizing audio and video...</div>
|
| 408 |
+
</div>
|
| 409 |
+
</div>
|
| 410 |
"""
|
| 411 |
+
yield progress_html, None, None, None
|
| 412 |
+
|
| 413 |
+
# Synchronize audio and video
|
| 414 |
+
# Clean brand name for filename (remove spaces and special characters)
|
| 415 |
+
clean_brand = "".join(c for c in brand_name if c.isalnum() or c in (' ', '-', '_')).rstrip()
|
| 416 |
+
clean_brand = clean_brand.replace(' ', '_')
|
| 417 |
+
if not clean_brand:
|
| 418 |
+
clean_brand = "commercial"
|
| 419 |
+
final_video_path = os.path.join(tmpdir, f"{clean_brand}_commercial.mp4")
|
| 420 |
+
|
| 421 |
try:
|
| 422 |
+
sync_manager.synchronize_media(
|
| 423 |
+
video_path=retro_video_path,
|
| 424 |
+
audio_path=audio_path,
|
| 425 |
+
output_path=final_video_path,
|
| 426 |
+
prefer_audio_duration=True
|
| 427 |
+
)
|
| 428 |
+
except Exception as e:
|
| 429 |
+
logger.error(f"Sync failed, using simple mux: {e}")
|
| 430 |
+
# Fallback to simple muxing
|
| 431 |
+
from utils_video import mux_audio
|
| 432 |
+
mux_audio(retro_video_path, audio_path, final_video_path)
|
| 433 |
+
|
| 434 |
+
# Validate sync and file existence
|
| 435 |
+
if os.path.exists(final_video_path):
|
| 436 |
+
is_synced, sync_diff = sync_manager.validate_sync(final_video_path, final_video_path)
|
| 437 |
+
else:
|
| 438 |
+
logger.error("Final video file was not created")
|
| 439 |
+
is_synced, sync_diff = False, float('inf')
|
| 440 |
+
|
| 441 |
+
# Format script output
|
| 442 |
+
script_lines = []
|
| 443 |
+
for i, segment in enumerate(generated_script.segments, 1):
|
| 444 |
+
script_lines.append(f"{i}. {segment.timing_marker} {segment.text}")
|
| 445 |
+
|
| 446 |
+
script_output = "\n".join(script_lines) + f"\n\nTAGLINE: {generated_script.tagline}"
|
| 447 |
+
|
| 448 |
+
# Final progress
|
| 449 |
+
sync_status = "✅ Perfect sync" if is_synced else f"⚠️ Sync diff: {sync_diff:.3f}s"
|
| 450 |
+
progress_html = f"""
|
| 451 |
+
<div class="progress-info">
|
| 452 |
+
<strong>🎉 Commercial Complete!</strong><br>
|
| 453 |
+
<div style="margin-top: 0.5rem;">
|
| 454 |
+
<div>✅ Script generated ({generated_script.word_count} words)</div>
|
| 455 |
+
<div>✅ Video generated ({num_frames} frames)</div>
|
| 456 |
+
<div>✅ Audio generated ({len(stereo_audio)/sr_final:.1f}s)</div>
|
| 457 |
+
<div>{sync_status}</div>
|
| 458 |
</div>
|
| 459 |
+
</div>
|
| 460 |
+
"""
|
| 461 |
+
|
| 462 |
+
yield progress_html, final_video_path, script_output, final_video_path
|
| 463 |
+
|
| 464 |
+
except Exception as e:
|
| 465 |
+
logger.error(f"Commercial generation failed: {e}")
|
| 466 |
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
| 467 |
+
error_html = f"""
|
| 468 |
+
<div class="error-info">
|
| 469 |
+
<strong>❌ Generation Failed</strong><br>
|
| 470 |
+
<div style="margin-top: 0.5rem; color: #666;">
|
| 471 |
+
Error: {str(e)}<br>
|
| 472 |
+
Please try again with different parameters or check the logs.
|
| 473 |
+
</div>
|
| 474 |
+
</div>
|
| 475 |
+
"""
|
| 476 |
+
yield error_html, None, None, None
|
| 477 |
+
|
| 478 |
+
def create_interface():
|
| 479 |
+
"""Create the Gradio interface."""
|
| 480 |
+
with gr.Blocks(
|
| 481 |
+
title="EceMotion Pictures",
|
| 482 |
+
theme=gr.themes.Soft(),
|
| 483 |
+
css="""
|
| 484 |
+
.progress-info {
|
| 485 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 486 |
+
color: white;
|
| 487 |
+
padding: 1rem;
|
| 488 |
+
border-radius: 10px;
|
| 489 |
+
margin: 1rem 0;
|
| 490 |
+
}
|
| 491 |
+
.error-info {
|
| 492 |
+
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
|
| 493 |
+
color: white;
|
| 494 |
+
padding: 1rem;
|
| 495 |
+
border-radius: 10px;
|
| 496 |
+
margin: 1rem 0;
|
| 497 |
+
}
|
| 498 |
+
"""
|
| 499 |
+
) as demo:
|
| 500 |
+
gr.Markdown("""
|
| 501 |
+
# 🎬 EceMotion Pictures
|
| 502 |
+
## AI-Powered 1980s Style Commercial Generator
|
| 503 |
+
|
| 504 |
+
Create authentic retro commercials with perfect audio-video synchronization!
|
| 505 |
+
""")
|
| 506 |
+
|
| 507 |
+
with gr.Row():
|
| 508 |
+
with gr.Column(scale=1):
|
| 509 |
+
gr.Markdown("### Commercial Setup")
|
| 510 |
|
| 511 |
+
brand = gr.Textbox(
|
| 512 |
+
label="Brand Name",
|
| 513 |
+
placeholder="Enter your brand name...",
|
| 514 |
+
value="EceMotion Pictures"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
)
|
| 516 |
|
| 517 |
+
structure = gr.Dropdown(
|
| 518 |
+
label="Commercial Structure",
|
| 519 |
+
choices=[
|
| 520 |
+
"Montage → Close-up → Logo stinger",
|
| 521 |
+
"Problem → Solution → Call to action",
|
| 522 |
+
"Story → Product reveal → Tagline",
|
| 523 |
+
"Before/After → Benefits → Brand"
|
| 524 |
+
],
|
| 525 |
+
value="Montage → Close-up → Logo stinger"
|
| 526 |
+
)
|
|
|
|
| 527 |
|
| 528 |
+
script_prompt = gr.Textbox(
|
| 529 |
+
label="Script Hook",
|
| 530 |
+
placeholder="Enter your script prompt...",
|
| 531 |
+
value="Remember when technology was simple?"
|
| 532 |
+
)
|
| 533 |
+
|
| 534 |
+
duration = gr.Slider(
|
| 535 |
+
label="Duration (seconds)",
|
| 536 |
+
minimum=5,
|
| 537 |
+
maximum=15,
|
| 538 |
+
value=10,
|
| 539 |
+
step=1
|
| 540 |
+
)
|
| 541 |
+
|
| 542 |
+
with gr.Row():
|
| 543 |
+
voice = gr.Dropdown(
|
| 544 |
+
label="Voice Style",
|
| 545 |
+
choices=["Announcer '80s", "Friendly '80s", "Dramatic '80s", "Upbeat '80s"],
|
| 546 |
+
value="Announcer '80s"
|
| 547 |
)
|
| 548 |
+
music = gr.Checkbox(label="Background Music", value=True)
|
| 549 |
+
|
| 550 |
+
with gr.Accordion("Advanced Settings", open=False):
|
| 551 |
+
model_video = gr.Dropdown(
|
| 552 |
+
label="Video Model",
|
| 553 |
+
choices=["damo-vilab/text-to-video-ms-1.7b", "THUDM/CogVideoX-5b"],
|
| 554 |
+
value="damo-vilab/text-to-video-ms-1.7b"
|
|
|
|
|
|
|
|
|
|
| 555 |
)
|
| 556 |
|
| 557 |
+
model_audio = gr.Dropdown(
|
| 558 |
+
label="Audio Model",
|
| 559 |
+
choices=["parler-tts/parler-tts-mini-v1", "SWivid/F5-TTS"],
|
| 560 |
+
value="parler-tts/parler-tts-mini-v1"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
)
|
| 562 |
|
| 563 |
+
vhs_intensity = gr.Slider(
|
| 564 |
+
label="VHS Effect Intensity",
|
| 565 |
+
minimum=0.0,
|
| 566 |
+
maximum=1.0,
|
| 567 |
+
value=0.5,
|
| 568 |
+
step=0.1
|
| 569 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 570 |
|
| 571 |
+
seed = gr.Number(label="Random Seed", value=42, precision=0)
|
| 572 |
+
|
| 573 |
+
with gr.Row():
|
| 574 |
+
roll_btn = gr.Button("🎲 Roll Script", variant="secondary")
|
| 575 |
+
generate_btn = gr.Button("🎬 Generate Commercial", variant="primary")
|
| 576 |
+
|
| 577 |
+
with gr.Column(scale=2):
|
| 578 |
+
gr.Markdown("### Generated Commercial")
|
| 579 |
+
|
| 580 |
+
progress_info = gr.HTML()
|
| 581 |
+
output_video = gr.Video(label="Commercial Preview")
|
| 582 |
+
output_script = gr.Textbox(label="Generated Script", lines=10)
|
| 583 |
+
download_btn = gr.DownloadButton("📥 Download Commercial", visible=False)
|
| 584 |
|
| 585 |
+
# Connect event handlers
|
| 586 |
+
roll_btn.click(
|
| 587 |
+
roll_script_suggestion,
|
| 588 |
+
inputs=[structure, seed],
|
| 589 |
+
outputs=[script_prompt]
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
generate_btn.click(
|
| 593 |
+
generate_commercial,
|
| 594 |
+
inputs=[
|
| 595 |
+
brand, structure, script_prompt, duration, voice, music,
|
| 596 |
+
model_video, model_audio, vhs_intensity, seed
|
| 597 |
],
|
| 598 |
outputs=[progress_info, output_video, output_script, download_btn]
|
| 599 |
)
|