From 13fc4bb70cd11892265f9348cbd4e634f01f2e52 Mon Sep 17 00:00:00 2001 From: Amber Date: Mon, 2 Dec 2024 22:59:16 -0500 Subject: [PATCH] f Adding an alternate script that outsources the GIF generation process to gifski; it does better than the original script for colored GIFs, looks less good for B&W stuff. Overall, it's better, and it may become the 'main' version of this script. --- video_to_gif2.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 video_to_gif2.py diff --git a/video_to_gif2.py b/video_to_gif2.py new file mode 100644 index 0000000..d7aaa70 --- /dev/null +++ b/video_to_gif2.py @@ -0,0 +1,77 @@ + +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()