raksa-the-wildcats commited on
Commit
92d1d5d
Β·
1 Parent(s): 0a73588

Fifth commit

Browse files
Files changed (2) hide show
  1. app.py +280 -288
  2. requirements.txt +1 -2
app.py CHANGED
@@ -1,355 +1,347 @@
1
  import gradio as gr
2
- import tempfile
3
- import os
4
  import subprocess
5
- import json
6
- import traceback
7
  import shutil
8
  from pathlib import Path
9
  import time
10
- import threading
11
- import queue
12
- import matplotlib.pyplot as plt
13
- import matplotlib.animation as animation
14
- import numpy as np
15
- from io import StringIO
16
- import sys
17
 
18
- class LightweightAnimationServer:
19
  def __init__(self):
 
 
 
 
 
20
  self.temp_dir = tempfile.mkdtemp()
21
- self.max_execution_time = 30 # 30 seconds timeout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- def validate_animation_code(self, code):
24
- """Basic validation of animation code"""
25
- dangerous_imports = [
26
- 'subprocess', 'os.system', 'eval', 'exec', 'open',
27
- 'compile', 'globals', 'locals', 'input', 'raw_input',
28
- 'file', '__import__'
29
- ]
30
 
31
- for dangerous in dangerous_imports:
32
- if dangerous in code:
33
- return False, f"Dangerous operation detected: {dangerous}"
34
 
35
- # More flexible validation - just check if it looks like animation code
36
- has_matplotlib = ('matplotlib' in code or 'plt' in code)
37
- has_animation_logic = ('animate' in code or 'FuncAnimation' in code or 'save(' in code)
38
 
39
- if not has_matplotlib:
40
- return False, "Code should use matplotlib for animations"
41
-
42
- if not has_animation_logic:
43
- return False, "Code should contain animation logic (animate function, FuncAnimation, or save call)"
44
-
45
- return True, "Valid"
46
 
47
- def execute_animation_code(self, code):
48
- """Execute animation code safely"""
49
  try:
50
  # Validate code
51
- is_valid, message = self.validate_animation_code(code)
52
  if not is_valid:
53
- return None, f"Code validation failed: {message}"
 
 
 
54
 
55
- # Create output file path
56
- output_path = os.path.join(self.temp_dir, "animation.mp4")
 
 
57
 
58
- # Create a safe execution environment with pre-imported modules
59
- import matplotlib
60
- import matplotlib.pyplot as plt
61
- import matplotlib.animation as animation
62
- import numpy as np
63
 
64
- # Create globals dictionary with all necessary modules
65
- execution_globals = {
66
- # Standard Python builtins
67
- '__builtins__': {
68
- 'range': range, 'len': len, 'enumerate': enumerate, 'zip': zip,
69
- 'min': min, 'max': max, 'abs': abs, 'round': round, 'sum': sum,
70
- 'print': print, 'int': int, 'float': float, 'str': str,
71
- 'list': list, 'dict': dict, 'tuple': tuple, 'set': set,
72
- 'bool': bool, 'type': type,
73
- },
74
- # Pre-imported modules
75
- 'matplotlib': matplotlib,
76
- 'plt': plt,
77
- 'animation': animation,
78
- 'np': np,
79
- 'numpy': np,
80
- 'output_path': output_path,
81
  }
 
82
 
83
- # Execute the code
84
- exec(code, execution_globals)
85
 
86
- # Check if animation was created
87
- if os.path.exists(output_path):
88
- return output_path, "Animation created successfully!"
89
- else:
90
- return None, "No animation file was generated. Make sure your code saves to 'output_path'."
91
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  except Exception as e:
93
- return None, f"Execution error: {str(e)}\n{traceback.format_exc()}"
 
94
 
