Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,37 +12,27 @@ import tempfile
|
|
| 12 |
# 1. Helper Functions
|
| 13 |
# ---------------------------------------------------------
|
| 14 |
|
| 15 |
-
def modify_animation_times(code: str, factor: float
|
| 16 |
"""
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
- Preview: Scales numeric times by a factor.
|
| 20 |
"""
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 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)
|
| 35 |
-
MIN_RUN_TIME = 0.1
|
| 36 |
|
| 37 |
def scale_match(m, is_wait):
|
| 38 |
try:
|
| 39 |
val = float(m.group(2))
|
| 40 |
new_val = val * factor
|
|
|
|
| 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 |
-
|
|
|
|
| 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)
|
| 48 |
return code
|
|
@@ -50,11 +40,14 @@ def modify_animation_times(code: str, factor: float = 1.0, for_precheck: bool =
|
|
| 50 |
def run_manim_pre_check(code_str: str) -> (bool, str):
|
| 51 |
"""
|
| 52 |
Runs Manim with '-s'.
|
| 53 |
-
-
|
| 54 |
-
-
|
|
|
|
| 55 |
"""
|
| 56 |
print("🕵️ Running fast pre-check with 'manim -s'...", flush=True)
|
| 57 |
-
|
|
|
|
|
|
|
| 58 |
|
| 59 |
with open("scene_pre_check.py", "w", encoding="utf-8") as f:
|
| 60 |
f.write(fast_code)
|
|
@@ -115,6 +108,7 @@ def run_manim(code_str, orientation, quality, timeout):
|
|
| 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}"
|
|
@@ -182,7 +176,7 @@ def render_video_from_code(code, orientation, quality, timeout, preview_factor):
|
|
| 182 |
return None, "Error: No valid code.", gr.Button(visible=False)
|
| 183 |
|
| 184 |
if quality == "Preview (360p)":
|
| 185 |
-
code_to_render = modify_animation_times(code, factor=float(preview_factor) or 0.5
|
| 186 |
else:
|
| 187 |
code_to_render = code
|
| 188 |
|
|
@@ -302,7 +296,6 @@ def merge_audio_to_video(video_input, audio_input):
|
|
| 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,7 +309,6 @@ with gr.Blocks(title="Manim Render & Audio Tool") as demo:
|
|
| 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)
|
|
@@ -355,6 +347,4 @@ with gr.Blocks(title="Manim Render & Audio Tool") as demo:
|
|
| 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)
|
|
|
|
| 12 |
# 1. Helper Functions
|
| 13 |
# ---------------------------------------------------------
|
| 14 |
|
| 15 |
+
def modify_animation_times(code: str, factor: float) -> str:
|
| 16 |
"""
|
| 17 |
+
Scales all numeric run_time and wait() values by a given factor.
|
| 18 |
+
This single, safe function is now used for both Pre-check and Preview modes.
|
|
|
|
| 19 |
"""
|
| 20 |
+
print(f"⚡ Scaling animation times by a factor of {factor}...", flush=True)
|
| 21 |
+
# A safe minimum to prevent Manim from crashing with run_time=0
|
| 22 |
+
MIN_RUN_TIME = 0.01
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
def scale_match(m, is_wait):
|
| 25 |
try:
|
| 26 |
val = float(m.group(2))
|
| 27 |
new_val = val * factor
|
| 28 |
+
# For waits, we can go low. For run_time, we must stay above zero.
|
| 29 |
final_val = new_val if is_wait else max(new_val, MIN_RUN_TIME)
|
| 30 |
return f"{m.group(1)}{final_val:.3f}"
|
| 31 |
except ValueError:
|
| 32 |
+
# This happens if run_time is a variable. We safely ignore it.
|
| 33 |
+
return m.group(0)
|
| 34 |
|
| 35 |
+
# This safe regex only matches explicit numbers, preventing SyntaxErrors.
|
| 36 |
code = re.sub(r"(run_time\s*=\s*)(\d+\.?\d*)", lambda m: scale_match(m, False), code)
|
| 37 |
code = re.sub(r"(self\.wait\s*\(\s*)(\d+\.?\d*)", lambda m: scale_match(m, True), code)
|
| 38 |
return code
|
|
|
|
| 40 |
def run_manim_pre_check(code_str: str) -> (bool, str):
|
| 41 |
"""
|
| 42 |
Runs Manim with '-s'.
|
| 43 |
+
- Uses the safe `modify_animation_times` function with a small factor for a significant speedup.
|
| 44 |
+
- Errors are now always considered critical because the modification is safe.
|
| 45 |
+
- Soft Pass on Timeout remains as a fallback.
|
| 46 |
"""
|
| 47 |
print("🕵️ Running fast pre-check with 'manim -s'...", flush=True)
|
| 48 |
+
# Using a factor of 0.2 makes a 10-second animation take 2 seconds in pre-check.
|
| 49 |
+
# This is a good balance of speed and reliability.
|
| 50 |
+
fast_code = modify_animation_times(code_str, factor=0.2)
|
| 51 |
|
| 52 |
with open("scene_pre_check.py", "w", encoding="utf-8") as f:
|
| 53 |
f.write(fast_code)
|
|
|
|
| 108 |
|
| 109 |
try:
|
| 110 |
process = subprocess.run(cmd, capture_output=True, timeout=timeout_sec, check=False)
|
| 111 |
+
|
| 112 |
stdout_log = process.stdout.decode('utf-8', 'ignore')
|
| 113 |
stderr_log = process.stderr.decode('utf-8', 'ignore')
|
| 114 |
full_logs = f"--- MANIM STDOUT ---\n{stdout_log}\n\n--- MANIM STDERR ---\n{stderr_log}"
|
|
|
|
| 176 |
return None, "Error: No valid code.", gr.Button(visible=False)
|
| 177 |
|
| 178 |
if quality == "Preview (360p)":
|
| 179 |
+
code_to_render = modify_animation_times(code, factor=float(preview_factor) or 0.5)
|
| 180 |
else:
|
| 181 |
code_to_render = code
|
| 182 |
|
|
|
|
| 296 |
# 4. Gradio Interface
|
| 297 |
# ---------------------------------------------------------
|
| 298 |
|
|
|
|
| 299 |
DEFAULT_CODE = """from manim import *
|
| 300 |
|
| 301 |
class GenScene(Scene):
|
|
|
|
| 309 |
with gr.Tab("🎬 Manim Video Renderer"):
|
| 310 |
with gr.Row():
|
| 311 |
with gr.Column(scale=1):
|
|
|
|
| 312 |
code_input = gr.Code(label="Python Code", language="python", value=DEFAULT_CODE, visible=True)
|
| 313 |
orientation_opt = gr.Radio(choices=["Landscape (16:9)", "Portrait (9:16)"], value="Portrait (9:16)", label="Orientation", visible=True)
|
| 314 |
quality_opt = gr.Dropdown(choices=["Preview (360p)", "480p", "720p", "1080p", "4k"], value="Preview (360p)", label="Quality", visible=True)
|
|
|
|
| 347 |
)
|
| 348 |
|
| 349 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 350 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|