devendergarg14 commited on
Commit
a14fce6
Β·
verified Β·
1 Parent(s): 4f56a17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -26
app.py CHANGED
@@ -84,23 +84,19 @@ def run_manim(code_str, orientation, quality, timeout):
84
  stderr_log = process.stderr.decode('utf-8', 'ignore')
85
  full_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
86
 
87
- # 1. Check if Manim explicitly failed (non-zero exit code)
88
  if process.returncode != 0:
89
  print(f"❌ Render Failed.\n{stderr_log}", flush=True)
90
  return None, f"⚠️ ERROR: Manim failed to render.\n{full_logs}", False
91
 
92
  except subprocess.TimeoutExpired as e:
93
- # (Same timeout handling as before)
94
- print(f"βŒ› Render timed out after {timeout_sec} seconds. Attempting recovery.", flush=True)
95
  stdout_log = e.stdout.decode('utf-8', 'ignore') if e.stdout else ""
96
  stderr_log = e.stderr.decode('utf-8', 'ignore') if e.stderr else ""
97
  timeout_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
98
-
99
- # ... [Recovery Logic Omitted for Brevity, but essentially same as previous version] ...
100
  return None, f"❌ ERROR: Timed out.\n{timeout_logs}", False
101
 
102
  # ---------------------------------------------------------
103
- # 2. LOCATE OUTPUT FILE (FIXED LOGIC)
104
  # ---------------------------------------------------------
105
 
106
  # Strategy A: Look for the expected Video File (.mp4)
@@ -118,45 +114,33 @@ def run_manim(code_str, orientation, quality, timeout):
118
  return found_video_path, f"βœ… Rendering Successful\n\n{full_logs}", True
119
 
120
  # Strategy B: Look for a Static Image (.png) and convert to Video
121
- # Manim saves images to media/images/scene/ if no animations are played.
122
  media_image_base = os.path.join("media", "images", "scene")
123
- expected_image_name = output_filename + ".png" # Manim often appends .png to the -o name
124
  found_image_path = None
125
 
126
  if os.path.exists(media_image_base):
127
- # Direct check
128
- direct_path = os.path.join(media_image_base, expected_image_name)
129
- if os.path.exists(direct_path):
130
- found_image_path = direct_path
131
- else:
132
- # Recursive check in case of subfolders
133
- for root, _, files in os.walk(media_image_base):
134
- if expected_image_name in files:
135
- found_image_path = os.path.join(root, expected_image_name)
136
- break
137
 
138
  if found_image_path:
139
  print(f"πŸ–ΌοΈ Static scene detected (0 animations). converting image to video: {found_image_path}", flush=True)
140
-
141
- # Convert PNG to MP4 using FFMPEG (1 second duration)
142
  converted_video_path = os.path.join(os.path.dirname(found_image_path), output_filename)
143
  ffmpeg_cmd = [
144
  "ffmpeg", "-y", "-loop", "1",
145
  "-i", found_image_path,
146
  "-t", "1",
147
  "-c:v", "libx264",
148
- "-pix_fmt", "yuv420p", # Ensure compatibility
149
  converted_video_path
150
  ]
151
-
152
  ffmpeg_proc = subprocess.run(ffmpeg_cmd, capture_output=True, check=False)
153
  if ffmpeg_proc.returncode == 0 and os.path.exists(converted_video_path):
154
- print(f"βœ… Conversion Success: {converted_video_path}", flush=True)
155
  return converted_video_path, f"βœ… Static Scene Rendered (Converted to 1s Video)\n\n{full_logs}", True
156
  else:
157
  print(f"❌ FFMPEG Conversion Failed: {ffmpeg_proc.stderr.decode()}", flush=True)
158
 
159
- # If neither video nor image found
160
  print(f"❌ Final output file '{output_filename}' not found.", flush=True)
161
  return None, f"Video file not created despite success code. Check logs:\n{full_logs}", False
162
 
@@ -190,9 +174,18 @@ def render_video_from_code(code, orientation, quality, timeout, preview_factor):
190
  # 4. Gradio Interface (API Definition)
191
  # ---------------------------------------------------------
192
 
 
 
 
 
 
 
 
 
 
193
  with gr.Blocks(title="Manim Render API") as demo:
194
  # Hidden components to define the API signature
195
- code_input = gr.Code(label="Python Code", language="python", value="from manim import *\nclass GenScene(Scene):\n def construct(self):\n self.add(Circle())")
196
  orientation_opt = gr.Radio(choices=["Landscape (16:9)", "Portrait (9:16)"], value="Portrait (9:16)", label="Orientation")
197
  quality_opt = gr.Dropdown(choices=["Preview (360p)", "480p", "720p", "1080p", "4k"], value="Preview (360p)", label="Quality")
