Chand11 commited on
Commit
4e3342e
Β·
verified Β·
1 Parent(s): d18e5b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +361 -139
app.py CHANGED
@@ -1,184 +1,406 @@
1
- import streamlit as st
2
  import subprocess
3
- import tempfile
4
  import re
5
  from pathlib import Path
6
- import google.generativeai as genai
7
- import os
 
 
 
 
 
 
8
 
9
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
10
 
11
- st.set_page_config(page_title="Manim Video Generator", page_icon="🎬")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- def fix_code(code):
14
- """Fix common Manim errors"""
 
 
15
  color_map = {
16
- 'ORANGE_A': 'ORANGE', 'GRAY_A': 'WHITE', 'GREY': 'WHITE',
17
- 'PURPLE_A': 'PURPLE', 'RED_A': 'RED', 'BLUE_A': 'BLUE',
18
- 'BROWN': 'ORANGE', 'GOLD': 'YELLOW'
 
19
  }
20
  for old, new in color_map.items():
21
  code = re.sub(rf'\b{old}\b', new, code)
22
 
 
 
 
 
 
 
 
 
23
  code = re.sub(r'\.get_x_axis_label\([^)]+\)', '', code)
24
  code = re.sub(r'\.get_y_axis_label\([^)]+\)', '', code)
25
- code = re.sub(r'Checkmark\([^)]*\)', 'Circle(radius=0.3, color=GREEN)', code)
26
- code = re.sub(r'font_size=(\d+)', lambda m: f'font_size={min(int(m.group(1)), 36)}', code)
27
 
28
- # Add color=BLACK to Text without color
29
- def add_color(match):
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  text = match.group(0)
31
  if 'color=' not in text:
32
  text = text[:-1] + ', color=BLACK)'
33
  return text
34
- code = re.sub(r'Text\([^)]+\)', add_color, code)
 
 
35
 
36
  return code
37
 
38
- def generate_code(prompt):
39
- """Generate Manim code using Gemini"""
40
- if not GEMINI_API_KEY:
41
- return None, "❌ GEMINI_API_KEY not set in secrets"
 
42
 
43
  try:
44
- genai.configure(api_key=GEMINI_API_KEY)
45
- # Get available models and use the first flash model
46
- models = [m.name for m in genai.list_models() if 'generateContent' in m.supported_generation_methods]
47
- model_name = next((m for m in models if 'flash' in m.lower()), models[0] if models else 'gemini-pro')
48
- model = genai.GenerativeModel(model_name)
49
-
50
- full_prompt = f"""Generate simple Manim code.
51
-
52
- RULES:
53
- 1. Use ONLY: BLUE, RED, GREEN, YELLOW, ORANGE, PURPLE, PINK, TEAL, WHITE, BLACK
54
- 2. Background is WHITE - ALL Text MUST have color=BLACK
55
- 3. Always FadeOut before new text:
56
- text = Text("Hello", color=BLACK)
57
- self.play(Write(text))
58
- self.wait(1)
59
- self.play(FadeOut(text))
60
- 4. Keep simple, 10-15 seconds max
61
- 5. MUST include: from manim import *
62
-
63
- User request: {prompt}
64
-
65
- Generate complete working code:"""
66
-
67
- response = model.generate_content(full_prompt)
68
- return response.text, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  except Exception as e:
70
- return None, str(e)
71
 
72
- def render_video(prompt):
73
- """Render Manim video"""
74
- temp_dir = Path(tempfile.mkdtemp())
 
75
 
76
- # Generate code
77
- with st.spinner("πŸ€– Generating code..."):
78
- code, error = generate_code(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  if error:
81
- st.error(error)
82
- return None, None
83
 
84
- # Extract Python code
85
  if "```python" in code:
86
  code = code.split("```python")[1].split("```")[0]
87
  elif "```" in code:
88
  code = code.split("```")[1].split("```")[0]
89
  code = code.strip()
90
 
91
- # Fix code
92
- code = fix_code(code)
93
-
94
- # Find Scene class
95
- match = re.search(r'class\s+(\w+)\s*\(Scene\)', code)
96
- if not match:
97
- st.error("No Scene class found!")
98
- return None, code
99
-
100
- class_name = match.group(1)
101
-
102
- # Save code
103
- code_file = temp_dir / "animation.py"
104
- code_file.write_text(code)
105
-
106
- # Render video
107
- with st.spinner(f"🎬 Rendering {class_name}... (this takes 1-2 minutes)"):
108
- cmd = f"manim {code_file} {class_name} -ql --disable_caching --media_dir {temp_dir}/media"
109
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=180)
110
 
