video-generator / app.py
Codex Local
Guard generation when no GPU is available
4556a27
"""
app.py — Interface Web Locale pour la Génération Vidéo IA (Texte → Vidéo)
Basée sur Gradio | Modèle : Wan 2.1 T2V 1.3B
Lancement :
python app.py
Puis ouvrez votre navigateur à l'adresse : http://127.0.0.1:7860
"""
import os
import sys
from datetime import datetime
import gradio as gr
try:
import spaces
except ImportError:
spaces = None
# Ajouter le dossier scripts au path Python
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "scripts"))
from generate import generate_video
# Dossier de sortie par défaut
OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "outputs")
os.makedirs(OUTPUT_DIR, exist_ok=True)
APP_THEME = gr.themes.Soft()
APP_CSS = """
.title-text { text-align: center; }
.generate-btn { background-color: #6c63ff !important; color: white !important; }
"""
def _gpu_decorator(func):
if spaces is None:
return func
return spaces.GPU(duration=300)(func)
@_gpu_decorator
def run_generation(prompt, negative_prompt, num_frames, num_steps, progress=gr.Progress()):
"""
Fonction principale appelée par l'interface Gradio.
"""
if not prompt or not prompt.strip():
return None, "⚠️ Veuillez entrer un prompt avant de générer."
try:
import torch
except ImportError:
return None, "❌ PyTorch n'est pas disponible dans l'environnement."
if not torch.cuda.is_available():
return (
None,
"❌ Aucun GPU n'est disponible sur ce Space. Activez un hardware GPU/ZeroGPU dans les paramètres Hugging Face avant de lancer une génération."
)
progress(0, desc="Initialisation...")
# Générer un nom de fichier unique basé sur l'horodatage
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"video_{timestamp}.mp4"
output_path = os.path.join(OUTPUT_DIR, output_filename)
progress(0.1, desc="Chargement du modèle (peut prendre quelques minutes la 1ère fois)...")
# Appel à la fonction de génération
result_path = generate_video(
prompt=prompt.strip(),
output_path=output_path,
negative_prompt=negative_prompt.strip() if negative_prompt else "",
num_frames=int(num_frames),
num_inference_steps=int(num_steps),
)
progress(1.0, desc="Terminé !")
if result_path and os.path.exists(result_path):
status_msg = f"✅ Vidéo générée avec succès !\nFichier : {result_path}"
return result_path, status_msg
else:
return None, "❌ La génération a échoué. Vérifiez la console pour les détails."
# ─── Interface Gradio ────────────────────────────────────────────────────────
with gr.Blocks(title="Générateur Vidéo IA") as demo:
gr.Markdown(
"""
# 🎬 Générateur de Vidéo par IA — Texte → Vidéo
Créez des courtes vidéos à partir d'une simple description textuelle, **entièrement en local et gratuitement**.
Modèle utilisé : **Wan 2.1 T2V 1.3B** (open-source, via Hugging Face Diffusers).
""",
elem_classes="title-text"
)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### ✍️ Paramètres de Génération")
prompt_input = gr.Textbox(
label="Prompt (Description de la vidéo)",
placeholder="Ex: Un robot qui marche dans une rue futuriste la nuit, style cinématique, caméra qui se déplace sur le côté, couleurs néon",
lines=4,
)
negative_prompt_input = gr.Textbox(
label="Prompt Négatif (Ce que vous ne voulez PAS voir)",
value="déformé, moche, flou, mauvaise qualité, artefacts, texte, filigrane, membres supplémentaires",
lines=2,
)
with gr.Row():
num_frames_slider = gr.Slider(
minimum=9, maximum=81, step=4, value=25,
label="Nombre d'images (frames)",
info="Utilisez 9, 13, 17, 21, 25... (`num_frames - 1` doit être divisible par 4)"
)
num_steps_slider = gr.Slider(
minimum=10, maximum=50, step=5, value=25,
label="Étapes de diffusion",
info="Plus d'étapes = meilleure qualité, mais plus lent"
)
generate_btn = gr.Button(
"🚀 Générer la Vidéo",
variant="primary",
elem_classes="generate-btn"
)
with gr.Column(scale=1):
gr.Markdown("### 🎥 Résultat")
video_output = gr.Video(label="Vidéo Générée", interactive=False)
status_output = gr.Textbox(label="Statut", interactive=False, lines=2)
# Exemples de prompts
gr.Markdown("### 💡 Exemples de Prompts")
gr.Examples(
examples=[
[
"Un robot qui marche dans une rue futuriste la nuit, style cinématique, caméra qui se déplace sur le côté, couleurs néon",
"déformé, moche, flou, artefacts, texte",
24, 25
],
[
"Un astronaute flottant dans l'espace devant une nébuleuse colorée, style cinématographique, plan large, travelling lent",
"déformé, moche, flou, artefacts, texte",
24, 25
],
[
"Une forêt enchantée avec des fées lumineuses volant entre les arbres, ambiance magique, lumière dorée, plan rapproché",
"déformé, moche, flou, artefacts, texte",
16, 20
],
],
inputs=[prompt_input, negative_prompt_input, num_frames_slider, num_steps_slider],
)
# Connexion du bouton à la fonction
generate_btn.click(
fn=run_generation,
inputs=[prompt_input, negative_prompt_input, num_frames_slider, num_steps_slider],
outputs=[video_output, status_output],
)
gr.Markdown(
"""
---
**Conseils :**
Pour de meilleurs résultats, décrivez précisément : le sujet, l'action, le décor, le style visuel et le mouvement de caméra.
La première génération peut prendre plusieurs minutes (téléchargement du modèle ~6 Go).
"""
)
if __name__ == "__main__":
demo.launch(
server_name=os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"),
server_port=int(os.getenv("GRADIO_SERVER_PORT", "7860")),
share=False, # Mettre True pour obtenir un lien public temporaire (via ngrok)
inbrowser=False,
theme=APP_THEME,
css=APP_CSS,
)