devendergarg14 commited on
Commit
7022c93
·
verified ·
1 Parent(s): 8bd2e7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -25
app.py CHANGED
@@ -15,13 +15,20 @@ import tempfile
15
  def modify_animation_times(code: str, factor: float = 1.0, for_precheck: bool = False) -> str:
16
  """
17
  Modifies wait() and run_time in the code.
18
- 1. Pre-Check: Zero latency (wait(0), run_time=0.01).
19
- 2. Preview: Scales time by factor.
20
  """
21
  if for_precheck:
22
  print("⚡ Optimizing code for Pre-check (Zero Latency)...", flush=True)
23
- code = re.sub(r"self\.wait\s*\([^)]*\)", "self.wait(0)", code)
24
- code = re.sub(r"run_time\s*=\s*[^,)]+", "run_time=0.01", code)
 
 
 
 
 
 
 
25
  return code
26
 
27
  print(f"⚡ Scaling animation times by a factor of {factor} for preview.", flush=True)
@@ -34,7 +41,7 @@ def modify_animation_times(code: str, factor: float = 1.0, for_precheck: bool =
34
  final_val = new_val if is_wait else max(new_val, MIN_RUN_TIME)
35
  return f"{m.group(1)}{final_val:.3f}"
36
  except ValueError:
37
- return m.group(0)
38
 
39
  code = re.sub(r"(run_time\s*=\s*)(\d+\.?\d*)", lambda m: scale_match(m, False), code)
40
  code = re.sub(r"(self\.wait\s*\(\s*)(\d+\.?\d*)", lambda m: scale_match(m, True), code)
@@ -43,9 +50,8 @@ def modify_animation_times(code: str, factor: float = 1.0, for_precheck: bool =
43
  def run_manim_pre_check(code_str: str) -> (bool, str):
44
  """
45
  Runs Manim with '-s'.
46
- - Speed Hack applied.
47
- - Soft Pass: Timeouts return True.
48
- - Errors return False.
49
  """
50
  print("🕵️ Running fast pre-check with 'manim -s'...", flush=True)
51
  fast_code = modify_animation_times(code_str, for_precheck=True)
@@ -62,12 +68,14 @@ def run_manim_pre_check(code_str: str) -> (bool, str):
62
  return True, "Pre-check successful."
63
  else:
64
  stderr_log = process.stderr.decode('utf-8', 'ignore')
65
- print(f"❌ Pre-check failed.\n{stderr_log}", flush=True)
66
  return False, f"⚠️ ERROR: Your code failed the pre-check.\n\n--- ERROR LOG ---\n{stderr_log}"
 
67
  except subprocess.TimeoutExpired:
68
  print("⌛ Pre-check timed out (30s). Soft Pass.", flush=True)
69
  return True, "⚠️ Warning: Pre-check timed out. Proceeding to full render..."
70
 
 
71
  def cleanup_media_directory():
72
  media_dir = 'media'
73
  if os.path.exists(media_dir):
@@ -88,9 +96,7 @@ def get_resolution_flags(orientation, quality):
88
 
89
  def run_manim(code_str, orientation, quality, timeout):
90
  """
91
- Executes Manim.
92
- - RECOVERY: Partial stitching ONLY happens inside 'except subprocess.TimeoutExpired'.
93
- - ERRORS: Standard errors do NOT trigger recovery.
94
  """
95
  timeout_sec = float(timeout) if timeout and float(timeout) > 0 else None
96
  print(f"🎬 Starting Full Render: {orientation} @ {quality} (Timeout: {timeout_sec}s)...", flush=True)
@@ -109,7 +115,6 @@ def run_manim(code_str, orientation, quality, timeout):
109
 
110
  try:
111
  process = subprocess.run(cmd, capture_output=True, timeout=timeout_sec, check=False)
112
-
113
  stdout_log = process.stdout.decode('utf-8', 'ignore')
114
  stderr_log = process.stderr.decode('utf-8', 'ignore')
115
  full_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
@@ -187,7 +192,7 @@ def render_video_from_code(code, orientation, quality, timeout, preview_factor):
187
  return None, f"Rendering failed: {str(e)}", gr.Button(visible=True)
188
 
189
  # ---------------------------------------------------------
190
- # NEW: Audio Merging Functions (API-Safe)
191
  # ---------------------------------------------------------
192
 
193
  def get_media_duration(file_path):
@@ -213,8 +218,7 @@ def build_atempo_filter(factor):
213
 
214
  def decode_base64_to_tempfile(data_obj):
215
  """Decodes a base64 string from a Gradio file object and saves to a temp file."""
216
- if not data_obj or 'data' not in data_obj:
217
- return None
218
 
219
  header, encoded_data = data_obj['data'].split(",", 1)
220
  file_extension = header.split('/')[1].split(';')[0]
@@ -291,17 +295,14 @@ def merge_audio_to_video(video_input, audio_input):
291
  finally:
292
  print(f"Cleaning up {len(temp_files_to_clean)} temporary files...", flush=True)
293
  for f in temp_files_to_clean:
294
- try:
295
- os.remove(f)
296
- except OSError as e:
297
- print(f"Error removing temp file {f}: {e}", flush=True)
298
 
299
  # ---------------------------------------------------------
300
- # 3. Gradio Interface
301
  # ---------------------------------------------------------
302
 
303
- # --- THIS IS THE FIX ---
304
- # Define the default code before it is used in the UI.
305
  DEFAULT_CODE = """from manim import *
306
 
307
  class GenScene(Scene):
@@ -315,8 +316,8 @@ with gr.Blocks(title="Manim Render & Audio Tool") as demo:
315
  with gr.Tab("🎬 Manim Video Renderer"):
316
  with gr.Row():
317
  with gr.Column(scale=1):
318
- # The 'value' parameter now has a defined variable to reference.
319
- code_input = gr.Code(label="Python Code", language="python", value=DEFAULT_CODE, visible=True)
320
  orientation_opt = gr.Radio(choices=["Landscape (16:9)", "Portrait (9:16)"], value="Portrait (9:16)", label="Orientation", visible=True)
321
  quality_opt = gr.Dropdown(choices=["Preview (360p)", "480p", "720p", "1080p", "4k"], value="Preview (360p)", label="Quality", visible=True)
322
  timeout_input = gr.Number(label="Render Timeout (seconds)", value=60, visible=True)
@@ -354,4 +355,6 @@ with gr.Blocks(title="Manim Render & Audio Tool") as demo:
354
  )
355
 
356
  if __name__ == "__main__":
 
 
357
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
15
  def modify_animation_times(code: str, factor: float = 1.0, for_precheck: bool = False) -> str:
16
  """
17
  Modifies wait() and run_time in the code.
18
+ - Pre-Check: Uses a robust regex to replace any run_time value with 0.01.
19
+ - Preview: Scales numeric times by a factor.
20
  """
21
  if for_precheck:
22
  print("⚡ Optimizing code for Pre-check (Zero Latency)...", flush=True)
23
+ # Use a tiny non-zero value to prevent ZeroDivisionError
24
+ replacement_time = "0.01"
25
+
26
+ # This robust regex handles: run_time=2.0, run_time=my_var, run_time=(some_function(a, b))
27
+ run_time_pattern = re.compile(r"(run_time\s*=\s*)(\(.*\)|[a-zA-Z_]\w*|\d+\.?\d*)")
28
+ code = run_time_pattern.sub(fr"\g<1>{replacement_time}", code)
29
+
30
+ # The wait regex is simpler and can remain as is
31
+ code = re.sub(r"self\.wait\s*\([^)]*\)", f"self.wait({replacement_time})", code)
32
  return code
33
 
34
  print(f"⚡ Scaling animation times by a factor of {factor} for preview.", flush=True)
 
41
  final_val = new_val if is_wait else max(new_val, MIN_RUN_TIME)
42
  return f"{m.group(1)}{final_val:.3f}"
43
  except ValueError:
44
+ return m.group(0) # Ignore if it's not a number (e.g., a variable)
45
 
46
  code = re.sub(r"(run_time\s*=\s*)(\d+\.?\d*)", lambda m: scale_match(m, False), code)
47
  code = re.sub(r"(self\.wait\s*\(\s*)(\d+\.?\d*)", lambda m: scale_match(m, True), code)
 
50
  def run_manim_pre_check(code_str: str) -> (bool, str):
51
  """
52
  Runs Manim with '-s'.
53
+ - A strong regex now prevents speed-hack errors, so we don't need special error handling.
54
+ - Soft Pass on Timeout remains as a fallback for true infinite loops.
 
55
  """
56
  print("🕵️ Running fast pre-check with 'manim -s'...", flush=True)
57
  fast_code = modify_animation_times(code_str, for_precheck=True)
 
68
  return True, "Pre-check successful."
69
  else:
70
  stderr_log = process.stderr.decode('utf-8', 'ignore')
71
+ print(f"❌ Pre-check failed with a critical error.\n{stderr_log}", flush=True)
72
  return False, f"⚠️ ERROR: Your code failed the pre-check.\n\n--- ERROR LOG ---\n{stderr_log}"
73
+
74
  except subprocess.TimeoutExpired:
75
  print("⌛ Pre-check timed out (30s). Soft Pass.", flush=True)
76
  return True, "⚠️ Warning: Pre-check timed out. Proceeding to full render..."
77
 
78
+
79
  def cleanup_media_directory():
80
  media_dir = 'media'
81
  if os.path.exists(media_dir):
 
96
 
97
  def run_manim(code_str, orientation, quality, timeout):
98
  """
99
+ Executes Manim. Partial stitching on Timeout. No recovery on other errors.
 
 
100
  """
101
  timeout_sec = float(timeout) if timeout and float(timeout) > 0 else None
102
  print(f"🎬 Starting Full Render: {orientation} @ {quality} (Timeout: {timeout_sec}s)...", flush=True)
 
115
 
116
  try:
117
  process = subprocess.run(cmd, capture_output=True, timeout=timeout_sec, check=False)
 
118
  stdout_log = process.stdout.decode('utf-8', 'ignore')
119
  stderr_log = process.stderr.decode('utf-8', 'ignore')
120
  full_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
 
192
  return None, f"Rendering failed: {str(e)}", gr.Button(visible=True)
193
 
194
  # ---------------------------------------------------------
195
+ # 3. Audio Merging Functions (API-Safe)
196
  # ---------------------------------------------------------
197
 
198
  def get_media_duration(file_path):
 
218
 
219
  def decode_base64_to_tempfile(data_obj):
220
  """Decodes a base64 string from a Gradio file object and saves to a temp file."""
221
+ if not data_obj or 'data' not in data_obj: return None
 
222
 
223
  header, encoded_data = data_obj['data'].split(",", 1)
224
  file_extension = header.split('/')[1].split(';')[0]
 
295
  finally:
296
  print(f"Cleaning up {len(temp_files_to_clean)} temporary files...", flush=True)
297
  for f in temp_files_to_clean:
298
+ try: os.remove(f)
299
+ except OSError as e: print(f"Error removing temp file {f}: {e}", flush=True)
 
 
300
 
301
  # ---------------------------------------------------------
302
+ # 4. Gradio Interface
303
  # ---------------------------------------------------------
304
 
305
+ # Define the default code here to be used in the UI
 
306
  DEFAULT_CODE = """from manim import *
307
 
308
  class GenScene(Scene):
 
316
  with gr.Tab("🎬 Manim Video Renderer"):
317
  with gr.Row():
318
  with gr.Column(scale=1):
319
+ # Use the DEFAULT_CODE variable directly here
320
+ code_input = gr.Code(label="Python Code", language="python", value=DEFAULT_CODE, visible=True)
321
  orientation_opt = gr.Radio(choices=["Landscape (16:9)", "Portrait (9:16)"], value="Portrait (9:16)", label="Orientation", visible=True)
322
  quality_opt = gr.Dropdown(choices=["Preview (360p)", "480p", "720p", "1080p", "4k"], value="Preview (360p)", label="Quality", visible=True)
323
  timeout_input = gr.Number(label="Render Timeout (seconds)", value=60, visible=True)
 
355
  )
356
 
357
  if __name__ == "__main__":
358
+ # The default code is now handled directly in the UI definition,
359
+ # so no extra logic is needed here.
360
  demo.launch(server_name="0.0.0.0", server_port=7860)