import gradio as gr import google.generativeai as genai import os import re import subprocess import tempfile import shutil from pathlib import Path import sys # Check if API key is available api_key = os.getenv("GEMINI_API_KEY") if not api_key: print("⚠️ Warning: GEMINI_API_KEY not found in environment variables") print("Please set your Gemini API key in the Hugging Face Spaces secrets") # Configure Gemini API genai.configure(api_key=api_key) model = genai.GenerativeModel( model_name="gemini-1.5-flash", # Using more stable model for deployment generation_config={ "temperature": 0.3, "top_p": 0.95, "top_k": 40, "max_output_tokens": 8000, # Reduced for better performance } ) def generate_video(topic, duration_minutes=1): if not api_key: return None, "❌ Error: GEMINI_API_KEY not configured. Please set it in Hugging Face Spaces secrets." try: # Step 1: Generate plan for Manim animation planning_prompt = f""" You are a professional with deep expertise in Manim CE 0.18.0. Your task is to design a presentation-style video using Manim for the topic: "{topic}" Guidelines: - The video should consist of **slides of text** with **smooth transitions** between them. - Structure the scene logically with 3-5 main points maximum. - Target duration: {duration_minutes} minute(s). - Use **simple text objects only**, no equations, graphs, or complex animations. - Keep text concise and readable. - Include details like: - Text to be shown (keep it short and clear) - When to fade in/out - Slide duration (2-4 seconds per slide) - Transitions (FadeIn, FadeOut, Transform) - Do not output any code here — only the **presentation plan**, step by step. Output Format: Scene 1: - Text: "Title: {topic}" - Action: FadeIn - Duration: 3 seconds Scene 2: - Text: "Key Point 1: [brief description]" - Action: Transform from previous - Duration: 4 seconds ... """ plan_response = model.generate_content(planning_prompt) plan = plan_response.text.strip() # Step 2: Generate Manim code based on the plan code_prompt = f""" Using this animation plan: {plan} Write a complete Manim CE 0.18.0 compatible Python file with a class called `VideoScene` that inherits from Scene. Critical Requirements: - Import all necessary modules: from manim import * - Use ONLY basic text objects and simple transitions - Background should be WHITE: self.camera.background_color = WHITE - Text should be BLACK for contrast - Keep text size reasonable (font_size=24 to 36) - Use FadeIn, FadeOut, and Transform only - Add self.clear() between slides to avoid overlapping text - Include proper self.wait() durations - Make sure the class is properly indented and structured - NO complex animations, NO equations, NO images - Keep it simple and clean Example structure: ```python from manim import * class VideoScene(Scene): def construct(self): self.camera.background_color = WHITE # Title slide title = Text("Your Title", font_size=36, color=BLACK) self.play(FadeIn(title)) self.wait(3) self.play(FadeOut(title)) self.clear() # Next slide... ``` Output: Just the complete Python code, no explanations. """ final_code = model.generate_content(code_prompt) raw_code = final_code.text.strip() # Clean the code cleaned_code = re.sub(r"^(```|''')python\s*", "", raw_code) cleaned_code = re.sub(r"(```|''')\s*$", "", cleaned_code) # Create temporary directory for this generation with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Save the generated code manim_file = temp_path / "generated_video.py" with open(manim_file, "w", encoding="utf-8") as f: f.write(cleaned_code) # Run manim to generate video with error handling try: # Use low quality for faster generation in cloud environment result = subprocess.run([ "manim", "-pql", "--fps", "15", str(manim_file), "VideoScene" ], cwd=temp_dir, capture_output=True, text=True, check=True, timeout=120 # 2 minute timeout ) # Find the generated video file media_dir = temp_path / "media" video_files = list(media_dir.rglob("*.mp4")) if video_files: video_file = video_files[0] # Copy to a location accessible by Gradio output_file = f"video_{abs(hash(topic)) % 10000}.mp4" shutil.copy2(video_file, output_file) success_msg = f"✅ Video generated successfully!\n\n**Plan:**\n{plan}\n\n**Generated Code:**\n```python\n{cleaned_code}\n```" return output_file, success_msg else: error_msg = f"❌ No video file found after generation.\n\n**Plan:**\n{plan}\n\n**Generated Code:**\n```python\n{cleaned_code}\n```\n\n**Manim Output:**\n{result.stdout}" return None, error_msg except subprocess.TimeoutExpired: return None, f"❌ Video generation timed out. Try a shorter duration or simpler topic.\n\n**Plan:**\n{plan}" except subprocess.CalledProcessError as e: error_msg = f"❌ Manim generation failed:\n{e.stderr}\n\n**Plan:**\n{plan}\n\n**Generated Code:**\n```python\n{cleaned_code}\n```" return None, error_msg except Exception as e: return None, f"❌ Unexpected error: {str(e)}" # Create Gradio interface with gr.Blocks(title="AI Video Generator", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎬 AI Video Generator Generate educational videos using AI and Manim animations. Simply enter a topic and get a professional presentation-style video! **Features:** - 🤖 AI-powered content generation using Gemini - 🎨 Professional Manim animations - ⏱️ Customizable duration (1-3 minutes) - 📱 Clean presentation style **Note:** Video generation may take 1-3 minutes depending on complexity. """) with gr.Row(): with gr.Column(scale=2): topic_input = gr.Textbox( label="📝 Video Topic", placeholder="Enter an educational topic (e.g., 'Introduction to Python', 'Basic Math Concepts')", value="Introduction to Python Programming", lines=2 ) duration_input = gr.Slider( minimum=1, maximum=3, value=1, step=1, label="⏱️ Duration (minutes)", info="Shorter durations generate faster" ) generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg") with gr.Column(scale=1): gr.Markdown(""" ### 💡 Tips for Best Results: - Use clear, educational topics - Avoid overly complex subjects - Shorter durations work better - Be patient - generation takes time ### 🎯 Great Topic Examples: - "Introduction to Python" - "Basic Data Structures" - "How Photosynthesis Works" - "Understanding Fractions" - "Solar System Overview" """) with gr.Row(): with gr.Column(): video_output = gr.Video( label="🎥 Generated Video", height=400 ) with gr.Column(): log_output = gr.Textbox( label="📋 Generation Details", lines=15, max_lines=25, show_copy_button=True, placeholder="Generation logs and AI-created content will appear here..." ) # Progress indicator with gr.Row(): status_text = gr.Textbox( label="📊 Status", value="Ready to generate video", interactive=False ) def update_status(message): return message def generate_with_status(topic, duration): yield None, "🔄 Starting video generation...", "🔄 Initializing AI content creation..." try: result_video, result_log = generate_video(topic, duration) if result_video: yield result_video, result_log, "✅ Video generated successfully!" else: yield None, result_log, "❌ Video generation failed" except Exception as e: yield None, f"❌ Error: {str(e)}", "❌ Generation failed with error" generate_btn.click( fn=generate_with_status, inputs=[topic_input, duration_input], outputs=[video_output, log_output, status_text], show_progress=True ) if __name__ == "__main__": demo.launch( share=False, server_name="0.0.0.0", server_port=7860, show_error=True )