@@ -37,6 +37,9 @@ class SceneFileWriter(object):
37
37
The PIL image mode to use when outputting PNGs
38
38
"movie_file_extension" (str=".mp4")
39
39
The file-type extension of the outputted video.
40
+ "partial_movie_files"
41
+ List of all the partial-movie files.
42
+
40
43
"""
41
44
42
45
def __init__ (self , scene , ** kwargs ):
@@ -46,7 +49,7 @@ def __init__(self, scene, **kwargs):
46
49
self .init_output_directories ()
47
50
self .init_audio ()
48
51
self .frame_count = 0
49
- self .index_partial_movie_file = 0
52
+ self .partial_movie_files = []
50
53
51
54
# Output directories and files
52
55
def init_output_directories (self ):
@@ -113,6 +116,29 @@ def init_output_directories(self):
113
116
)
114
117
)
115
118
119
+ def add_partial_movie_file (self , hash_animation ):
120
+ """Adds a new partial movie file path to scene.partial_movie_files from an hash. This method will compute the path from the hash.
121
+
122
+ Parameters
123
+ ----------
124
+ hash_animation : str
125
+ Hash of the animation.
126
+ """
127
+
128
+ # None has to be added to partial_movie_files to keep the right index with scene.num_plays.
129
+ # i.e if an animation is skipped, scene.num_plays is still incremented and we add an element to partial_movie_file be even with num_plays.
130
+ if hash_animation is None :
131
+ self .partial_movie_files .append (None )
132
+ return
133
+ new_partial_movie_file = os .path .join (
134
+ self .partial_movie_directory ,
135
+ "{}{}" .format (
136
+ hash_animation ,
137
+ file_writer_config ["movie_file_extension" ],
138
+ ),
139
+ )
140
+ self .partial_movie_files .append (new_partial_movie_file )
141
+
116
142
def get_default_module_directory (self ):
117
143
"""
118
144
This method gets the name of the directory containing
@@ -149,7 +175,7 @@ def get_resolution_directory(self):
149
175
This method gets the name of the directory that immediately contains the
150
176
video file. This name is ``<height_in_pixels_of_video>p<frame_rate>``.
151
177
For example, if you are rendering an 854x480 px animation at 15fps,
152
- the name of the directory that immediately contains the video file
178
+ the name of the directory that immediately contains the video, file
153
179
will be ``480p15``.
154
180
155
181
The file structure should look something like::
@@ -186,29 +212,6 @@ def get_image_file_path(self):
186
212
"""
187
213
return self .image_file_path
188
214
189
- def get_next_partial_movie_path (self ):
190
- """
191
- Manim renders each play-like call in a short partial
192
- video file. All such files are then concatenated with
193
- the help of FFMPEG.
194
-
195
- This method returns the path of the next partial movie.
196
-
197
- Returns
198
- -------
199
- str
200
- The path of the next partial movie.
201
- """
202
- result = os .path .join (
203
- self .partial_movie_directory ,
204
- "{}{}" .format (
205
- self .scene .play_hashes_list [self .index_partial_movie_file ],
206
- file_writer_config ["movie_file_extension" ],
207
- ),
208
- )
209
- self .index_partial_movie_file += 1
210
- return result
211
-
212
215
def get_movie_file_path (self ):
213
216
"""
214
217
Returns the final path of the written video file.
@@ -399,7 +402,9 @@ def open_movie_pipe(self):
399
402
FFMPEG and begin writing to FFMPEG's input
400
403
buffer.
401
404
"""
402
- file_path = self .get_next_partial_movie_path ()
405
+ file_path = self .partial_movie_files [self .scene .num_plays ]
406
+
407
+ # TODO #486 Why does ffmpeg need temp files ?
403
408
temp_file_path = (
404
409
os .path .splitext (file_path )[0 ]
405
410
+ "_temp"
@@ -497,21 +502,20 @@ def combine_movie_files(self):
497
502
# cuts at all the places you might want. But for viewing
498
503
# the scene as a whole, one of course wants to see it as a
499
504
# single piece.
500
- partial_movie_files = [
501
- os .path .join (
502
- self .partial_movie_directory ,
503
- "{}{}" .format (hash_play , file_writer_config ["movie_file_extension" ]),
504
- )
505
- for hash_play in self .scene .play_hashes_list
506
- ]
507
- if len (partial_movie_files ) == 0 :
508
- logger .error ("No animations in this scene" )
509
- return
505
+ partial_movie_files = [el for el in self .partial_movie_files if el is not None ]
506
+ # NOTE : Here we should do a check and raise an exeption if partial movie file is empty.
507
+ # We can't, as a lot of stuff (in particular, in tests) use scene initialization, and this error would be raised as it's just
508
+ # an empty scene initialized.
509
+
510
510
# Write a file partial_file_list.txt containing all
511
511
# partial movie files. This is used by FFMPEG.
512
512
file_list = os .path .join (
513
513
self .partial_movie_directory , "partial_movie_file_list.txt"
514
514
)
515
+ logger .debug (
516
+ f"Partial movie files to combine ({ len (partial_movie_files )} files): %(p)s" ,
517
+ {"p" : partial_movie_files [:5 ]},
518
+ )
515
519
with open (file_list , "w" ) as fp :
516
520
fp .write ("# This file is used internally by FFMPEG.\n " )
517
521
for pf_path in partial_movie_files :
0 commit comments