111
  if result.returncode == 0:
112
- # Find video
113
  videos = list((temp_dir / "media").rglob("*.mp4"))
114
  if videos:
115
- return videos[0], code
116
  else:
117
- st.error("Video file not found after rendering")
118
- st.code(result.stdout, language="text")
119
- return None, code
120
  else:
121
- st.error("Rendering failed!")
122
- st.code(result.stderr, language="text")
123
- return None, code
124
-
125
- # UI
126
- st.title("🎬 Manim Video Generator")
127
- st.markdown("Create mathematical animations using AI!")
128
-
129
- if not GEMINI_API_KEY:
130
- st.error("⚠️ GEMINI_API_KEY not set! Add it in Settings β†’ Secrets")
131
- st.markdown("Get your free API key: https://aistudio.google.com/app/apikey")
132
- st.stop()
133
-
134
- # Input
135
- prompt = st.text_area(
136
- "What animation do you want?",
137
- placeholder="e.g., Show a blue circle morphing into a red square",
138
- height=100
139
- )
140
-
141
- # Examples
142
- st.markdown("**Examples:**")
143
- col1, col2, col3 = st.columns(3)
144
- with col1:
145
- if st.button("πŸ”΅ Circle β†’ Square"):
146
- prompt = "Show a blue circle morphing into a red square"
147
- with col2:
148
- if st.button("πŸ“ Pythagorean"):
149
- prompt = "Animate the Pythagorean theorem: aΒ² + bΒ² = cΒ²"
150
- with col3:
151
- if st.button("〰️ Sine Wave"):
152
- prompt = "Show a sine wave moving"
153
-
154
- # Generate button
155
- if st.button("🎬 Generate Video", type="primary", use_container_width=True):
156
- if not prompt:
157
- st.warning("Please enter a prompt!")
158
- else:
159
- video_path, code = render_video(prompt)
160
-
161
- if video_path:
162
- st.success("βœ… Video rendered successfully!")
163
 
164
- # Display video
165
- st.video(str(video_path))
 
 
 
 
166
 
167
- # Download button
168
- with open(video_path, 'rb') as f:
169
- st.download_button(
170
- label="πŸ“₯ Download Video",
171
- data=f.read(),
172
- file_name=f"manim_{video_path.stem}.mp4",
173
- mime="video/mp4"
174
- )
175
 
176
- # Show code
177
- with st.expander("πŸ“ View Generated Code"):
178
- st.code(code, language="python")
179
- elif code:
180
- st.warning("Video rendering failed, but here's the generated code:")
181
- st.code(code, language="python")
182
-
183
- st.markdown("---")
184
- st.markdown("πŸ’‘ **Tips:** Start with simple prompts β€’ Rendering takes 1-2 minutes β€’ Be specific about what you want")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
  import subprocess
3
+ import os
4
  import re
5
  from pathlib import Path
6
+ import tempfile
7
+
8
+ try:
9
+ from google import genai
10
+ from google.genai import types
11
+ GENAI_AVAILABLE = True
12
+ except ImportError:
13
+ GENAI_AVAILABLE = False
14
 
15
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
16
 
17
+ # Video templates for different content types
18
+ TEMPLATES = {
19
+ "Problem Solver": {
20
+ "structure": "Title β†’ Problem Statement β†’ Step-by-Step Solution β†’ Final Answer",
21
+ "style": "Clean, focused, educational",
22
+ "duration": "Focus on clear problem-solving steps"
23
+ },
24
+ "Concept Explainer": {
25
+ "structure": "Title β†’ What is it? β†’ Visual Example β†’ Key Points β†’ Summary",
26
+ "style": "Visual, intuitive, smooth transitions",
27
+ "duration": "Build understanding gradually"
28
+ },
29
+ "Formula Breakdown": {
30
+ "structure": "Title β†’ Formula Display β†’ What Each Part Means β†’ Example β†’ Application",
31
+ "style": "Clear labeling, color-coded parts",
32
+ "duration": "Thorough explanation of components"
33
+ },
34
+ "Quick Trick": {
35
+ "structure": "Title β†’ Normal Method β†’ Shortcut Method β†’ Why It Works β†’ Practice",
36
+ "style": "Fast-paced, attention-grabbing",
37
+ "duration": "Efficient and memorable"
38
+ }
39
+ }
40
 
