Skip to content

Instantly share code, notes, and snippets.

@mskrajnowski
Created April 16, 2015 12:19
Show Gist options
  • Save mskrajnowski/6540217bc70eab5b7c8f to your computer and use it in GitHub Desktop.
Save mskrajnowski/6540217bc70eab5b7c8f to your computer and use it in GitHub Desktop.
Video to GIF conversion script using ffmpeg
"""
Video to GIF conversion script using ffmpeg
based on http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html
"""
import argparse
import datetime
import os
import subprocess
import tempfile
def parse_timedelta(string):
error_msg = (
u'{!r} is not a valid time, '
u'must be a number in seconds, or in hh:mm:ss[.xxx] form'
.format(string)
)
parts = tuple(reversed(string.split(u':')))
if len(parts) > 3:
raise argparse.ArgumentTypeError(error_msg)
try:
hours = int(parts[2]) if len(parts) > 2 else 0
minutes = int(parts[1]) if len(parts) > 1 else 0
seconds = float(parts[0])
except ValueError:
raise argparse.ArgumentTypeError(error_msg)
return datetime.timedelta(
hours=hours,
minutes=minutes,
seconds=seconds,
)
def format_timedelta(delta):
total_seconds = delta.total_seconds()
hours = int(total_seconds / 3600)
minutes = int(total_seconds % 3600 / 60)
seconds = total_seconds % 60
return '{:02d}:{:02d}:{:02.3f}'.format(hours, minutes, seconds)
def parse_args():
parser = argparse.ArgumentParser(
description='Converts video to .gif using ffmpeg',
)
parser.add_argument(
'files',
nargs='+',
type=argparse.FileType('r'),
help='video files to convert'
)
parser.add_argument(
'--filters',
help='ffmpeg filters to apply',
default='fps=10',
)
parser.add_argument(
'--start',
help='start time',
type=parse_timedelta,
)
end_time_group = parser.add_mutually_exclusive_group()
end_time_group.add_argument(
'--end',
help='end time',
type=parse_timedelta,
)
end_time_group.add_argument(
'--duration',
help='maximum duration',
type=parse_timedelta,
)
return parser.parse_args()
def convert(file, args):
path, ext = os.path.splitext(file.name)
out_path = u'{}.gif'.format(path)
print u'converting {} -> {}'.format(file.name, out_path)
with tempfile.NamedTemporaryFile(suffix='.png') as palette:
ff_palette_cmd = ['ffmpeg', '-v', 'warning']
ff_convert_cmd = ['ffmpeg', '-v', 'warning']
if args.start is not None:
option = ('-ss', format_timedelta(args.start))
ff_palette_cmd.extend(option)
ff_convert_cmd.extend(option)
if args.end is not None:
option = ('-to', format_timedelta(args.end))
ff_palette_cmd.extend(option)
ff_convert_cmd.extend(option)
if args.duration is not None:
option = ('-t', format_timedelta(args.duration))
ff_palette_cmd.extend(option)
ff_convert_cmd.extend(option)
ff_palette_cmd.extend(('-i', file.name))
ff_convert_cmd.extend(('-i', file.name))
ff_palette_cmd.extend((
'-vf', '{},palettegen'.format(args.filters or ''),
'-y', palette.name,
))
ff_convert_cmd.extend((
'-i', palette.name,
'-lavfi', '{} [x]; [x][1:v] paletteuse'.format(args.filters or ''),
'-y', out_path,
))
code = subprocess.call(ff_palette_cmd)
if code == 0:
subprocess.call(ff_convert_cmd)
def main():
args = parse_args()
for file in args.files:
convert(file, args)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment