Skip to content

Commit 8b9dba2

Browse files
leotrskolibril13
andauthored
Make manim crash when ffmpeg crashes (#546)
* make manim crash when ffmpeg crashes * lol black * removed Example3DNo1 for not breaking the docs * Quick test with all 3d scenes removed * re added 3d scene * woah that was hard to debug * fix underlines * woah THAT was hard to debug * black * blackest night * do the other call too * I just want to re-trigger the RTD build Co-authored-by: kolibril13 <[email protected]>
1 parent 8ffcf06 commit 8b9dba2

File tree

6 files changed

+59
-25
lines changed

6 files changed

+59
-25
lines changed

docs/source/examples/3d.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,3 @@
7878
self.add(axes, curve1)
7979
self.set_camera_orientation(phi=80 * DEGREES, theta=-60 * DEGREES)
8080
self.wait()
81-

docs/source/installation/linux.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The two necessary dependencies are cairo and ffmpeg. LaTeX is strongly
1111
recommended, as it is necessary to have access to the ``Tex`` and ``MathTex`` classes.
1212

1313
Ubuntu/Mint/Debian
14-
*************
14+
******************
1515

1616
Before installing anything, make sure that your system is up to date.
1717

@@ -42,15 +42,15 @@ To install LaTeX:
4242
If you don't have python3-pip installed, install it:
4343

4444
.. code-block:: bash
45-
45+
4646
sudo apt install python3-pip
47-
47+
4848
.. note:: These instructions are also valid for other Debian-based
4949
distributions or distributions that use the ``apt`` package manager.
5050

5151

5252
Fedora/CentOS/RHEL
53-
*************
53+
******************
5454

5555
To install cairo:
5656

@@ -116,7 +116,7 @@ To install LaTeX:
116116
If you don't have python-pip installed, install it:
117117

118118
.. code-block:: bash
119-
119+
120120
sudo pacman -S python-pip
121121
122122

docs/source/manim_directive.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ def run(self):
168168
video_dir = os.path.join(media_dir, "videos")
169169
output_file = f"{clsname}-{classnamedict[clsname]}"
170170

171+
# Important: note that all scenes are being rendered on the same python
172+
# interpreter. That means that each time we change the config, that
173+
# same config will be used for the next scene. For this reason, we
174+
# have to make sure to restore the original config after each scene.
175+
save_config_code = [
176+
"original_config = copy.deepcopy(config)",
177+
"original_fw_config = copy.deepcopy(file_writer_config)",
178+
]
171179
file_writer_config_code = [
172180
f'config["frame_rate"] = {frame_rate}',
173181
f'config["pixel_height"] = {pixel_height}',
@@ -181,6 +189,14 @@ def run(self):
181189
f'file_writer_config["save_as_gif"] = {save_as_gif}',
182190
f'file_writer_config["output_file"] = "{output_file}"',
183191
]
192+
file_writer_config_code.append(
193+
'file_writer_config["write_to_movie"] = '
194+
+ ("False" if save_last_frame else "True")
195+
)
196+
restore_config_code = [
197+
"config = original_config",
198+
"file_writer_config = original_fw_config",
199+
]
184200

185201
user_code = self.content
186202
if user_code[0].startswith(">>> "): # check whether block comes from doctest
@@ -190,9 +206,12 @@ def run(self):
190206

191207
code = [
192208
"from manim import *",
209+
f"logger.info('rendering {clsname}')",
210+
*save_config_code,
193211
*file_writer_config_code,
194212
*user_code,
195213
f"{clsname}().render()",
214+
*restore_config_code,
196215
]
197216
exec("\n".join(code), globals())
198217

manim/constants.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
"""
2-
Constant definitions.
3-
"""
1+
"""Constant definitions."""
42

53
import numpy as np
64

manim/scene/scene_file_writer.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,11 @@ def combine_movie_files(self):
544544
if not self.includes_sound:
545545
commands.insert(-1, "-an")
546546

547-
combine_process = subprocess.Popen(commands)
548-
combine_process.wait()
547+
try:
548+
subprocess.check_call(commands)
549+
except subprocess.CalledProcessError as exc:
550+
logger.error(f"FFMPEG returned with code {exc.returncode}")
551+
raise exc
549552

550553
if self.includes_sound:
551554
sound_file_path = movie_file_path.replace(
@@ -582,7 +585,12 @@ def combine_movie_files(self):
582585
# "-shortest",
583586
temp_file_path,
584587
]
585-
subprocess.call(commands)
588+
try:
589+
subprocess.check_call(commands)
590+
except subprocess.CalledProcessError as exc:
591+
logger.error(f"FFMPEG returned with code {exc.returncode}")
592+
raise exc
593+
586594
shutil.move(temp_file_path, movie_file_path)
587595
os.remove(sound_file_path)
588596

manim/utils/caching.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ def wrapper(self, scene, *args, **kwargs):
3636
hash_play = get_hash_from_play_call(
3737
self, self.camera, animations, mobjects_on_scene
3838
)
39-
if self.file_writer.is_already_cached(hash_play):
40-
logger.info(
41-
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
42-
{"hash_play": hash_play},
43-
)
44-
file_writer_config["skip_animations"] = True
39+
if file_writer_config["write_to_movie"]:
40+
if self.file_writer.is_already_cached(hash_play):
41+
logger.info(
42+
f"Animation {self.num_plays} : Using cached data (hash : %(hash_play)s)",
43+
{"hash_play": hash_play},
44+
)
45+
file_writer_config["skip_animations"] = True
4546
else:
4647
hash_play = "uncached_{:05}".format(self.num_plays)
4748
self.animations_hashes.append(hash_play)
48-
self.file_writer.add_partial_movie_file(hash_play)
49+
if (
50+
not file_writer_config["disable_caching"]
51+
and file_writer_config["write_to_movie"]
52+
):
53+
self.file_writer.add_partial_movie_file(hash_play)
4954
logger.debug(
5055
"List of the first few animation hashes of the scene: %(h)s",
5156
{"h": str(self.animations_hashes[:5])},
@@ -84,15 +89,20 @@ def wrapper(self, scene, duration=DEFAULT_WAIT_TIME, stop_condition=None):
8489
hash_wait = get_hash_from_wait_call(
8590
self, self.camera, duration, stop_condition, scene.get_mobjects()
8691
)
87-
if self.file_writer.is_already_cached(hash_wait):
88-
logger.info(
89-
f"Wait {self.num_plays} : Using cached data (hash : {hash_wait})"
90-
)
91-
file_writer_config["skip_animations"] = True
92+
if file_writer_config["write_to_movie"]:
93+
if self.file_writer.is_already_cached(hash_wait):
94+
logger.info(
95+
f"Wait {self.num_plays} : Using cached data (hash : {hash_wait})"
96+
)
97+
file_writer_config["skip_animations"] = True
9298
else:
9399
hash_wait = "uncached_{:05}".format(self.num_plays)
94100
self.animations_hashes.append(hash_wait)
95-
self.file_writer.add_partial_movie_file(hash_wait)
101+
if (
102+
not file_writer_config["disable_caching"]
103+
and file_writer_config["write_to_movie"]
104+
):
105+
self.file_writer.add_partial_movie_file(hash_wait)
96106
logger.debug(
97107
"List of the first few animation hashes of the scene: %(h)s",
98108
{"h": str(self.animations_hashes[:5])},

0 commit comments

Comments
 (0)