41
+ def fix_all_known_errors(code):
42
+ """Fix ALL known Manim errors"""
43
+
44
+ # Fix colors to base 10 only
45
  color_map = {
46
+ 'ORANGE_A': 'ORANGE', 'ORANGE_B': 'ORANGE', 'ORANGE_C': 'ORANGE',
47
+ 'GRAY_A': 'WHITE', 'GRAY_B': 'WHITE', 'GREY': 'WHITE', 'GRAY': 'WHITE',
48
+ 'PURPLE_A': 'PURPLE', 'RED_A': 'RED', 'BLUE_A': 'BLUE', 'GREEN_A': 'GREEN',
49
+ 'BROWN': 'ORANGE', 'GOLD': 'YELLOW', 'CYAN': 'TEAL', 'MAGENTA': 'PINK',
50
  }
51
  for old, new in color_map.items():
52
  code = re.sub(rf'\b{old}\b', new, code)
53
 
54
+ # Fix invalid objects
55
+ code = re.sub(r'Checkmark\([^)]*\)', 'Circle(radius=0.3, color=GREEN, fill_opacity=1)', code)
56
+ code = re.sub(r'SVGMobject\([^)]+\)', 'Circle(radius=0.8, color=PINK, fill_opacity=0.8)', code)
57
+ code = re.sub(r'ImageMobject\([^)]+\)', 'Square(side_length=2, color=BLUE, fill_opacity=0.5)', code)
58
+
59
+ # Remove problematic methods
60
+ code = re.sub(r'\.add_prefix\([^)]+\)', '', code)
61
+ code = re.sub(r'\.add_suffix\([^)]+\)', '', code)
62
  code = re.sub(r'\.get_x_axis_label\([^)]+\)', '', code)
63
  code = re.sub(r'\.get_y_axis_label\([^)]+\)', '', code)
 
 
64
 
65
+ # Remove Table usage - too error-prone
66
+ if 'Table(' in code:
67
+ code = re.sub(r'(\s+)(.*Table\(.*)', r'\1# \2 # Removed', code)
68
+ code = re.sub(r'(\s+)(.*\.get_entries\(.*)', r'\1# \2', code)
69
+
70
+ # STRICT limits
71
+ code = re.sub(r'font_size=(\d+)', lambda m: f'font_size={min(int(m.group(1)), 32)}', code)
72
+ code = re.sub(r'\.shift\(UP\s*\*\s*\d+\.?\d*\)', '.shift(UP*1)', code)
73
+ code = re.sub(r'\.shift\(DOWN\s*\*\s*\d+\.?\d*\)', '.shift(DOWN*1)', code)
74
+ code = re.sub(r'\.shift\(LEFT\s*\*\s*\d+\.?\d*\)', '.shift(LEFT*2)', code)
75
+ code = re.sub(r'\.shift\(RIGHT\s*\*\s*\d+\.?\d*\)', '.shift(RIGHT*2)', code)
76
+ code = re.sub(r'\.to_edge\((UP|DOWN|LEFT|RIGHT)\)(?!\s*,)', r'.to_edge(\1, buff=0.8)', code)
77
+
78
+ # Add color=BLACK to Text/MathTex without color
79
+ def add_black(match):
80
  text = match.group(0)
81
  if 'color=' not in text:
82
  text = text[:-1] + ', color=BLACK)'
83
  return text
84
+
85
+ code = re.sub(r'Text\([^)]+\)', add_black, code)
86
+ code = re.sub(r'MathTex\([^)]+\)', add_black, code)
87
 
88
  return code
89
 
90
+ def generate_code_with_gemini(prompt, template, duration, quality):
91
+ """Generate Manim code using Gemini API"""
92
+
93
+ if not GENAI_AVAILABLE or not GEMINI_API_KEY:
94
+ return None, "❌ Setup GEMINI_API_KEY in Settings β†’ Secrets"
95
 
96
  try:
