import argparse from pathlib import Path import random import string import subprocess from typing import Any, Dict, List, Tuple def get_args() -> Dict[str, Any]: parser = argparse.ArgumentParser( prog='video_to_gif.py', description='Use ffmpeg and gifski to make GIFs from videos', epilog='— Be gay and do crime') parser.add_argument('input', type=str, metavar='INPUT', nargs='+', help="input file, supports passing a glob like /Users/MyName/Videos/*.mov") parser.add_argument('-w', '--width', type=str, default='960', help='width of the GIF in pixels, respecting aspect ratio (default: 960)') parser.add_argument('-r', '--framerate', type=str, default='12', help='framerate of GIF (default: 12)'), parser.add_argument('-t', '--tag', type=str, default=get_tag(), help='optional tag included in file name') return vars(parser.parse_args()) def get_tag() -> str: return ''.join(random.choices(string.ascii_uppercase + string.digits, k=5)) def call_ffmpeg(command: List[str]) -> bool: print("ffmpeg", ' '.join(command)) full_command = ["ffmpeg", '-hide_banner', '-loglevel', 'error'] + command # TODO: Find a way to avoid shell=True usage. process = subprocess.run(' '.join(full_command), shell=True) return process.returncode == 0 def generate_gif_files(inputs: List[str], args: Dict[str, Any]) -> Dict[str, Tuple[bool, str]]: tag = args['tag'] width = args['width'] framerate = args['framerate'] output_map = {} for input in inputs: file_name = f"{Path(input).stem}_{tag}.gif" full_path = f"{Path(input).parent}/{file_name}" command = ['-i', input, '-pix_fmt', 'yuv444p', '-f', 'yuv4mpegpipe', '-', '|', 'gifski', \ '--width', f"{width}", '-r', f"{framerate}", '-o', f"{full_path}", '-'] success = call_ffmpeg(command) # Tuple of (was it successful?, path of output) output_map[input] = (success, full_path) return output_map def make_gifs(args: Dict[str, Any]) -> None: inputs = args['input'] output_map = generate_gif_files(inputs, args) successes = 0 for input in output_map: entry = output_map[input] if entry[0]: print(f"Created {entry[1]} from {input}") successes += 1 total = len(output_map) print(f"{successes} of {total} commands succeeded.") def main() -> None: make_gifs(get_args()) return if __name__ == '__main__': main()