95
- def cleanup(self):
96
- """Clean up temporary files"""
97
- try:
98
- shutil.rmtree(self.temp_dir)
99
- except:
100
- pass
101
-
102
- # Initialize the server
103
- animation_server = LightweightAnimationServer()
104
-
105
- # Example animation scripts
106
- EXAMPLE_SCRIPTS = {
107
- "Sine Wave": '''import matplotlib.pyplot as plt
108
- import matplotlib.animation as animation
109
- import numpy as np
110
-
111
- # Create figure and axis
112
- fig, ax = plt.subplots(figsize=(10, 6))
113
- ax.set_xlim(0, 4*np.pi)
114
- ax.set_ylim(-2, 2)
115
- ax.set_xlabel('x')
116
- ax.set_ylabel('sin(x)')
117
- ax.set_title('Animated Sine Wave')
118
-
119
- x = np.linspace(0, 4*np.pi, 1000)
120
- line, = ax.plot([], [], 'b-', linewidth=2)
121
-
122
- def animate(frame):
123
- y = np.sin(x + frame/10)
124
- line.set_data(x, y)
125
- return line,
126
-
127
- # Create animation
128
- anim = animation.FuncAnimation(fig, animate, frames=200, interval=50, blit=True)
129
- anim.save(output_path, writer='ffmpeg', fps=20)
130
- plt.close()''',
131
-
132
- "Growing Circle": '''import matplotlib.pyplot as plt
133
- import matplotlib.animation as animation
134
- import numpy as np
135
-
136
- fig, ax = plt.subplots(figsize=(8, 8))
137
- ax.set_xlim(-2, 2)
138
- ax.set_ylim(-2, 2)
139
- ax.set_aspect('equal')
140
- ax.set_title('Growing Circle')
141
-
142
- circle = plt.Circle((0, 0), 0, fill=False, color='red', linewidth=3)
143
- ax.add_patch(circle)
144
-
145
- def animate(frame):
146
- radius = (frame / 100) * 1.5
147
- circle.set_radius(radius)
148
- return circle,
149
-
150
- anim = animation.FuncAnimation(fig, animate, frames=150, interval=100, blit=True)
151
- anim.save(output_path, writer='ffmpeg', fps=10)
152
- plt.close()''',
153
-
154
- "Rotating Plot": '''import matplotlib.pyplot as plt
155
- import matplotlib.animation as animation
156
- import numpy as np
157
-
158
- fig = plt.figure(figsize=(10, 8))
159
- ax = fig.add_subplot(111, projection='3d')
160
-
161
- # Generate data
162
- t = np.linspace(0, 2*np.pi, 100)
163
- x = np.cos(t)
164
- y = np.sin(t)
165
- z = t
166
-
167
- line, = ax.plot(x, y, z, 'b-', linewidth=2)
168
- ax.set_xlabel('X')
169
- ax.set_ylabel('Y')
170
- ax.set_zlabel('Z')
171
- ax.set_title('Rotating 3D Helix')
172
-
173
- def animate(frame):
174
- ax.view_init(elev=30, azim=frame*2)
175
- return line,
176
-
177
- anim = animation.FuncAnimation(fig, animate, frames=180, interval=100, blit=False)
178
- anim.save(output_path, writer='ffmpeg', fps=10)
179
- plt.close()''',
180
-
181
- "Bar Chart Race": '''import matplotlib.pyplot as plt
182
- import matplotlib.animation as animation
183
- import numpy as np
184
 
185
- fig, ax = plt.subplots(figsize=(12, 8))
 
186
 
187
- # Sample data
188
- categories = ['A', 'B', 'C', 'D', 'E']
189
- colors = ['red', 'blue', 'green', 'orange', 'purple']
190
 
191
- def animate(frame):
192
- ax.clear()
193
-
194
- # Generate some dynamic data
195
- values = np.random.rand(5) * (frame + 1) * 10
 
 
 
 
196
 
197
- bars = ax.bar(categories, values, color=colors, alpha=0.7)
198
- ax.set_ylim(0, 100)
199
- ax.set_title(f'Animated Bar Chart - Frame {frame}')
200
- ax.set_ylabel('Values')
 
 
 
 
 
 
 
 
 
201
 
202
- # Add value labels on bars
203
- for bar, value in zip(bars, values):
204
- height = bar.get_height()
205
- ax.text(bar.get_x() + bar.get_width()/2., height + 1,
206
- f'{value:.1f}', ha='center', va='bottom')
 
 
 
 
 
 
207
 
208
- return bars
209
 
210
- anim = animation.FuncAnimation(fig, animate, frames=50, interval=200, blit=False)
211
- anim.save(output_path, writer='ffmpeg', fps=5)
212
- plt.close()'''
 
 
 
 
 
 
 
 
 
 
213
  }