97
+ client = genai.Client(api_key=GEMINI_API_KEY)
98
+
99
+ # Duration guidance
100
+ duration_guide = {
101
+ "Short (30-60s)": "Keep it concise - 3-4 key points max, quick transitions",
102
+ "Medium (2-5 min)": "Detailed explanation - 5-8 steps, smooth pacing",
103
+ "Long (5-10 min)": "Comprehensive - 10+ steps, thorough breakdown, multiple examples"
104
+ }
105
+
106
+ template_info = TEMPLATES[template]
107
+ duration_info = duration_guide[duration]
108
+
109
+ full_prompt = f"""You are creating a YOUTUBE MATH VIDEO for government exam preparation (SSC, Banking, Railways).
110
+
111
+ TEMPLATE: {template}
112
+ STRUCTURE: {template_info['structure']}
113
+ STYLE: {template_info['style']} (inspired by 3Blue1Brown)
114
+ TARGET DURATION: {duration} - {duration_info}
115
+
116
+ USER REQUEST: {prompt}
117
+
118
+ CRITICAL RULES:
119
+
120
+ 1. YOUTUBE QUALITY:
121
+ - Professional, clean, smooth animations
122
+ - Clear visual hierarchy (title β†’ content β†’ conclusion)
123
+ - Engaging pacing with proper timing
124
+ - Everything centered and visible
125
+
126
+ 2. STRUCTURE (Follow {template} template):
127
+ - Start with BOLD title card (2 seconds)
128
+ - Present problem/concept clearly
129
+ - Step-by-step visual explanation
130
+ - Highlight key insights/shortcuts
131
+ - End with summary or practice prompt
132
+
133
+ 3. COLORS & VISIBILITY:
134
+ - Background: WHITE
135
+ - ALL Text/MathTex: color=BLACK (MANDATORY!)
136
+ - Use vibrant accent colors: BLUE (trust), GREEN (correct), RED (important), ORANGE (highlight)
137
+ - Color-code different elements (formulas=BLUE, steps=GREEN, answers=RED)
138
+
139
+ 4. PREVENT OVERLAP (CRITICAL):
140
+ - ALWAYS FadeOut before new content
141
+ - Pattern:
142
+ ```python
143
+ step1 = Text("Step 1: ...", font_size=28, color=BLACK).to_edge(UP, buff=0.8)
144
+ self.play(Write(step1), run_time=1)
145
+ self.wait(2) # Let viewers read
146
+ self.play(FadeOut(step1))
147
+ self.wait(0.3) # Small pause
148
+
149
+ step2 = Text("Step 2: ...", font_size=28, color=BLACK).to_edge(UP, buff=0.8)
150
+ self.play(Write(step2), run_time=1)
151
+ self.wait(2)
152
+ self.play(FadeOut(step2))
153
+ ```
154
+
155
+ 5. TIMING (YouTube pacing):
156
+ - Title card: 2 seconds
157
+ - Each explanation step: 2-3 seconds
158
+ - Complex calculations: 3-4 seconds
159
+ - Final answer highlight: 2 seconds
160
+ - Total should match {duration}
161
+
162
+ 6. VISUAL ELEMENTS:
163
+ - Use boxes/rectangles to group related info
164
+ - Arrows to show relationships
165
+ - Underlines for emphasis
166
+ - Fade transitions between sections
167
+ - Scale/rotate for dynamic interest
168
+
169
+ 7. STAY IN FRAME:
170
+ - Font: MAX 32 (title), 28 (content), 24 (details)
171
+ - Safe zone: X[-2.5, 2.5], Y[-1.5, 1.5]
172
+ - Use .to_edge() with buff=0.8
173
+ - Center main content with .move_to(ORIGIN)
174
+
175
+ 8. NEVER use:
176
+ - Table, DecimalTable (use VGroup instead)
177
+ - get_x_axis_label/get_y_axis_label
178
+ - Checkmark, SVGMobject, ImageMobject
179
+ - Complex grids
180
+
181
+ 9. QUALITY SETTINGS:
182
+ - Use run_time for smooth animations (1-2 seconds)
183
+ - Add fill_opacity=0.8 for shapes
184
+ - Use stroke_width=2 for emphasis
185
+
186
+ 10. CODE STRUCTURE:
187
+ ```python
188
+ from manim import *
189
+
190
+ class MathExplainer(Scene):
191
+ def construct(self):
192
+ self.camera.background_color = WHITE
193
+
194
+ # Title Card (2 sec)
195
+ title = Text("Topic Title", font_size=32, color=BLACK, weight=BOLD)
196
+ self.play(Write(title), run_time=1)
197
+ self.wait(2)
198
+ self.play(FadeOut(title))
199
+
200
+ # Main Content (follow template structure)
201
+ # ... your explanation with proper FadeOuts
202
+
203
+ # Conclusion
204
+ conclusion = Text("Key Takeaway", font_size=28, color=GREEN, weight=BOLD)
205
+ self.play(Write(conclusion), run_time=1)
206
+ self.wait(2)
207
+ self.play(FadeOut(conclusion))
208
+ ```
209
+
210
+ Generate professional, YouTube-ready code following the {template} template for: {prompt}"""
211
+
212
+ response = client.models.generate_content(
213
+ model='gemini-1.5-flash',
214
+ contents=full_prompt
215
+ )
216
+ code = response.text
217
+
218
+ return code, None
219
+
220
  except Exception as e:
