def extract_frames(video_path: str, output_frames_dir: str, notification_path: Optional[str]=None, quality: int=0) -> None: """Use ffmpeg to read a video file and extract the frames. Requires 'ffmpeg' to be on the command line. The resulting JPEG frames will be named in the format '%06d.jpg'. Uses notification_path to indicate whether the extraction completed; this will be named video_path'/.finished-extraction' if None. Will warn and write over the output dir if it exists but no file at notification_path exists. Quality must be between 0 and 31, where 0 is the highest. """ if notification_path is None: notification_path = pjoin(dirname(video_path), ".finished-extraction") if pexists(notification_path) and pexists(output_frames_dir): logger.info("Frames directory {} is already complete; skipping.".format(output_frames_dir)) else: if pexists(output_frames_dir): logger.warn("Frames directory {} already exists but is incomplete. Extracting frames...".format(output_frames_dir)) else: logger.info("Extracting frames into {}".format(output_frames_dir)) if not pexists(video_path): raise ValueError('Cannot extract frames: video.avi does not exist') wrap_cmd_call([ 'ffmpeg', '-i', video_path, '-q:v', quality, pjoin(output_frames_dir, '%06d.jpg') ]) with open(notification_path, 'w'): print('') # all done
def run(self, args: List[str]) -> None: full_args = self.parser.parse_args(args[1:2]) subcommand = full_args.subcommand.replace('-', '_') if not hasattr(self.target, subcommand) and not subcommand.startswith('_'): print(Fore.RED + 'Unrecognized subcommand {}'.format(subcommand)) self.parser.print_help() return # clever; from Chase Seibert: https://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html # use dispatch pattern to invoke method with same name try: if self.temp_dir is not None: if pexists(self.temp_dir) and pdir(self.temp_dir): shutil.rmtree(self.temp_dir) elif pexists(self.temp_dir): raise InvalidDirectoryException(self.temp_dir) remake_dirs(self.temp_dir) logger.debug("Created temp dir at {}".format(self.temp_dir)) getattr(self.target, subcommand)() except NaturalExpectedException as e: pass # ignore totally except KeyboardInterrupt as e: try: logger.fatal("Received cancellation signal", exc_info=True) self.cancel_handler(e) except BaseException: pass raise e except SystemExit as e: try: logger.fatal("Received system exit signal", exc_info=True) self.cancel_handler(e) except BaseException: pass raise e except BaseException as e: try: logger.fatal("{} failed!".format(self.parser.prog), exc_info=True) self.error_handler(e) except BaseException: pass raise e finally: if self.temp_dir is not None: if pexists(self.temp_dir): logger.debug("Deleted temp dir at {}".format( self.temp_dir)) shutil.rmtree(self.temp_dir) try: os.remove(self.temp_dir) except IOError: pass
def pempty(s: str): """ Assesses whether the path is "empty" OR does not exist. Returns False if the path exists or is either: - A socket or block device (even if "empty" -- does not attempt to read) - A nonempty file - A directory containing subpaths - A symlink to a nonempty file Currently DOES NOT HANDLE: Symlinks to anything other than a file. Will raise a TypeError. """ if not pexists(s): return True s = Path(s) if s.is_block_device() or s.is_socket(): return False elif s.is_dir(): # short-circuit for _ in s.iterdir(): return False return True elif s.is_symlink(): target = Path(os.readlink(str(s))) if not target.exists(): return True if target.is_file(): return s.lstat().st_size == 0 # TODO if dir without infinite loops elif s.is_file(): return s.stat().st_size == 0 raise TypeError("Unknown path type {}".format(s))
def sevenz(dir_to_sevenz: str, sevenz_path: str, overwrite: OverwriteChoice = OverwriteChoice.FAIL, _7z_executable: str = '7z') -> None: """7-zips a directory and adds a .sha256 with the same base filename of the output archive. Leaves the original directory when it finishes. Requires '7z' to be on the command line. """ if not pexists(dir_to_sevenz): raise InvalidDirectoryException( "The path {} to 7-zip does not exist".format(dir_to_sevenz)) if not pdir(dir_to_sevenz): raise InvalidDirectoryException( "The path {} to 7-zip is not a directory".format(dir_to_sevenz)) file_hasher = FileHasher(algorithm=hashlib.sha256, extension='.sha256') logging.info("7-zipping files in {}".format(dir_to_sevenz)) if pexists(sevenz_path) and not pfile(sevenz_path): raise InvalidFileException( "The 7-zip file cannot be written to {}: The path exists and is not a file" .format(sevenz_path)) if pexists(sevenz_path): if overwrite is OverwriteChoice.FAIL: raise InvalidFileException( "Cannot proceed: The 7-zip file {} already exists.".format( sevenz_path)) elif overwrite is OverwriteChoice.WARN: warnings.warn( "The 7-zip file {} already exists. Won't overwrite.".format( sevenz_path)) elif overwrite is OverwriteChoice.OVERWRITE: os.remove(sevenz_path) elif overwrite is OverwriteChoice.IGNORE: pass wrap_cmd_call([_7z_executable, 'a', sevenz_path, dir_to_sevenz]) file_hasher.add_hash(sevenz_path) # will overwrite regardless
def file_from_env_var(var: str) -> str: """ Just returns the path of a file specified in an environment variable, checking that it's a file. Will raise a MissingResourceException error if not set or not a file. :param var: The environment variable name, not including the $ """ if var not in os.environ: raise MissingResourceException( 'Environment variable ${} is not set'.format(var)) config_file_path = fix_path(os.environ[var]) if not pexists(config_file_path): raise MissingResourceException("{} file {} does not exist".format( var, config_file_path)) if not pfile(config_file_path): raise MissingResourceException("{} file {} is not a file".format( var, config_file_path)) return config_file_path