File size: 9,785 Bytes
1dfc9df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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
    )