221
+ return None, f"❌ Error: {str(e)}"
222
 
223
+ def render_video_from_code(code, quality):
224
+ """Render video from code"""
225
+ if not code or not code.strip():
226
+ return "❌ No code!", None
227
 
228
+ try:
229
+ temp_dir = Path(tempfile.mkdtemp(prefix="manim_"))
230
+ code = fix_all_known_errors(code)
231
+
232
+ match = re.search(r'class\s+(\w+)\s*\(Scene\)', code)
233
+ if not match:
234
+ return "❌ No Scene class!", None
235
+
236
+ class_name = match.group(1)
237
+ code_file = temp_dir / "animation.py"
238
+ code_file.write_text(code)
239
+
240
+ quality_map = {'Low (fast)': '-ql', 'Medium': '-qm', 'High (1080p)': '-qh'}
241
+ quality_flag = quality_map.get(quality, '-qm')
242
+
243
+ cmd = f"manim {code_file} {class_name} {quality_flag} --disable_caching --media_dir {temp_dir}/media"
244
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=300)
245
+
246
+ if result.returncode == 0:
247
+ videos = list((temp_dir / "media").rglob("*.mp4"))
248
+ if videos:
249
+ return "βœ… Rendered!", str(videos[0])
250
+ return f"❌ Failed:\n{result.stderr[-500:]}", None
251
+ except Exception as e:
252
+ return f"❌ {e}", None
253
+
254
+ def render_video(prompt, template, duration, quality):
255
+ """Generate and render video"""
256
+ if not prompt:
257
+ yield "❌ Enter a prompt!", None, None
258
+ return
259
+
260
+ try:
261
+ temp_dir = Path(tempfile.mkdtemp(prefix="manim_"))
262
+
263
+ yield f"πŸ€– Generating {template} video ({duration})...", None, None
264
+
265
+ code, error = generate_code_with_gemini(prompt, template, duration, quality)
266
 
267
  if error:
268
+ yield error, None, None
269
+ return
270
 
 
271
  if "```python" in code:
272
  code = code.split("```python")[1].split("```")[0]
273
  elif "```" in code:
274
  code = code.split("```")[1].split("```")[0]
275
  code = code.strip()
276
 
277
+ code = fix_all_known_errors(code)
278
+
279
+ match = re.search(r'class\s+(\w+)\s*\(Scene\)', code)
280
+ if not match:
281
+ yield "❌ No Scene class!", code, None
282
+ return
283
+
284
+ class_name = match.group(1)
285
+ code_file = temp_dir / "animation.py"
286
+ code_file.write_text(code)
287
+
288
+ yield f"🎬 Rendering {class_name}...", code, None
289
+
290
+ quality_map = {'Low (fast)': '-ql', 'Medium': '-qm', 'High (1080p)': '-qh'}
291
+ quality_flag = quality_map.get(quality, '-qm')
292
+
293
+ cmd = f"manim {code_file} {class_name} {quality_flag} --disable_caching --media_dir {temp_dir}/media"
294
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=300)
 
295
 
296
  if result.returncode == 0:
 
297
  videos = list((temp_dir / "media").rglob("*.mp4"))
298
  if videos:
299
+ yield "βœ… YouTube-ready video rendered! πŸŽ‰", code, str(videos[0])
300
  else:
301
+ yield "❌ Video not found", code, None
 
 
302
  else:
