| import gradio as gr |
|
|
| from inference import generate_video |
|
|
| DESCRIPTION = """ |
| # 🎬 Wan2.1 Text-to-Video |
| |
| Generate cinematic AI videos with Wan2.1-T2V-1.3B running on Hugging Face ZeroGPU. |
| |
| ### Features |
| |
| - ⚡ ZeroGPU |
| - 🎥 480P Output |
| - ⏱️ Adjustable Video Length |
| - 🎲 Random Seed |
| - 📥 MP4 Download |
| """ |
|
|
| CSS = """ |
| footer { |
| visibility: hidden; |
| } |
| |
| .gradio-container { |
| max-width: 1200px !important; |
| margin: auto; |
| } |
| |
| #generate-btn { |
| height: 56px; |
| font-size: 18px; |
| font-weight: 600; |
| } |
| |
| video { |
| border-radius: 12px; |
| } |
| """ |
|
|
| EXAMPLES = [ |
| ["A cinematic drone shot over snowy mountains during sunrise"], |
| ["A futuristic cyberpunk street at night with neon lights and rain"], |
| ["A golden retriever surfing giant ocean waves"], |
| ["An astronaut walking through an ancient Egyptian temple"], |
| ] |
|
|
|
|
| def update_length(seconds): |
| fps = 16 |
| frames = int(seconds * fps) |
|
|
| |
| frames = ((frames - 1) // 4) * 4 + 1 |
| frames = max(33, min(frames, 161)) |
|
|
| return f"Estimated frames: **{frames}**" |
|
|
|
|
| with gr.Blocks(title="Wan2.1 Text-to-Video") as demo: |
|
|
| gr.Markdown(DESCRIPTION) |
|
|
| with gr.Row(): |
|
|
| with gr.Column(scale=2): |
|
|
| prompt = gr.Textbox( |
| label="Prompt", |
| lines=5, |
| placeholder="Describe the video you want to generate..." |
| ) |
|
|
| negative_prompt = gr.Textbox( |
| label="Negative Prompt", |
| lines=3, |
| value="low quality, blurry, watermark, logo, text, distorted" |
| ) |
|
|
| gr.Examples( |
| examples=EXAMPLES, |
| inputs=prompt, |
| ) |
|
|
| length = gr.Slider( |
| minimum=2, |
| maximum=10, |
| value=5, |
| step=1, |
| label="Video Length (seconds)" |
| ) |
|
|
| frame_info = gr.Markdown("Estimated frames: **81**") |
|
|
| length.change( |
| fn=update_length, |
| inputs=length, |
| outputs=frame_info, |
| ) |
|
|
| with gr.Accordion("Advanced Settings", open=False): |
|
|
| steps = gr.Slider( |
| minimum=20, |
| maximum=60, |
| value=30, |
| step=1, |
| label="Inference Steps", |
| ) |
|
|
| guidance = gr.Slider( |
| minimum=1, |
| maximum=10, |
| value=5, |
| step=0.5, |
| label="Guidance Scale", |
| ) |
|
|
| seed = gr.Number( |
| value=-1, |
| precision=0, |
| label="Seed (-1 = Random)", |
| ) |
|
|
| generate_btn = gr.Button( |
| "🎬 Generate Video", |
| variant="primary", |
| elem_id="generate-btn", |
| ) |
|
|
| with gr.Column(scale=1): |
|
|
| output_video = gr.Video( |
| label="Generated Video", |
| autoplay=True, |
| ) |
|
|
| status = gr.Textbox( |
| label="Status", |
| interactive=False, |
| ) |
|
|
| generate_btn.click( |
| fn=generate_video, |
| inputs=[ |
| prompt, |
| negative_prompt, |
| steps, |
| guidance, |
| length, |
| seed, |
| ], |
| outputs=[ |
| output_video, |
| status, |
| ], |
| concurrency_limit=1, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.queue( |
| default_concurrency_limit=1, |
| max_size=20, |
| ).launch( |
| theme=gr.themes.Soft(), |
| css=CSS, |
| ) |