214
 
215
- def create_animation(code, animation_type):
216
- """Gradio interface function for creating animations"""
 
 
217
  if not code.strip():
218
- return None, "Please provide animation code"
219
 
220
- try:
221
- video_path, message = animation_server.execute_animation_code(code)
222
-
223
- if video_path:
224
- return video_path, f"βœ… {message}"
225
- else:
226
- return None, f"❌ {message}"
227
-
228
- except Exception as e:
229
- return None, f"❌ Error: {str(e)}"
230
 
231
  def load_example(example_name):
232
- """Load example script"""
233
- return EXAMPLE_SCRIPTS.get(example_name, "")
234
-
235
- def clear_all():
236
- """Clear all fields"""
237
- return "", None, ""
238
-
239
- # Try to install manim if not available, fallback to matplotlib-only mode
240
- try:
241
- import manim
242
- MANIM_AVAILABLE = True
243
- except ImportError:
244
- MANIM_AVAILABLE = False
245
 
246
  # Create Gradio interface
247
- with gr.Blocks(title="Animation MCP Server", theme=gr.themes.Soft()) as demo:
248
- if not MANIM_AVAILABLE:
249
- gr.Markdown("""
250
- # 🎬 Animation MCP Server (Lightweight Mode)
251
-
252
- **Note**: Full Manim is not available in this environment. This is a lightweight version using Matplotlib animations.
253
-
254
- Create animated plots, charts, and visualizations using matplotlib's animation capabilities!
255
- """)
256
- else:
257
- gr.Markdown("""
258
- # 🎬 Animation MCP Server
259
-
260
- Create animations using matplotlib or Manim Community Edition!
261
- """)
262
-
263
  gr.Markdown("""
264
- ## How to use:
265
- 1. Write or select an animation script
266
- 2. Make sure your code saves the animation to `output_path`
267
- 3. Click "Create Animation"
268
 
269
- ### Key Requirements:
270
- - Import matplotlib: `import matplotlib.pyplot as plt`
271
- - Create animation: Use `matplotlib.animation.FuncAnimation`
272
- - Save to output: `anim.save(output_path, writer='ffmpeg', fps=10)`
 
 
 
 
 
 
 
 
 
273
  """)
274
 
275
  with gr.Row():
276
  with gr.Column(scale=2):
277
- gr.Markdown("### πŸ“ Animation Code")
278
-
279
- with gr.Row():
280
- example_dropdown = gr.Dropdown(
281
- choices=list(EXAMPLE_SCRIPTS.keys()),
282
- label="Load Example",
283
- value=None
284
- )
285
- clear_btn = gr.Button("Clear All", size="sm")
286
-
287
  code_input = gr.Code(
288
- label="Animation Script",
289
  language="python",
290
- lines=25,
291
- value=EXAMPLE_SCRIPTS["Sine Wave"]
 
292
  )
293
 
294
- animation_type = gr.Dropdown(
295
- choices=["matplotlib", "manim"] if MANIM_AVAILABLE else ["matplotlib"],
296
- label="Animation Type",
297
- value="matplotlib"
298
- )
299
-
300
- create_btn = gr.Button("🎬 Create Animation", variant="primary", size="lg")
 
 
 
 
301
 