198
  timeout_input = gr.Number(label="Render Timeout (seconds)", value=60)
@@ -202,7 +195,6 @@ with gr.Blocks(title="Manim Render API") as demo:
202
  status_output = gr.Textbox(label="Status/Logs")
203
  fix_btn_output = gr.Button("Fix Error & Re-render", variant="stop")
204
 
205
- # The trigger
206
  render_btn = gr.Button("Render")
207
 
208
  render_btn.click(
 
84
  stderr_log = process.stderr.decode('utf-8', 'ignore')
85
  full_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
86
 
 
87
  if process.returncode != 0:
88
  print(f"❌ Render Failed.\n{stderr_log}", flush=True)
89
  return None, f"⚠️ ERROR: Manim failed to render.\n{full_logs}", False
90
 
91
  except subprocess.TimeoutExpired as e:
92
+ print(f"βŒ› Render timed out after {timeout_sec} seconds.", flush=True)
 
93
  stdout_log = e.stdout.decode('utf-8', 'ignore') if e.stdout else ""
94
  stderr_log = e.stderr.decode('utf-8', 'ignore') if e.stderr else ""
95
  timeout_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
 
 
96
  return None, f"❌ ERROR: Timed out.\n{timeout_logs}", False
97
 
98
  # ---------------------------------------------------------
99
+ # 2. LOCATE OUTPUT FILE
100
  # ---------------------------------------------------------
101
 
102
  # Strategy A: Look for the expected Video File (.mp4)
 
114
  return found_video_path, f"βœ… Rendering Successful\n\n{full_logs}", True
115
 
116
  # Strategy B: Look for a Static Image (.png) and convert to Video
 
117
  media_image_base = os.path.join("media", "images", "scene")
118
+ expected_image_name = output_filename + ".png"
119
  found_image_path = None
120
 
121
  if os.path.exists(media_image_base):
122
+ for root, _, files in os.walk(media_image_base):
123
+ if expected_image_name in files:
124
+ found_image_path = os.path.join(root, expected_image_name)
125
+ break
 
 
 
 
 
 
126
 
127
  if found_image_path:
128
  print(f"πŸ–ΌοΈ Static scene detected (0 animations). converting image to video: {found_image_path}", flush=True)
 
 
129
  converted_video_path = os.path.join(os.path.dirname(found_image_path), output_filename)
130
  ffmpeg_cmd = [
131
  "ffmpeg", "-y", "-loop", "1",
132
  "-i", found_image_path,
133
  "-t", "1",
134
  "-c:v", "libx264",
135
+ "-pix_fmt", "yuv420p",
136
  converted_video_path
137
  ]
 
138
  ffmpeg_proc = subprocess.run(ffmpeg_cmd, capture_output=True, check=False)
139
  if ffmpeg_proc.returncode == 0 and os.path.exists(converted_video_path):
 
140
  return converted_video_path, f"βœ… Static Scene Rendered (Converted to 1s Video)\n\n{full_logs}", True
141
  else:
142
  print(f"❌ FFMPEG Conversion Failed: {ffmpeg_proc.stderr.decode()}", flush=True)
143
 
 
144
  print(f"❌ Final output file '{output_filename}' not found.", flush=True)
145
  return None, f"Video file not created despite success code. Check logs:\n{full_logs}", False
146
 
 
174
  # 4. Gradio Interface (API Definition)
175
  # ---------------------------------------------------------
176
 
177
+ DEFAULT_CODE = """from manim import *
178
+
179
+ class GenScene(Scene):
180
+ def construct(self):
181
+ c = Circle(color=BLUE, fill_opacity=0.5)
182
+ self.play(Create(c)) # Animation adds runtime
183
+ self.wait(1) # Pause adds runtime
184
+ """
185
+
186
  with gr.Blocks(title="Manim Render API") as demo:
187
  # Hidden components to define the API signature
188
+ code_input = gr.Code(label="Python Code", language="python", value=DEFAULT_CODE)
189
  orientation_opt = gr.Radio(choices=["Landscape (16:9)", "Portrait (9:16)"], value="Portrait (9:16)", label="Orientation")
190
  quality_opt = gr.Dropdown(choices=["Preview (360p)", "480p", "720p", "1080p", "4k"], value="Preview (360p)", label="Quality")
191
  timeout_input = gr.Number(label="Render Timeout (seconds)", value=60)
 
195
  status_output = gr.Textbox(label="Status/Logs")
196
  fix_btn_output = gr.Button("Fix Error & Re-render", variant="stop")
197
 
 
198
  render_btn = gr.Button("Render")
199
 
200
  render_btn.click(