Spaces:
Sleeping
Sleeping
| """ | |
| 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) | |
| 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, | |
| ) | |