302
- with gr.Column(scale=1):
303
- gr.Markdown("### πŸŽ₯ Output")
304
 
305
- status_output = gr.Textbox(
306
- label="Status",
307
- lines=4,
308
- interactive=False
 
309
  )
 
 
 
 
 
 
310
 
311
- video_output = gr.Video(
312
- label="Generated Animation",
313
- height=400
314
- )
315
 
316
- gr.Markdown("""
317
- ### πŸ“‹ Tips for Matplotlib Animations:
318
- - Use `fig, ax = plt.subplots()` to create your plot
319
- - Define an `animate(frame)` function that updates your plot
320
- - Use `FuncAnimation(fig, animate, frames=N, interval=ms)`
321
- - Always save with `anim.save(output_path, writer='ffmpeg')`
322
- - Call `plt.close()` at the end to free memory
323
 
324
- ### ⚠️ Limitations:
325
- - 30-second execution timeout
326
- - No file I/O operations
327
- - Limited to safe Python operations
328
- - Matplotlib animations only (unless full Manim is available)
329
- """)
330
 
331
- # Event handlers
332
  example_dropdown.change(
333
  fn=load_example,
334
  inputs=[example_dropdown],
335
  outputs=[code_input]
336
  )
337
 
338
- clear_btn.click(
339
- fn=clear_all,
340
- outputs=[code_input, video_output, status_output]
341
  )
342
 
