caipivara
7/3/2015 - 5:25 PM

Giffify - easily create optimised GIFs from a video

Giffify - easily create optimised GIFs from a video

#!/bin/bash

# License for any modification to the original (linked below):
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# Sebastiano Poggi and Daniele Conti wrote this file. As long as you retain 
# this notice you can do whatever you want with this stuff. If we meet some day,
# and you think this stuff is worth it, you can buy us a beer in return.

if ! which "ffmpeg" > /dev/null 2>&1; then
  echo "You need ffmpeg in your path to run giffify" >&2
  exit 1
fi

show_usage() {
  echo "Usage:"
  echo ""
  echo "giffify -i input_path.mp4 -o output_path.gif [-w width] [-y height] [-f fps=15] [-h shows this message]"
}

input_path=""
output_path=""
dw=-1
dh=-1
fps=15

while getopts ":i:o:w:y:f:h" opt; do
  case $opt in
    i )
      input_path=$OPTARG
      ;;
    o )
      output_path=$OPTARG
      ;;
    w )
      dw=$OPTARG
      ;;
    y )
      dh=$OPTARG
      ;;
    f )
      fps=$OPTARG
      ;;
    h )
      show_usage
      exit 0
      ;;
    \? )
      echo "Argument $OPTARG not recognized." >&2
      exit 1
      ;;
    : )
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

if [[ -z "$input_path" ]]; then
  echo "You must specify an input file to process"
  echo ""
  show_usage
  exit 1
fi

if [[ -z "$output_path" ]]; then
  output_path="$input_path.gif"
fi

palette="palette.png"
filters="fps=$fps,scale=$dw:$dh:flags=lanczos"

ffmpeg -v warning -i $input_path -vf "$filters,palettegen" -y $palette
if [[ -f $palette ]]; then
  ffmpeg -v warning -i $input_path -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y $output_path
  rm $palette
fi
#!/usr/bin/python

# License for any modification to the original (linked below):
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# Sebastiano Poggi and Daniele Conti wrote this file. As long as you retain 
# this notice you can do whatever you want with this stuff. If we meet some day,
# and you think this stuff is worth it, you can buy us a beer in return.

import argparse, sys, subprocess, tempfile
from os.path import splitext
from distutils.spawn import find_executable




def look_for_ffmpeg_or_abort():
	ffmpeg_path = find_executable('ffmpeg')
	if ffmpeg_path == None:
	  print "** ComputerSaysNoError **" 
	  print "You need to have ffmpeg installed on your system and on the path for Giffify to work" 
	  exit(1)

def parse_cli_arguments():
	parser = argparse.ArgumentParser(description='Processes a video into a gif.')
	parser.add_argument('video', type=str, help='The video to be processed')
	parser.add_argument('-r', '--rotate', dest='rotate', action='store_true', help='Rotate output 270 degrees clockwise (useful for Genymotion)')
	parser.add_argument('-o', '--outfile', type=str, help='Target path')
	parser.add_argument('-dw', '--desired-width', type=int, default=-1)
	parser.add_argument('-dh', '--desired-height', type=int, default=-1)
	parser.add_argument('-fps', type=int, default=15, help='Output frames per second. Default: 15 fps')
	parser.add_argument('-s', '--start-time', type=int, default=-1, help='Start timestamp, as [-][HH:]MM:SS[.m...] or [-]S+[.m...]')
	parser.add_argument('-e', '--end-time', type=int, default=-1, help='End timestamp, as [-][HH:]MM:SS[.m...] or [-]S+[.m...]. Overridden by -d')
	parser.add_argument('-d', '--duration', type=int, default=-1, help='Duration, as [-][HH:]MM:SS[.m...] or [-]S+[.m...]. Overrides -e')

	return parser.parse_args()

def gif_path(path):
  return splitext(path)[0] + '.gif'

def get_palette_path():
    try:
        palette_file = tempfile.NamedTemporaryFile()
        return palette_file.name + '.png'
    finally:
        palette_file.close()

def insert_before_output_path(args, elements):
	index = args.index('-y') 
	return args[:index] + elements + args[index:]


look_for_ffmpeg_or_abort()

args = parse_cli_arguments()

input_path = args.video
output_path = gif_path(input_path) if args.outfile is None else args.outfile

fps = args.fps

dw = args.desired_width
dh = args.desired_height

s = args.start_time
e = args.end_time
d = args.duration

if args.rotate:
	rotate_filters = "transpose=2,"
else:
    rotate_filters = ""

filters = "fps={fps},scale={dw}:{dh}:flags=lanczos".format(fps = fps, dw = dw, dh = dh)
output_filters = "{rotate}{filters} [x]; [x][1:v] paletteuse".format(filters = filters, rotate = rotate_filters)

palette_filters = "{rotate}{filters},palettegen".format(filters = filters, rotate = rotate_filters)
palette_path = get_palette_path()

ffmpeg_args_palette = ['ffmpeg', 
		'-v', 'warning', 
		'-i', input_path, 
		'-vf', palette_filters,
		'-y', palette_path]
ffmpeg_args_gif = ['ffmpeg', 
		'-v', 'warning', 
		'-i', input_path, 
		'-i', palette_path, 
		'-lavfi', output_filters,
		'-y', output_path]

if s != -1:
	ffmpeg_args_gif = insert_before_output_path(ffmpeg_args_gif, ["-ss", str(s)])

if e != -1:
	ffmpeg_args_gif = insert_before_output_path(ffmpeg_args_gif, ["-to", str(e)])

if d != -1:
	ffmpeg_args_gif = insert_before_output_path(ffmpeg_args_gif, ["-t", str(d)])

print "First pass: extracting colour palette, hang tight..."
subprocess.call(ffmpeg_args_palette)

print "Second pass: converting that nice video into a sweet, high quality gif..."
subprocess.call(ffmpeg_args_gif)

print "Done! Now go and show off to your friends and colleagues"