video-to-gif/video_to_gif.py

84 lines
2.7 KiB
Python

import argparse
import glob
from pathlib import Path
import random
import string
import subprocess
from typing import Any, Dict, List
def get_args() -> Dict[str, Any]:
parser = argparse.ArgumentParser(
prog='video_to_gif.py',
description='Use ffmpeg to make GIFs from videos',
epilog='— Be gay and do crime')
parser.add_argument('-i', '--input', type=str, required=True,
help="Input pattern like '/users/MyName/Videos/*.mov'")
parser.add_argument('-w', '--width', type=str, default='960',
help='Width of the GIF in pixels, respecting aspect ratio')
parser.add_argument('-r', '--framerate', type=str,
default='12', help='Framerate of GIF')
parser.add_argument('-s', '--scaler', type=str, choices=get_scaling_algorithms(),
default='lanczos', help='Scaling algorithm to use')
parser.add_argument('-t', '--tag', type=str, default=get_tag(),
help='Optional tag included in file name')
return vars(parser.parse_args())
def get_scaling_algorithms() -> List[str]:
return ['fast_bilinear', 'bilinear', 'bicubic', 'experimental', 'neighbor',
'area', 'bicublin', 'gauss', 'sinc', 'lanczos', 'spline']
# TODO: Figure out how to specify both scaling and dithering algorithms.
def get_dithering_algorithms() -> List[str]:
return ['none', 'auto', 'bayer', 'ed', 'a_dither', 'x_dither']
def get_tag() -> str:
return ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))
def get_inputs(args: Dict[str, Any]) -> List[str]:
return glob.glob(args['input'])
def call_ffmpeg(command: str) -> str:
print("ffmpeg", *command.split(' '))
return str(subprocess.run(["ffmpeg", '-hide_banner', '-loglevel', 'error', *command.split(' ')]))
def generate_gif_files(inputs: List[str], args: Dict[str, Any]) -> Dict[str, str]:
tag = args['tag']
width = args['width']
framerate = args['framerate']
scaler = args['scaler']
output_map = {}
for input in inputs:
file_name = f"{Path(input).stem}_{tag}.gif"
full_path = f"{Path(input).parent}/{file_name}"
call_ffmpeg(f"-i {input} -f gif -r {framerate} -filter_complex scale={ \
width}:-1:flags={scaler},split[v1][v2];[v1]palettegen[plt];[v2][plt]paletteuse { \
full_path}")
output_map[input] = full_path
return output_map
def make_gifs(args: Dict[str, Any]) -> None:
inputs = get_inputs(args)
output_map = generate_gif_files(inputs, args)
for input in output_map:
print(f"Created {output_map[input]} from {input}")
def main() -> None:
make_gifs(get_args())
return
if __name__ == '__main__':
main()