343
- create_btn.click(
344
- fn=create_animation,
345
- inputs=[code_input, animation_type],
346
- outputs=[video_output, status_output]
347
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
 
349
- # Launch the app
350
  if __name__ == "__main__":
351
- demo.launch(
352
- server_name="0.0.0.0",
353
- server_port=7860,
354
- share=True
355
- )
 
1
  import gradio as gr
 
 
2
  import subprocess
3
+ import os
4
+ import tempfile
5
  import shutil
6
  from pathlib import Path
7
  import time
 
 
 
 
 
 
 
8
 
9
+ class ManimAnimationGenerator:
10
  def __init__(self):
11
+ self.temp_dir = None
12
+ self.output_dir = None
13
+
14
+ def setup_directories(self):
15
+ """Setup temporary directories for Manim execution"""
16
  self.temp_dir = tempfile.mkdtemp()
17
+ self.output_dir = os.path.join(self.temp_dir, "media", "videos", "480p15")
18
+ os.makedirs(self.output_dir, exist_ok=True)
19
+ return self.temp_dir
20
+
21
+ def cleanup_directories(self):
22
+ """Clean up temporary directories"""
23
+ if self.temp_dir and os.path.exists(self.temp_dir):
24
+ shutil.rmtree(self.temp_dir)
25
+ self.temp_dir = None
26
+ self.output_dir = None
27
+
28
+ def validate_manim_code(self, code):
29
+ """Basic validation of Manim code"""
30
+ required_imports = ["from manim import *", "import manim"]
31
+ has_import = any(imp in code for imp in required_imports)
32
 
33
+ if not has_import:
34
+ return False, "Code must include 'from manim import *' or 'import manim'"
 
 
 
 
 
35
 
36
+ if "class" not in code:
37
+ return False, "Code must contain at least one class definition"
 
38
 
39
+ if "Scene" not in code:
40
+ return False, "Class must inherit from Scene or a Scene subclass"
 
41
 
42
+ return True, "Code validation passed"
 
 
 
 
 
 
43
 
44
+ def execute_manim_code(self, code, quality="low", format_type="gif"):
45
+ """Execute Manim code and return the generated animation"""
46
  try:
47
  # Validate code
48
+ is_valid, message = self.validate_manim_code(code)
49
  if not is_valid:
50
+ return None, f"❌ Validation Error: {message}", ""
51
+
52
+ # Setup directories
53
+ temp_dir = self.setup_directories()
54
 
55
+ # Create Python file
56
+ python_file = os.path.join(temp_dir, "animation.py")
57
+ with open(python_file, "w") as f:
58
+ f.write(code)
59
 
60
+ # Extract class name for Manim command
61
+ class_name = self.extract_class_name(code)
62
+ if not class_name:
63
+ self.cleanup_directories()
64
+ return None, "❌ Error: Could not find a valid Scene class in the code", ""
65
 
66
+ # Quality settings
67
+ quality_map = {
68
+ "low": "-ql",
69
+ "medium": "-qm",
70
+ "high": "-qh"
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
+ quality_flag = quality_map.get(quality, "-ql")
73
 
74
+ # Format settings
75
+ format_flag = "--format=gif" if format_type == "gif" else ""
76
 
77
+ # Build Manim command
78
+ cmd = [
79
+ "manim",
80
+ quality_flag,
81
+ python_file,
82
+ class_name
83
+ ]
84
+
85
+ if format_flag:
86
+ cmd.append(format_flag)
87
+
88
+ # Execute Manim
89
+ result = subprocess.run(
90
+ cmd,
91
+ cwd=temp_dir,
92
+ capture_output=True,
93
+ text=True,
94
+ timeout=120 # 2 minute timeout
95
+ )
96
+
97
+ if result.returncode != 0:
98
+ error_msg = f"❌ Manim execution failed:\n{result.stderr}"
99
+ self.cleanup_directories()
100
+ return None, error_msg, result.stdout
101
+
102
+ # Find generated file
103
+ output_file = self.find_output_file(temp_dir, class_name, format_type)
104
+ if not output_file:
105
+ self.cleanup_directories()
106
+ return None, "❌ Error: Could not find generated animation file", result.stdout
107
+
108
+ # Copy to a permanent location for Gradio
109
+ permanent_file = f"/tmp/{class_name}_{int(time.time())}.{format_type}"
110
+ shutil.copy2(output_file, permanent_file)
111
+
112
+ success_msg = f"βœ… Animation generated successfully!\nClass: {class_name}\nQuality: {quality}\nFormat: {format_type}"
113
+
114
+ self.cleanup_directories()
115
+ return permanent_file, success_msg, result.stdout
116
+
117
+ except subprocess.TimeoutExpired:
118
+ self.cleanup_directories()
119
+ return None, "❌ Error: Animation generation timed out (2 minutes)", ""
120
  except Exception as e:
121
+ self.cleanup_directories()
122
+ return None, f"❌ Error: {str(e)}", ""
123
 
124
+ def extract_class_name(self, code):
125
+ """Extract the first Scene class name from the code"""
126
+ lines = code.split('\n')
127
+ for line in lines:
128
+ if line.strip().startswith('class ') and 'Scene' in line:
129
+ # Extract class name
130
+ class_def = line.strip().split('class ')[1].split('(')[0].strip()
131
+ return class_def
132
+ return None
133
+
134
+ def find_output_file(self, temp_dir, class_name, format_type):
135
+ """Find the generated output file"""
136
+ # Common Manim output paths
137
+ possible_paths = [
138
+ os.path.join(temp_dir, "media", "videos", "480p15", f"{class_name}.{format_type}"),
139
+ os.path.join(temp_dir, "media", "videos", "720p30", f"{class_name}.{format_type}"),
140
+ os.path.join(temp_dir, "media", "videos", "1080p60", f"{class_name}.{format_type}"),
141
+ os.path.join(temp_dir, "media", "images", f"{class_name}.png"),
142
+ ]
143
+
144
+ # Also search recursively
145
+ for root, dirs, files in os.walk(temp_dir):
146
+ for file in files:
147
+ if file.startswith(class_name) and file.endswith(f".{format_type}"):
148
+ return os.path.join(root, file)
149
+
150
+ # Check specific paths
151
+ for path in possible_paths:
152
+ if os.path.exists(path):
153
+ return path
154
+
155
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ # Initialize the generator
158
+ generator = ManimAnimationGenerator()
159
 
160
+ # Example Manim codes for users
161
+ example_codes = {
162
+ "Simple Square": '''from manim import *
163
 
164
+ class CreateSquare(Scene):
165
+ def construct(self):
166
+ square = Square()
167
+ square.set_fill(BLUE, opacity=0.5)
168
+ square.set_stroke(WHITE, width=2)
169
+
170
+ self.play(Create(square))
171
+ self.play(square.animate.rotate(PI/4))
172
+ self.wait()''',
173
 
174
+ "Moving Circle": '''from manim import *
175
+
176
+ class MovingCircle(Scene):
177
+ def construct(self):
178
+ circle = Circle()
179
+ circle.set_fill(RED, opacity=0.5)
180
+
181
+ self.play(Create(circle))
182
+ self.play(circle.animate.shift(RIGHT * 2))
183
+ self.play(circle.animate.shift(UP * 2))
184
+ self.play(circle.animate.shift(LEFT * 2))
185
+ self.play(circle.animate.shift(DOWN * 2))
186
+ self.wait()''',
187
 
188
+ "Text Animation": '''from manim import *
189
+
190
+ class TextAnimation(Scene):
191
+ def construct(self):
192
+ text = Text("Hello Manim!", font_size=48)
193
+ text2 = Text("Animated Math!", font_size=48)
194
+
195
+ self.play(Write(text))
196
+ self.wait()
197
+ self.play(Transform(text, text2))
198
+ self.wait()''',
199
 
200
+ "Mathematical Formula": '''from manim import *
201
 
202
+ class MathFormula(Scene):
203
+ def construct(self):
204
+ formula = MathTex(r"\\frac{d}{dx}(x^2) = 2x")
205
+ formula.scale(2)
206
+
207
+ self.play(Write(formula))
208
+ self.wait()
209
+
210
+ formula2 = MathTex(r"\\int_0^1 x^2 dx = \\frac{1}{3}")
211
+ formula2.scale(2)
212
+
213
+ self.play(Transform(formula, formula2))
214
+ self.wait()'''
215
  }
216
 
217
+ def generate_animation(code, quality, format_type, progress=gr.Progress()):
218
+ """Main function to generate animation"""
219
+ progress(0, desc="Starting animation generation...")
220
+
221
  if not code.strip():
222
+ return None, "❌ Please enter some Manim code", ""
223
 
224
+ progress(0.3, desc="Validating code...")
225
+ result = generator.execute_manim_code(code, quality, format_type)
226
+
227
+ progress(0.7, desc="Generating animation...")
228
+
229
+ if result[0]: # Success
230
+ progress(1.0, desc="Animation generated successfully!")
231
+ return result[0], result[1], result[2]
232
+ else: # Error
233
+ return None, result[1], result[2]
234
 
235
  def load_example(example_name):
236
+ """Load example code"""
237
+ return example_codes.get(example_name, "")
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  # Create Gradio interface
240
+ with gr.Blocks(title="Manim Animation Generator", theme=gr.themes.Soft()) as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  gr.Markdown("""
242
+ # 🎬 Manim Animation Generator
 
 
 
243
 
244
+ Create beautiful mathematical animations using [Manim](https://www.manim.community/)!
245
+ Enter your Python code below and watch it come to life.
246
+
247
+ ## πŸ“ How to use:
248
+ 1. Write or select example Manim code
249
+ 2. Choose quality and format settings
250
+ 3. Click "Generate Animation"
251
+ 4. Wait for your animation to render
252
+
253
+ ## πŸ’‘ Tips:
254
+ - Your code must include `from manim import *`
255
+ - Create a class that inherits from `Scene`
256
+ - Use the `construct` method to define your animation
257
  """)
258
 
259
  with gr.Row():
260
  with gr.Column(scale=2):
 
 
 
 
 
 
 
 
 
 
261
  code_input = gr.Code(
262
+ label="Manim Code",
263
  language="python",
264
+ placeholder="Enter your Manim code here...",
265
+ lines=20,
266
+ value=example_codes["Simple Square"]
267
  )
268
 
269
+ with gr.Row():
270
+ quality = gr.Dropdown(
271
+ choices=["low", "medium", "high"],
272
+ value="low",
273
+ label="Quality"
274
+ )
275
+ format_type = gr.Dropdown(
276
+ choices=["gif", "mp4"],
277
+ value="gif",
278
+ label="Format"
279
+ )
280
 
281
+ generate_btn = gr.Button("🎬 Generate Animation", variant="primary", size="lg")
 
282
 
283
+ gr.Markdown("### πŸ“š Example Codes:")
284
+ example_dropdown = gr.Dropdown(
285
+ choices=list(example_codes.keys()),
286
+ label="Load Example",
287
+ value="Simple Square"
288
  )
289
+ load_example_btn = gr.Button("πŸ“‚ Load Example")
290
+
291
+ with gr.Column(scale=2):
292
+ output_video = gr.File(label="Generated Animation", file_types=[".gif", ".mp4"])
293
+ status_output = gr.Textbox(label="Status", lines=5, max_lines=10)
294
+ logs_output = gr.Textbox(label="Manim Logs", lines=8, max_lines=15, visible=False)
295
 
296
+ with gr.Row():
297
+ show_logs_btn = gr.Button("Show Logs", size="sm")
298
+ hide_logs_btn = gr.Button("Hide Logs", size="sm")
 
299
 
300
+ # Event handlers
301
+ generate_btn.click(
302
+ fn=generate_animation,
303
+ inputs=[code_input, quality, format_type],
304
+ outputs=[output_video, status_output, logs_output]
305
+ )
 
306
 
307
+ load_example_btn.click(
308
+ fn=load_example,
309
+ inputs=[example_dropdown],
310
+ outputs=[code_input]
311
+ )
 
312
 
 
313
  example_dropdown.change(
314
  fn=load_example,
315
  inputs=[example_dropdown],
316
  outputs=[code_input]
317
  )
318
 
319
+ show_logs_btn.click(
320
+ fn=lambda: gr.update(visible=True),
321
+ outputs=[logs_output]
322
  )
323
 
324
+ hide_logs_btn.click(
325
+ fn=lambda: gr.update(visible=False),
326
+ outputs=[logs_output]
 
327
  )
328
+
329
+ gr.Markdown("""
330
+ ## πŸ”§ Troubleshooting:
331
+ - **Timeout errors**: Try simpler animations or lower quality
332
+ - **Import errors**: Make sure to include `from manim import *`
333
+ - **Class errors**: Your class must inherit from `Scene`
334
+ - **No output**: Check that your `construct` method has animation commands
335
+
336
+ ## 🎯 Common Manim Objects:
337
+ - **Shapes**: `Circle()`, `Square()`, `Triangle()`, `Rectangle()`
338
+ - **Text**: `Text("Hello")`, `MathTex(r"x^2")`
339
+ - **Animations**: `Create()`, `Write()`, `Transform()`, `FadeIn()`, `FadeOut()`
340
+ - **Colors**: `RED`, `BLUE`, `GREEN`, `YELLOW`, `WHITE`, `BLACK`
341
+
342
+ ---
343
+ Made with ❀️ using [Manim](https://www.manim.community/) and [Gradio](https://gradio.app/)
344
+ """)
345
 
 
346
  if __name__ == "__main__":
347
+ app.launch(debug=True, share=True)
 
 
 
 
requirements.txt CHANGED
@@ -1,6 +1,5 @@
1
  gradio>=4.0.0
2
- matplotlib>=3.5.0
3
  numpy
4
- scipy
5
  pillow
6
  ffmpeg-python
 
1
  gradio>=4.0.0
2
+ manim>=0.18.0
3
  numpy
 
4
  pillow
5
  ffmpeg-python