11#(c) Alex Spirin 2023
22
3- import hashlib , os , sys , glob , subprocess , pathlib
4-
3+ import hashlib , os , sys , glob , subprocess , pathlib , platform
4+ import zipfile
5+ from shutil import copy
6+ import requests
57
68def generate_file_hash (input_file ):
9+ """Generates has for video vile based on name, size, creation time
10+ """
711 # Get file name and metadata
812 file_name = os .path .basename (input_file )
913 file_size = os .path .getsize (input_file )
@@ -50,7 +54,7 @@ def __init__(self, source_path, outdir_prefix='', videoframes_root='', update_on
5054 if len (glob .glob (source_path ))> 0 :
5155 self .frame_paths = sorted (glob .glob (source_path ))
5256 else :
53- raise Exception (f'Frame source for { outdir_prefix } not found at { source_path } \n Please specify an existing source path.' )
57+ raise FileNotFoundError (f'Frame source for { outdir_prefix } not found at { source_path } \n Please specify an existing source path.' )
5458 if os .path .exists (source_path ):
5559 if os .path .isfile (source_path ):
5660 if os .path .splitext (source_path )[1 ][1 :].lower () in image_extenstions :
@@ -63,11 +67,11 @@ def __init__(self, source_path, outdir_prefix='', videoframes_root='', update_on
6367 self .frame_paths = glob .glob (os .path .join (out_path , '*.*' ))
6468 self .source_path = out_path
6569 if len (self .frame_paths )< 1 :
66- raise Exception (f'Couldn`t extract frames from { source_path } \n Please specify an existing source path.' )
70+ raise FileNotFoundError (f'Couldn`t extract frames from { source_path } \n Please specify an existing source path.' )
6771 elif os .path .isdir (source_path ):
6872 self .frame_paths = glob .glob (os .path .join (source_path , '*.*' ))
6973 if len (self .frame_paths )< 1 :
70- raise Exception (f'Found 0 frames in { source_path } \n Please specify an existing source path.' )
74+ raise FileNotFoundError (f'Found 0 frames in { source_path } \n Please specify an existing source path.' )
7175 extensions = []
7276 if self .frame_paths is not None :
7377 for f in self .frame_paths :
@@ -81,7 +85,7 @@ def __init__(self, source_path, outdir_prefix='', videoframes_root='', update_on
8185
8286 self .frame_paths = sorted (self .frame_paths )
8387
84- else : raise Exception (f'Frame source for { outdir_prefix } not found at { source_path } \n Please specify an existing source path.' )
88+ else : raise FileNotFoundError (f'Frame source for { outdir_prefix } not found at { source_path } \n Please specify an existing source path.' )
8589 print (f'Found { len (self .frame_paths )} frames at { source_path } ' )
8690
8791 def __getitem__ (self , idx ):
@@ -100,10 +104,10 @@ def __init__(self, source_path):
100104 self .frame_paths = None
101105 self .source_path = source_path
102106 if not os .path .exists (source_path ):
103- raise Exception (f'Frame source not found at { source_path } \n Please specify an existing source path.' )
107+ raise FileNotFoundError (f'Frame source not found at { source_path } \n Please specify an existing source path.' )
104108 if os .path .exists (source_path ):
105109 if os .path .isfile (source_path ):
106- raise Exception (f'{ source_path } is a file. Please specify path to a folder.' )
110+ raise NotADirectoryError (f'{ source_path } is a file. Please specify path to a folder.' )
107111 elif os .path .isdir (source_path ):
108112 self .frame_paths = glob .glob (os .path .join (source_path , '*.*' ))
109113
@@ -123,8 +127,97 @@ def get_size(size, max_size, divisible_by=8):
123127 divisible_by = int (divisible_by )
124128 x ,y = size
125129 max_dim = max (size )
126- if max_dim > max_size :
127- ratio = max_size / max_dim
128- new_size = ((int (x * ratio )// divisible_by * divisible_by ),(int (y * ratio )// divisible_by * divisible_by ))
129- return new_size
130- return size
130+ # if max_dim>max_size:
131+ ratio = max_size / max_dim
132+ new_size = ((int (x * ratio )// divisible_by * divisible_by ),(int (y * ratio )// divisible_by * divisible_by ))
133+ return new_size
134+ # return size
135+
136+ def find_ffmpeg (start_dir ):
137+ files = glob .glob (f"{ start_dir } /**/*.*" , recursive = True )
138+ for f in files :
139+ if platform .system () == 'Linux' :
140+ if f .endswith ('ffmpeg' ): return f
141+ elif f .endswith ('ffmpeg.exe' ): return f
142+ return None
143+
144+ def download_ffmpeg (start_dir ):
145+ ffmpeg_url = 'https://github.com/GyanD/codexffmpeg/releases/download/6.0/ffmpeg-6.0-full_build.zip'
146+ print ('ffmpeg.exe not found, downloading...' )
147+ r = requests .get (ffmpeg_url , allow_redirects = True , timeout = 60 )
148+ print ('downloaded, extracting' )
149+ open ('ffmpeg-6.0-full_build.zip' , 'wb' ).write (r .content )
150+
151+ with zipfile .ZipFile ('ffmpeg-6.0-full_build.zip' , 'r' ) as zip_ref :
152+ zip_ref .extractall (f'{ start_dir } /' )
153+
154+ copy (f'{ start_dir } /ffmpeg-6.0-full_build/bin/ffmpeg.exe' , f'{ start_dir } /' )
155+ return f'{ start_dir } /ffmpeg.exe'
156+
157+
158+ def get_ffmpeg ():
159+ start_dir = os .getcwd ()
160+ ffmpeg_path = find_ffmpeg (start_dir )
161+ if ffmpeg_path is None or not os .path .exists (ffmpeg_path ):
162+ ffmpeg_path = download_ffmpeg (start_dir )
163+ return ffmpeg_path
164+
165+ def save_video (indir , video_out , batch_name = '' , start_frame = 1 , last_frame = - 1 , fps = 30 , output_format = 'h264_mp4' , use_deflicker = False ):
166+ ffmpeg_path = get_ffmpeg ()
167+ print ('Found ffmpeg at: ' , ffmpeg_path )
168+ os .makedirs (video_out , exist_ok = True )
169+ indir = indir .replace ('\\ ' ,'/' )
170+ image_path = f"{ indir } /{ batch_name } _%06d.png"
171+
172+ postfix = ''
173+ if use_deflicker :
174+ postfix += '_dfl'
175+
176+ indir_stem = indir .replace ('\\ ' ,'/' ).split ('/' )[- 1 ]
177+ out_filepath = f"{ video_out } /{ indir_stem } _{ postfix } .{ output_format .split ('_' )[- 1 ]} "
178+ if last_frame == - 1 :
179+ last_frame = len (glob .glob (f"{ indir } /*.png" ))
180+
181+ cmd = [ffmpeg_path ,
182+ '-y' ,
183+ '-vcodec' ,
184+ 'png' ,
185+ '-r' ,
186+ str (fps ),
187+ '-start_number' ,
188+ str (start_frame ),
189+ '-i' ,
190+ image_path ,
191+ '-frames:v' ,
192+ str (last_frame + 1 ),
193+ '-c:v' ]
194+
195+ if output_format == 'h264_mp4' :
196+ cmd += ['libx264' ,
197+ '-pix_fmt' ,
198+ 'yuv420p' ]
199+ elif output_format == 'qtrle_mov' :
200+ cmd += ['qtrle' ,
201+ '-vf' ,
202+ f'fps={ fps } ' ]
203+ elif output_format == 'prores_mov' :
204+ cmd += ['prores_aw' ,
205+ '-profile:v' ,
206+ '2' ,
207+ '-pix_fmt' ,
208+ 'yuv422p10' ,
209+ '-vf' ,
210+ f'fps={ fps } ' ]
211+
212+ if use_deflicker :
213+ cmd += ['-vf' ,'deflicker=mode=pm:size=10' ]
214+ cmd += [out_filepath ]
215+
216+ process = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
217+ stdout , stderr = process .communicate ()
218+ if process .returncode != 0 :
219+ print (stderr )
220+ raise RuntimeError (stderr )
221+ else :
222+ print (f"The video is ready and saved to { out_filepath } " )
223+ return 0
0 commit comments