|
|
import gradio as gr |
|
|
import os |
|
|
from huggingface_hub import InferenceClient |
|
|
import tempfile |
|
|
import shutil |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
client = InferenceClient( |
|
|
provider="fal-ai", |
|
|
api_key=os.environ.get("HF_TOKEN"), |
|
|
bill_to="huggingface", |
|
|
) |
|
|
|
|
|
def text_to_video(prompt, duration=5, aspect_ratio="16:9", resolution="720p", profile: gr.OAuthProfile | None = None): |
|
|
"""Generate video from text prompt""" |
|
|
try: |
|
|
if profile is None: |
|
|
return None, "❌ Click Sign in with Hugging Face button to use this app for free" |
|
|
|
|
|
if not prompt or prompt.strip() == "": |
|
|
return None, "Please enter a text prompt" |
|
|
|
|
|
|
|
|
video = client.text_to_video( |
|
|
prompt, |
|
|
model="akhaliq/veo3.1-fast", |
|
|
) |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file: |
|
|
tmp_file.write(video) |
|
|
video_path = tmp_file.name |
|
|
|
|
|
return video_path, f"✅ Video generated successfully from prompt: '{prompt[:50]}...'" |
|
|
|
|
|
except Exception as e: |
|
|
return None, f"❌ Error generating video: {str(e)}" |
|
|
|
|
|
def image_to_video(image, prompt, duration=5, aspect_ratio="16:9", resolution="720p", profile: gr.OAuthProfile | None = None): |
|
|
"""Generate video from image and prompt""" |
|
|
try: |
|
|
if profile is None: |
|
|
return None, "❌ Click Sign in with Hugging Face button to use this app for free" |
|
|
|
|
|
if image is None: |
|
|
return None, "Please upload an image" |
|
|
|
|
|
if not prompt or prompt.strip() == "": |
|
|
return None, "Please enter a prompt describing the motion" |
|
|
|
|
|
|
|
|
if isinstance(image, str): |
|
|
|
|
|
with open(image, "rb") as image_file: |
|
|
input_image = image_file.read() |
|
|
else: |
|
|
|
|
|
import io |
|
|
from PIL import Image as PILImage |
|
|
|
|
|
|
|
|
if isinstance(image, PILImage.Image): |
|
|
buffer = io.BytesIO() |
|
|
image.save(buffer, format='PNG') |
|
|
input_image = buffer.getvalue() |
|
|
else: |
|
|
|
|
|
pil_image = PILImage.fromarray(image) |
|
|
buffer = io.BytesIO() |
|
|
pil_image.save(buffer, format='PNG') |
|
|
input_image = buffer.getvalue() |
|
|
|
|
|
|
|
|
video = client.image_to_video( |
|
|
input_image, |
|
|
prompt=prompt, |
|
|
model="akhaliq/veo3.1-fast-image-to-video", |
|
|
) |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file: |
|
|
tmp_file.write(video) |
|
|
video_path = tmp_file.name |
|
|
|
|
|
return video_path, f"✅ Video generated successfully with motion: '{prompt[:50]}...'" |
|
|
|
|
|
except Exception as e: |
|
|
return None, f"❌ Error generating video: {str(e)}" |
|
|
|
|
|
def clear_text_tab(): |
|
|
"""Clear text-to-video tab""" |
|
|
return "", None, "" |
|
|
|
|
|
def clear_image_tab(): |
|
|
"""Clear image-to-video tab""" |
|
|
return None, "", None, "" |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
.container { |
|
|
max-width: 1200px; |
|
|
margin: auto; |
|
|
} |
|
|
.header-link { |
|
|
text-decoration: none; |
|
|
color: #2196F3; |
|
|
font-weight: bold; |
|
|
} |
|
|
.header-link:hover { |
|
|
text-decoration: underline; |
|
|
} |
|
|
.status-box { |
|
|
padding: 10px; |
|
|
border-radius: 5px; |
|
|
margin-top: 10px; |
|
|
} |
|
|
.auth-warning { |
|
|
color: #ff6b00; |
|
|
font-weight: bold; |
|
|
text-align: center; |
|
|
margin: 1em 0; |
|
|
padding: 1em; |
|
|
background-color: #fff3e0; |
|
|
border-radius: 5px; |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="AI Video Generator") as demo: |
|
|
gr.Markdown( |
|
|
""" |
|
|
# 🎬 AI Video Generator |
|
|
### Generate stunning videos from text or animate your images with AI |
|
|
#### Powered by VEO 3.1 Fast Model | [Built with anycoder](https://huggingface.co/spaces/akhaliq/anycoder) |
|
|
""" |
|
|
) |
|
|
|
|
|
gr.HTML( |
|
|
""" |
|
|
<div class="auth-warning"> |
|
|
⚠️ You must Sign in with Hugging Face using the button below to use this app. |
|
|
</div> |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
gr.LoginButton() |
|
|
|
|
|
with gr.Tabs() as tabs: |
|
|
|
|
|
with gr.Tab("📝 Text to Video", id=0): |
|
|
gr.Markdown("### Transform your text descriptions into dynamic videos") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
text_prompt = gr.Textbox( |
|
|
label="Text Prompt", |
|
|
placeholder="Describe the video you want to create... (e.g., 'A young man walking on the street during sunset')", |
|
|
lines=4, |
|
|
max_lines=6 |
|
|
) |
|
|
|
|
|
with gr.Accordion("Advanced Settings", open=False): |
|
|
text_duration = gr.Slider( |
|
|
minimum=1, |
|
|
maximum=10, |
|
|
value=5, |
|
|
step=1, |
|
|
label="Duration (seconds)", |
|
|
info="Video duration in seconds" |
|
|
) |
|
|
text_aspect_ratio = gr.Dropdown( |
|
|
choices=["16:9", "9:16", "1:1", "4:3", "21:9"], |
|
|
value="16:9", |
|
|
label="Aspect Ratio", |
|
|
info="Video aspect ratio" |
|
|
) |
|
|
text_resolution = gr.Dropdown( |
|
|
choices=["480p", "720p", "1080p"], |
|
|
value="720p", |
|
|
label="Resolution", |
|
|
info="Video resolution" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
text_generate_btn = gr.Button("🎬 Generate Video", variant="primary", scale=2) |
|
|
text_clear_btn = gr.ClearButton(value="🗑️ Clear", scale=1) |
|
|
|
|
|
text_status = gr.Textbox( |
|
|
label="Status", |
|
|
interactive=False, |
|
|
visible=True, |
|
|
elem_classes=["status-box"] |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
text_video_output = gr.Video( |
|
|
label="Generated Video", |
|
|
autoplay=True, |
|
|
show_download_button=True, |
|
|
height=400 |
|
|
) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
["A serene beach at sunset with gentle waves"], |
|
|
["A bustling city street with neon lights at night"], |
|
|
["A majestic eagle soaring through mountain peaks"], |
|
|
["An astronaut floating in space near the International Space Station"], |
|
|
["Cherry blossoms falling in slow motion in a Japanese garden"], |
|
|
], |
|
|
inputs=text_prompt, |
|
|
label="Example Prompts" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("🖼️ Image to Video", id=1): |
|
|
gr.Markdown("### Bring your static images to life with motion") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
image_input = gr.Image( |
|
|
label="Upload Image", |
|
|
type="pil", |
|
|
height=300 |
|
|
) |
|
|
|
|
|
image_prompt = gr.Textbox( |
|
|
label="Motion Prompt", |
|
|
placeholder="Describe how the image should move... (e.g., 'The cat starts to dance')", |
|
|
lines=3, |
|
|
max_lines=5 |
|
|
) |
|
|
|
|
|
with gr.Accordion("Advanced Settings", open=False): |
|
|
image_duration = gr.Slider( |
|
|
minimum=1, |
|
|
maximum=10, |
|
|
value=5, |
|
|
step=1, |
|
|
label="Duration (seconds)", |
|
|
info="Video duration in seconds" |
|
|
) |
|
|
image_aspect_ratio = gr.Dropdown( |
|
|
choices=["16:9", "9:16", "1:1", "4:3", "21:9"], |
|
|
value="16:9", |
|
|
label="Aspect Ratio", |
|
|
info="Video aspect ratio" |
|
|
) |
|
|
image_resolution = gr.Dropdown( |
|
|
choices=["480p", "720p", "1080p"], |
|
|
value="720p", |
|
|
label="Resolution", |
|
|
info="Video resolution" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
image_generate_btn = gr.Button("🎬 Animate Image", variant="primary", scale=2) |
|
|
image_clear_btn = gr.ClearButton(value="🗑️ Clear", scale=1) |
|
|
|
|
|
image_status = gr.Textbox( |
|
|
label="Status", |
|
|
interactive=False, |
|
|
visible=True, |
|
|
elem_classes=["status-box"] |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
image_video_output = gr.Video( |
|
|
label="Generated Video", |
|
|
autoplay=True, |
|
|
show_download_button=True, |
|
|
height=400 |
|
|
) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
[None, "The person starts walking forward"], |
|
|
[None, "The animal begins to run"], |
|
|
[None, "Camera slowly zooms in while the subject smiles"], |
|
|
[None, "The flowers sway gently in the breeze"], |
|
|
[None, "The clouds move across the sky in time-lapse"], |
|
|
], |
|
|
inputs=[image_input, image_prompt], |
|
|
label="Example Motion Prompts" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Accordion("📖 How to Use", open=False): |
|
|
gr.Markdown( |
|
|
""" |
|
|
### Text to Video: |
|
|
1. Enter a detailed description of the video you want to create |
|
|
2. Optionally adjust advanced settings (duration, aspect ratio, resolution) |
|
|
3. Click "Generate Video" and wait for the AI to create your video |
|
|
4. Download or preview your generated video |
|
|
|
|
|
### Image to Video: |
|
|
1. Upload an image you want to animate |
|
|
2. Describe the motion or action you want to add to the image |
|
|
3. Optionally adjust advanced settings |
|
|
4. Click "Animate Image" to bring your image to life |
|
|
5. Download or preview your animated video |
|
|
|
|
|
### Tips for Better Results: |
|
|
- Be specific and descriptive in your prompts |
|
|
- For image-to-video, describe natural motions that fit the image |
|
|
- Use high-quality input images for better results |
|
|
- Experiment with different prompts to get the desired effect |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
text_generate_btn.click( |
|
|
fn=text_to_video, |
|
|
inputs=[text_prompt, text_duration, text_aspect_ratio, text_resolution], |
|
|
outputs=[text_video_output, text_status], |
|
|
show_progress="full", |
|
|
queue=False, |
|
|
api_name=False, |
|
|
show_api=False |
|
|
) |
|
|
|
|
|
text_clear_btn.click( |
|
|
fn=clear_text_tab, |
|
|
inputs=[], |
|
|
outputs=[text_prompt, text_video_output, text_status], |
|
|
queue=False |
|
|
) |
|
|
|
|
|
image_generate_btn.click( |
|
|
fn=image_to_video, |
|
|
inputs=[image_input, image_prompt, image_duration, image_aspect_ratio, image_resolution], |
|
|
outputs=[image_video_output, image_status], |
|
|
show_progress="full", |
|
|
queue=False, |
|
|
api_name=False, |
|
|
show_api=False |
|
|
) |
|
|
|
|
|
image_clear_btn.click( |
|
|
fn=clear_image_tab, |
|
|
inputs=[], |
|
|
outputs=[image_input, image_prompt, image_video_output, image_status], |
|
|
queue=False |
|
|
) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch( |
|
|
show_api=False, |
|
|
share=False, |
|
|
show_error=True, |
|
|
enable_monitoring=False, |
|
|
quiet=True |
|
|
) |