def check_split_video_requirements(use_mkvmerge): # type: (bool) -> None """ Validates that the proper tool is available on the system to perform the split-video command, which depends on if -c/--copy is set (to use mkvmerge) or not (to use ffmpeg). Arguments: use_mkvmerge: True if -c/--copy is set, False otherwise. Raises: click.BadParameter if the proper video splitting tool cannot be found. """ if (use_mkvmerge and not is_mkvmerge_available()) or not is_ffmpeg_available(): error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}.".format( EXTERN_TOOL='mkvmerge' if use_mkvmerge else 'ffmpeg', EXTRA_ARGS=' -c/--copy' if use_mkvmerge else '') ] error_strs += [ "Install one of the above tools to enable the split-video command." ] if not use_mkvmerge and is_mkvmerge_available(): error_strs += [ 'You can also specify `-c/--copy` to use mkvmerge for splitting.' ] elif use_mkvmerge and is_ffmpeg_available(): error_strs += [ 'You can also omit `-c/--copy` to use ffmpeg for splitting.' ] error_str = '\n'.join(error_strs) raise click.BadParameter(error_str, param_hint='split-video')
def split_video_command(ctx, output, filename, high_quality, override_args, quiet, copy, rate_factor, preset): """Split input video(s) using ffmpeg or mkvmerge.""" if ctx.obj.split_video: logging.warning('split-video command is specified twice.') ctx.obj.check_input_open() ctx.obj.split_video = True ctx.obj.split_quiet = True if quiet else False ctx.obj.split_directory = output ctx.obj.split_name_format = filename if copy: ctx.obj.split_mkvmerge = True if high_quality: logging.warning( '-hq/--high-quality flag ignored due to -c/--copy.') if override_args: logging.warning( '-f/--ffmpeg-args option ignored due to -c/--copy.') if not override_args: if rate_factor is None: rate_factor = 22 if not high_quality else 17 if preset is None: preset = 'veryfast' if not high_quality else 'slow' override_args = ( '-c:v libx264 -preset {PRESET} -crf {RATE_FACTOR} -c:a aac'.format( PRESET=preset, RATE_FACTOR=rate_factor)) if not copy: logging.info('FFmpeg codec args set: %s', override_args) if filename: logging.info('Video output file name format: %s', filename) if ctx.obj.split_directory is not None: logging.info('Video output path set: \n%s', ctx.obj.split_directory) ctx.obj.split_args = override_args mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if not (mkvmerge_available or ffmpeg_available) or ( (not mkvmerge_available and copy) or (not ffmpeg_available and not copy)): split_tool = 'ffmpeg/mkvmerge' if (not mkvmerge_available and copy): split_tool = 'mkvmerge' elif (not ffmpeg_available and not copy): split_tool = 'ffmpeg' error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}.".format( EXTERN_TOOL=split_tool, EXTRA_ARGS=' -c/--copy' if copy else ''), "Install the above tool%s to enable video splitting support." % ('s' if split_tool.find('/') > 0 else '') ] if mkvmerge_available: error_strs += [ 'You can also specify `split-video -c/--copy` to use mkvmerge for splitting.' ] error_str = '\n'.join(error_strs) logging.debug(error_str) ctx.obj.options_processed = False raise click.BadParameter(error_str, param_hint='split-video')
def split_video_command(ctx, output, filename, high_quality, override_args, quiet, copy, rate_factor, preset): """Split input video(s) using ffmpeg or mkvmerge.""" if ctx.obj.split_video: logging.warning('split-video command is specified twice.') ctx.obj.check_input_open() ctx.obj.split_video = True ctx.obj.split_quiet = True if quiet else False ctx.obj.split_directory = output ctx.obj.split_name_format = filename if copy: ctx.obj.split_mkvmerge = True if high_quality: logging.warning('-hq/--high-quality flag ignored due to -c/--copy.') if override_args: logging.warning('-f/--ffmpeg-args option ignored due to -c/--copy.') if not override_args: if rate_factor is None: rate_factor = 22 if not high_quality else 17 if preset is None: preset = 'veryfast' if not high_quality else 'slow' override_args = ('-c:v libx264 -preset {PRESET} -crf {RATE_FACTOR} -c:a copy'.format( PRESET=preset, RATE_FACTOR=rate_factor)) if not copy: logging.info('FFmpeg codec args set: %s', override_args) if filename: logging.info('Video output file name format: %s', filename) if ctx.obj.split_directory is not None: logging.info('Video output path set: \n%s', ctx.obj.split_directory) ctx.obj.split_args = override_args mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if not (mkvmerge_available or ffmpeg_available) or ( (not mkvmerge_available and copy) or (not ffmpeg_available and not copy)): split_tool = 'ffmpeg/mkvmerge' if (not mkvmerge_available and copy): split_tool = 'mkvmerge' elif (not ffmpeg_available and not copy): split_tool = 'ffmpeg' error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}.".format( EXTERN_TOOL=split_tool, EXTRA_ARGS=' -c/--copy' if copy else ''), "Install the above tool%s to enable video splitting support." % ( 's' if split_tool.find('/') > 0 else '')] if mkvmerge_available: error_strs += [ 'You can also specify `split-video -c/--copy` to use mkvmerge for splitting.'] error_str = '\n'.join(error_strs) logging.debug(error_str) ctx.obj.options_processed = False raise click.BadParameter(error_str, param_hint='split-video')
def extract_split_audio(video_pth: Path, output_dir: Path, scene_list: List[Tuple[FrameTimecode, FrameTimecode]], suppress_output: bool = True): """ Extracts the audio from each of the split up scenes using ffmpeg :param video_pth: Path to video as input :param output_dir: Output dir to export audio to :param scene_list: SceneList created by detect_scenes() :param suppress_output: If True (default) will suppress output of ffmpeg :return: """ if video_splitter.is_ffmpeg_available() is False: raise RuntimeError("Please install ffmpeg to ./ffmpeg!") audio_output_dir = output_dir / video_pth.stem audio_output_dir.mkdir(parents=True, exist_ok=True) for i, (start_time, end_time) in tqdm(enumerate(scene_list), total=len(scene_list), unit="scene", miniters=1, desc="Exporting audio"): duration = (end_time - start_time) call_list = ['ffmpeg'] if suppress_output: call_list += ['-v', 'quiet'] elif i > 0: # Only show ffmpeg output for the first call, which will display any # errors if it fails, and then break the loop. We only show error messages # for the remaining calls. call_list += ['-v', 'error'] call_list += [ '-y', '-ss', start_time.get_timecode(), '-i', f"{video_pth.absolute()}" ] call_list += [ '-strict', '-2', '-t', duration.get_timecode(), f"{audio_output_dir.joinpath(video_pth.stem + f'_{i + 1}.mp3')}" ] ret_val = subprocess.call(call_list) if not suppress_output and i == 0 and len(scene_list) > 1: logger.info( 'Output from ffmpeg for Scene 1 shown above, splitting remaining scenes...' )
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug( 'Skipping processing, CLI options were not parsed successfully.' ) return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...),\n' ' or failed to process all command line arguments.') return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames) / duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv(stats_file, base_timecode) else: logging.debug( 'No frame metrics updated, skipping update of the stats file.' ) # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Ensure we don't divide by zero. if scene_list: logging.info( 'Detected %d scenes, average shot length %.1f seconds.', len(scene_list), sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(len(scene_list))) else: logging.info('No scenes detected.') # Handle list-scenes command. if self.scene_list_output: scene_list_filename = Template( self.scene_list_name_format).safe_substitute( VIDEO_NAME=video_name) if not scene_list_filename.lower().endswith('.csv'): scene_list_filename += '.csv' scene_list_path = get_and_create_path( scene_list_filename, self.scene_list_directory if self.scene_list_directory is not None else self.output_directory) logging.info('Writing scene list to CSV file:\n %s', scene_list_path) with open(scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, scene_list, cut_list) # Handle `list-scenes`. if self.print_scene_list: logging.info( """Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join([ ' | %5d | %11d | %s | %11d | %s |' % (i + 1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list) ])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images( scene_list=scene_list, video_name=video_name, image_name_template=self.image_name_format, output_dir=self.image_directory, downscale_factor=self.video_manager.get_downscale_factor()) # Handle export-html command. if self.export_html: html_filename = Template( self.html_name_format).safe_substitute(VIDEO_NAME=video_name) if not html_filename.lower().endswith('.html'): html_filename += '.html' html_path = get_and_create_path( html_filename, self.image_directory if self.image_directory is not None else self.output_directory) logging.info('Exporting to html file:\n %s:', html_path) if not self.html_include_images: self.image_filenames = None write_scene_list_html(html_path, scene_list, cut_list, image_filenames=self.image_filenames, image_width=self.image_width, image_height=self.image_height) # Handle split-video command. if self.split_video: # Add proper extension to filename template if required. dot_pos = self.split_name_format.rfind('.') if self.split_mkvmerge and not self.split_name_format.endswith( '.mkv'): self.split_name_format += '.mkv' # Don't add if we find an extension between 2 and 4 characters elif not (dot_pos >= 0) or (dot_pos >= 0 and not ((len(self.split_name_format) - (dot_pos + 1) <= 4 >= 2))): self.split_name_format += '.mp4' output_file_prefix = get_and_create_path( self.split_name_format, self.split_directory if self.split_directory is not None else self.output_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.warning( 'ffmpeg not found, falling back to fast copy mode (split-video -c/--copy).' ) split_video_mkvmerge(video_paths, scene_list, output_file_prefix, video_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: if self.split_mkvmerge: logging.warning( 'mkvmerge not found, falling back to normal splitting' ' mode (split-video).') split_video_ffmpeg(video_paths, scene_list, output_file_prefix, video_name, arg_override=self.split_args, hide_progress=self.quiet_mode, suppress_output=self.quiet_mode or self.split_quiet) else: if not (mkvmerge_available or ffmpeg_available): error_strs = [ "ffmpeg/mkvmerge is required for split-video [-c/--copy]." ] else: error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}." .format(EXTERN_TOOL='mkvmerge' if self.split_mkvmerge else 'ffmpeg', EXTRA_ARGS=' -c/--copy' if self.split_mkvmerge else '') ] error_strs += [ "Install one of the above tools to enable the split-video command." ] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') if scene_list: logging.info( 'Video splitting completed, individual scenes written to disk.' )
def find_scenes(video_path): start_time = time.time() print("Analyzing video " + video_path) # type: (str) -> List[Tuple[FrameTimecode, FrameTimecode]] video_manager = VideoManager([video_path]) stats_manager = StatsManager() # Pass StatsManager to SceneManager to accelerate computing time scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (each detector's constructor # takes detector options, e.g. threshold). scene_manager.add_detector(ContentDetector()) base_timecode = video_manager.get_base_timecode() # We save our stats file to {VIDEO_PATH}.stats.csv. stats_file_path = '%s.stats.csv' % (video_path) scene_list = [] folder = os.path.splitext(video_path)[0] if os.path.exists(folder): print( '--- STOP : The folder for this video already exists, it is probably already split.' ) else: try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) if video_splitter.is_ffmpeg_available(): # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Each scene is a tuple of (start, end) FrameTimecodes. print('%s scenes obtained' % len(scene_list)) if len(scene_list) > 0: # STATISTICS : Store scenes length with open(FILE_SCENE_LENGH, 'a') as myfile: for i, scene in enumerate(scene_list): myfile.write( '%s, %d, %f\n' % (os.path.splitext( os.path.basename(video_path))[0], scene[1].get_frames() - scene[0].get_frames(), (scene[1] - scene[0]).get_seconds())) # STATISTICS : Store number of scenes with open(FILE_SCENE_NUMBER, 'a') as myfile: myfile.write('%s,%d\n' % (os.path.splitext( os.path.basename(video_path))[0], len(scene_list))) # Split the video print('Splitting the video. Put scenes in %s/%s' % (folder, VIDEO_SPLIT_TEMPLATE)) os.mkdir(folder) video_splitter.split_video_ffmpeg( [video_path], scene_list, folder + "/" + VIDEO_SPLIT_TEMPLATE + ".mp4", os.path.basename(folder), suppress_output=True) print("-- Finished video splitting in {:.2f}s --".format( time.time() - start_time)) else: print( 'Ffmpeg is not installed on your computer. Please install it before running this code' ) finally: video_manager.release() return scene_list
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug( 'Skipping processing, CLI options were not parsed successfully.' ) return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...).' ) return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, start_time=self.start_frame, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames) / duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv(stats_file, base_timecode) else: logging.debug( 'No frame metrics updated, skipping update of the stats file.' ) # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Handle list-scenes command. # Handle `list-scenes -o`. if self.scene_list_path is not None: with open(self.scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, cut_list, scene_list) # Handle `list-scenes`. list_length = len(scene_list) if len(scene_list) else 1 logging.info( 'Detected %d scenes, average shot length %.1f seconds.', list_length, sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(list_length)) if self.print_scene_list: logging.info( """ Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join([ ' | %5d | %11d | %s | %11d | %s |' % (i + 1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list) ])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images(scene_list=scene_list, image_prefix=video_name, output_dir=self.image_directory) # Handle split-video command. if self.split_video: output_file_name = self.get_output_file_path( video_name, output_dir=self.split_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.info('ffmpeg not found.') logging.info('Splitting input video%s using mkvmerge...', 's' if len(video_paths) > 1 else '') split_video_mkvmerge(video_paths, scene_list, output_file_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: logging.info('Splitting input video%s using ffmpeg...', 's' if len(video_paths) > 1 else '') split_video_ffmpeg(video_paths, scene_list, output_file_name, arg_override=self.split_args, hide_progress=self.quiet_mode or self.split_quiet, suppress_output=self.quiet_mode or self.split_quiet) else: error_strs = [ "ffmpeg/mkvmerge is required for video splitting.", "Install one of the above tools to enable the split-video command." ] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') logging.info( 'Video splitting completed, individual scenes written to disk.' )
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug('Skipping processing, CLI options were not parsed successfully.') return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...),\n' ' or failed to process all command line arguments.') return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames)/duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv( stats_file, base_timecode) else: logging.debug('No frame metrics updated, skipping update of the stats file.') # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Ensure we don't divide by zero. if scene_list: logging.info('Detected %d scenes, average shot length %.1f seconds.', len(scene_list), sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(len(scene_list))) else: logging.info('No scenes detected.') # Handle list-scenes command. if self.scene_list_output: scene_list_filename = Template(self.scene_list_name_format).safe_substitute( VIDEO_NAME=video_name) if not scene_list_filename.lower().endswith('.csv'): scene_list_filename += '.csv' scene_list_path = self.get_output_file_path( scene_list_filename, self.scene_list_directory) logging.info('Writing scene list to CSV file:\n %s', scene_list_path) with open(scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, scene_list, cut_list) # Handle `list-scenes`. if self.print_scene_list: logging.info("""Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join( [' | %5d | %11d | %s | %11d | %s |' % ( i+1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list)])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images(scene_list=scene_list, video_name=video_name, image_name_template=self.image_name_format, output_dir=self.image_directory) # Handle export-html command. if self.export_html: html_filename = Template(self.html_name_format).safe_substitute( VIDEO_NAME=video_name) if not html_filename.lower().endswith('.html'): html_filename += '.html' html_path = self.get_output_file_path( html_filename, self.image_directory) logging.info('Exporting to html file:\n %s:', html_path) if not self.html_include_images: self.image_filenames = None write_scene_list_html(html_path, scene_list, cut_list, image_filenames=self.image_filenames, image_width=self.image_width, image_height=self.image_height) # Handle split-video command. if self.split_video: # Add proper extension to filename template if required. dot_pos = self.split_name_format.rfind('.') if self.split_mkvmerge and not self.split_name_format.endswith('.mkv'): self.split_name_format += '.mkv' # Don't add if we find an extension between 2 and 4 characters elif not (dot_pos >= 0) or ( dot_pos >= 0 and not ((len(self.split_name_format) - (dot_pos+1) <= 4 >= 2))): self.split_name_format += '.mp4' output_file_prefix = self.get_output_file_path( self.split_name_format, output_dir=self.split_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.warning( 'ffmpeg not found, falling back to fast copy mode (split-video -c/--copy).') split_video_mkvmerge(video_paths, scene_list, output_file_prefix, video_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: if self.split_mkvmerge: logging.warning('mkvmerge not found, falling back to normal splitting' ' mode (split-video).') split_video_ffmpeg(video_paths, scene_list, output_file_prefix, video_name, arg_override=self.split_args, hide_progress=self.quiet_mode, suppress_output=self.quiet_mode or self.split_quiet) else: if not (mkvmerge_available or ffmpeg_available): error_strs = ["ffmpeg/mkvmerge is required for split-video [-c/--copy]."] else: error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}.".format( EXTERN_TOOL='mkvmerge' if self.split_mkvmerge else 'ffmpeg', EXTRA_ARGS=' -c/--copy' if self.split_mkvmerge else '')] error_strs += ["Install one of the above tools to enable the split-video command."] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') if scene_list: logging.info('Video splitting completed, individual scenes written to disk.')