303
+ yield f"❌ Failed:\n{result.stderr[-500:]}", code, None
304
+
305
+ except Exception as e:
306
+ yield f"❌ {e}", None, None
307
+
308
+ # Gradio Interface
309
+ with gr.Blocks(title="YouTube Math Explainer") as demo:
310
+ gr.Markdown("""
311
+ # 🎬 YouTube Math Video Generator (Govt Exam Prep)
312
+
313
+ **Professional Manim animations for your YouTube channel!**
314
+ Perfect for SSC, Banking, Railways exam preparation videos.
315
+ """)
316
+
317
+ with gr.Row():
318
+ with gr.Column(scale=1):
319
+ prompt_input = gr.Textbox(
320
+ label="πŸ“ Video Topic",
321
+ placeholder="e.g., Solve percentage problem: If 20% of 500 is increased by 40%, find the result",
322
+ lines=4
323
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
+ template_input = gr.Dropdown(
326
+ choices=list(TEMPLATES.keys()),
327
+ value="Problem Solver",
328
+ label="🎨 Video Template",
329
+ info="Choose content structure"
330
+ )
331
 
332
+ duration_input = gr.Radio(
333
+ choices=["Short (30-60s)", "Medium (2-5 min)", "Long (5-10 min)"],
334
+ value="Short (30-60s)",
335
+ label="⏱️ Target Duration"
336
+ )
 
 
 
337
 
338
+ quality_input = gr.Radio(
339
+ choices=["Low (fast)", "Medium", "High (1080p)"],
340
+ value="Medium",
341
+ label="πŸŽ₯ Quality"
342
+ )
343
+
344
+ with gr.Row():
345
+ generate_btn = gr.Button("πŸš€ Generate Video", variant="primary", size="lg")
346
+ render_btn = gr.Button("πŸ”„ Render Edited", variant="secondary")
347
+
348
+ with gr.Column(scale=1):
349
+ status_output = gr.Textbox(label="πŸ“Š Status", lines=3)
350
+ video_output = gr.Video(label="πŸŽ₯ Your Video")
351
+
352
+ code_output = gr.Code(
353
+ label="πŸ’» Generated Code (Editable)",
354
+ language="python",
355
+ lines=15,
356
+ interactive=True
357
+ )
358
+
359
+ gr.Markdown("""
360
+ ### πŸ“š Templates Explained:
361
+
362
+ - **Problem Solver**: Step-by-step solution for math problems
363
+ - **Concept Explainer**: Visual explanation of mathematical concepts
364
+ - **Formula Breakdown**: Detailed formula explanation with examples
365
+ - **Quick Trick**: Shortcuts and time-saving techniques
366
+
367
+ ### 🎯 Tips for YouTube Success:
368
+ - Use **Problem Solver** for numerical questions
369
+ - Use **Quick Trick** for shortcuts (great for Shorts!)
370
+ - Use **Concept Explainer** for theory topics
371
+ - Start with **Short videos** to test, then go longer
372
+ - **High (1080p)** quality for main channel, **Low** for testing
373
+
374
+ ### ✨ Features:
375
+ βœ… 3Blue1Brown inspired style
376
+ βœ… Professional animations
377
+ βœ… No text overlap
378
+ βœ… Everything stays in frame
379
+ βœ… Exam-focused content
380
+ βœ… Edit & re-render support
381
+ """)
382
+
383
+ generate_btn.click(
384
+ fn=render_video,
385
+ inputs=[prompt_input, template_input, duration_input, quality_input],
386
+ outputs=[status_output, code_output, video_output]
387
+ )
388
+
389
+ render_btn.click(
390
+ fn=render_video_from_code,
391
+ inputs=[code_output, quality_input],
392
+ outputs=[status_output, video_output]
393
+ )
394
+
395
+ gr.Examples(
396
+ examples=[
397
+ ["Solve: If 25% of a number is 75, find the number", "Problem Solver", "Short (30-60s)", "Medium"],
398
+ ["Explain Pythagoras theorem with visual proof", "Concept Explainer", "Medium (2-5 min)", "Medium"],
399
+ ["Quick trick: Calculate 15% of any number mentally", "Quick Trick", "Short (30-60s)", "Medium"],
400
+ ["Break down the quadratic formula: x = (-b ± √(b²-4ac))/2a", "Formula Breakdown", "Medium (2-5 min)", "Medium"],
401
+ ],
402
+ inputs=[prompt_input, template_input, duration_input, quality_input]
403
+ )
404
+
405
+ if __name__ == "__main__":
406
+ demo.launch()