aboutsummaryrefslogtreecommitdiffstats
path: root/make-gif
diff options
context:
space:
mode:
Diffstat (limited to 'make-gif')
-rwxr-xr-xmake-gif202
1 files changed, 85 insertions, 117 deletions
diff --git a/make-gif b/make-gif
index 7aa845e..582a312 100755
--- a/make-gif
+++ b/make-gif
@@ -1,28 +1,32 @@
#!/usr/bin/env zsh
# export a clip from a video as a gif
-local callstr="$0"
-local hasgsic=$(whence gifsicle)
+local callstr=$0
+local hasgsic
+
+[[ $(whence gifsicle) ]] && hasgsic=true
print_error() {
echo -e "\e[1;31merror:\e[0m $1\n"
}
usage() {
- [[ "$1" != "" ]] && print_error $1
+ [[ -z $1 ]] || print_error $1
echo "Usage: $callstr [OPTIONS...] <infile> <outfile>"
+ echo "Create an animated gif from a video"
echo ""
- echo " description option default val"
- echo " start time -s <time> 00:00:00"
- echo " length in seconds -t <num> full length"
- echo " gif fps -f <num> 10"
- echo " gif pixel width -w <num> 480"
- echo " use subtitle track -b <int> none"
- echo " number of colours -c <int> 256"
- echo " dithering algorithm -d <str> sierra2_4a"
- echo "redraw only changed rect -r"
- [[ $hasgsic ]] && echo " optimise with gifsicle -o"
- echo " print this help -h"
+ echo -e " \e[1mdescription opt longform arg default val\e[0m"
+ echo " start time -s --start <time> 00:00:00"
+ echo " length in seconds -l --length <num> full length"
+ echo " gif fps -f --fps <num> 10"
+ echo " output pixel width -w --width <num> 480"
+ echo " use subtitles -b --sub [int] track 0, if enabled"
+ echo " (optionally specify track)"
+ echo " number of colours -c --colours <int> 256"
+ echo " dithering algorithm -d --dither <str> sierra2_4a"
+ echo "redraw only changed rectangle -r --rect"
+ [[ $hasgsic ]] && echo " optimise with gifsicle -o --optimise"
+ echo " print this help -h --help"
exit 1
}
@@ -37,7 +41,7 @@ rm_tmps() {
}
abort() {
- [[ "$1" != "" ]] && print_error $1
+ [[ -z $1 ]] || print_error $1
rm_tmps
exit 1
}
@@ -59,123 +63,85 @@ local timepat='^(([0-9][0-9]:){1,2}[0-9][0-9]|[0-9]+)(\.[0-9]+){0,1}$'
local numpat='^([1-9][0-9]*(\.[0-9]+){0,1}|0\.[0-9]*[1-9])$'
local intpat='^[1-9][0-9]*$'
local zintpat='^[0-9]+$'
-local dithpat='^(bayer[0-5]|heckbert|floyd_steinberg|sierra2|sierra2_4a)$'
-
-# used to hold '-t' if length is used
-local t=""
-
-# used to hold subtitle options, if present
-local substr=""
-
-# really annoying, but no other good way to do optional args
-if [[ $hasgsic ]]; then
- while getopts :s:t:f:w:b:c:d:rho opt; do
- case "$opt" in
- s)
- [[ ! $(echo $OPTARG | grep -oE "$timepat") ]] \
+local dithpat='^(none|bayer[0-5]|heckbert|floyd_steinberg|sierra2|sierra2_4a)$'
+
+zparseopts -D -E -M -A args \
+ s: -start:=s \
+ l: -length:=l \
+ f: -fps:=f \
+ w: -width:=w \
+ b:: -sub::=b \
+ c: -colours:=c -colors:=c \
+ d: -dither:=d \
+ r -rect=r \
+ o -optimise=o -optimize=o \
+ h -help=h
+
+local optarg
+for opt in ${(@k)args}; do
+ unset optarg
+ [[ -z $args[$opt] ]] || optarg=$args[$opt]
+ case $opt in
+ -s)
+ [[ ! $(echo $optarg | grep -oE "$timepat") ]] \
&& usage "malformed start timestamp"
- start="$OPTARG"
+ start="$optarg"
;;
- t)
- [[ ! $(echo $OPTARG | grep -oE "$numpat") ]] \
+ -l)
+ [[ ! $(echo $optarg | grep -oE "$numpat") ]] \
&& usage "length must be a positive rational number"
- length=$OPTARG
- t="-t"
+ length=(-t $optarg)
;;
- f)
- [[ ! $(echo $OPTARG | grep -oE "$numpat") ]] \
+ -f)
+ [[ ! $(echo $optarg | grep -oE "$numpat") ]] \
&& usage "fps must be a positive rational number"
- fps=$OPTARG
+ fps=$optarg
;;
- w)
- [[ ! $(echo $OPTARG | grep -oE "$intpat") ]] \
+ -w)
+ [[ ! $(echo $optarg | grep -oE "$intpat") ]] \
&& usage "width must be a positive integer"
- width=$OPTARG
+ width=$optarg
;;
- b)
- [[ ! $(echo $OPTARG | grep -oE "$zintpat") ]] \
+ -b)
+ [[ ! -z $optarg ]] && [[ ! $(echo $optarg | grep -oE "$zintpat") ]] \
&& usage "sub track index must be a non-negative integer"
- strack=$OPTARG
+ [[ -z $optarg ]] && strack=0 || start=$optarg
subs=true
;;
- c)
- [[ ! $(echo $OPTARG | grep -oE "$intpat") ]] \
- || [[ $OPTARG -gt 256 || $OPTARG -lt 2 ]] \
+ -c)
+ [[ ! $(echo $optarg | grep -oE "$intpat") ]] \
+ || [[ $optarg -gt 256 || $optarg -lt 2 ]] \
&& usage "colour count must be an integer in the range 2-256"
- colour_count=$OPTARG
+ colour_count=$optarg
;;
- d)
- [[ ! $(echo $OPTARG | grep -oE "$dithpat") ]] \
- && usage "dithering algorithm must be one of bayer<0-5>, heckbert, floyd_steinberg, sierra2, sierra2_4a"
- dithalg=$OPTARG
- [[ $(echo $OPTARG | grep -o bayer) ]] \
- && dithalg="bayer:bayer_scale=$(echo $OPTARG | grep -oE '[0-5]')"
- ;;
- o) gsic=true ;;
- r) rectmode=":diff_mode=rectangle" ;;
- h) usage ;;
- [?]) usage "unrecognised option" ;;
- esac
- done
-else
- while getopts :s:t:f:w:b:c:d:rh opt; do
- case "$opt" in
- s)
- [[ ! $(echo $OPTARG | grep -oE "$timepat") ]] \
- && usage "malformed start timestamp"
- start="$OPTARG"
- ;;
- t)
- [[ ! $(echo $OPTARG | grep -oE "$numpat") ]] \
- && usage "length must be a positive rational number"
- length=$OPTARG
- t="-t"
- ;;
- f)
- [[ ! $(echo $OPTARG | grep -oE "$numpat") ]] \
- && usage "fps must be a positive rational number"
- fps=$OPTARG
- ;;
- w)
- [[ ! $(echo $OPTARG | grep -oE "$intpat") ]] \
- && usage "width must be a positive integer"
- width=$OPTARG
+ -d)
+ [[ ! $(echo $optarg | grep -oE "$dithpat") ]] \
+ && usage "dithering algorithm must be one of none, bayer<0-5>, heckbert,\nfloyd_steinberg, sierra2, sierra2_4a"
+ dithalg=$optarg
+ [[ $(echo $optarg | grep -o bayer) ]] \
+ && dithalg="bayer:bayer_scale=$(echo $optarg | grep -oE '[0-5]')"
;;
- b)
- [[ ! $(echo $OPTARG | grep -oE "$zintpat") ]] \
- && usage "sub track index must be a non-negative integer"
- strack=$OPTARG
- subs=true
- ;;
- c)
- [[ ! $(echo $OPTARG | grep -oE "$intpat") ]] \
- || [[ $OPTARG -gt 256 || $OPTARG -lt 2 ]] \
- && usage "colour count must be an integer in the range 2-256"
- colour_count=$OPTARG
- ;;
- d)
- [[ ! $(echo $OPTARG | grep -oE "$dithpat") ]] \
- && usage "dithering algorithm must be one of bayer<0-5>, heckbert, floyd_steinberg, sierra2, sierra2_4a"
- dithalg=$OPTARG
- [[ $(echo $OPTARG | grep -o bayer) ]] \
- && dithalg="bayer:bayer_scale=$(echo $OPTARG | grep -oE '[0-5]')"
- ;;
- r) rectmode=":diff_mode=rectangle" ;;
- h) usage ;;
- [?]) usage "unrecognised option" ;;
- esac
+ -o) gsic=true; [[ -z $hasgsic ]] && usage "gifsicle program not found" ;;
+ -r) rectmode=":diff_mode=rectangle" ;;
+ -h) usage ;;
+ esac
+done
+
+# check some error conditions
+if [[ ${#@} -gt 2 ]]; then
+ for opt in $@; do
+ [[ $opt[1] == '-' ]] && usage "unrecognised option $opt"
done
+ usage "trailing file arguments detected"
fi
-shift $OPTIND-1
-# check some error conditions
-[[ ${#@} -gt 2 ]] && usage "trailing arguments detected"
+[[ ${#@} -eq 1 ]] && usage "no output file specified"
-[[ ${#@} -lt 2 ]] && usage "no output file specified"
+[[ ${#@} -eq 0 ]] && usage "no input file specified"
-[[ "${2:e}" != "gif" ]] && usage "output file must have a .gif file extension"
+[[ $1 != "%d.png" ]] && [[ ! -f $1 ]] && usage "input file not found"
-[[ "$1" != "%d.png" ]] && [[ ! -f "$1" ]] && usage "input file not found"
+[[ ${2:e} != "gif" ]] && usage "output file must have a .gif file extension"
# make links
local tmp_pref_i
@@ -186,7 +152,7 @@ while [[ -f "${tmp_pref_i}-palette.png" ]] || [[ -f "${tmp_pref_i}-in" ]]; do
tmp_pref_i="${tmp_pref_i}-1"
done
-tmp_pref="${tmp_pref_i}"
+tmp_pref=${tmp_pref_i}
ln -s "${1:a}" "${tmp_pref}-in" \
@@ -195,25 +161,26 @@ ln -s "${1:a}" "${tmp_pref}-in" \
# get output height using aspect ratio
local height
local as
-
height=-1
ffprobe -loglevel -8 -print_format json -show_streams "${tmp_pref}-in" \
| grep -m 1 display_aspect_ratio | grep -Eo '[0-9]+:[0-9]+' \
| IFS=':' read -A as
-[[ "$as" != "" ]] && let "height = ${width} * ${as[2]} / ${as[1]}"
+[[ -z $as ]] || let "height = ${width} * ${as[2]} / ${as[1]}"
+# convenience var
+local substr
[[ $subs ]] && substr="subtitles=${tmp_pref}-in:si=$strack,"
# convert
echo "pass 1..."
-ffmpeg -loglevel 16 -y -ss "$start" $t $length -i "${tmp_pref}-in" -copyts -filter_complex \
+ffmpeg -loglevel 16 -y -ss "$start" ${length[@]} -i "${tmp_pref}-in" -copyts -filter_complex \
"${substr}setsar=1/1,fps=$fps,scale=${width}:${height}:flags=lanczos,palettegen=max_colors=${colour_count}" \
${tmp_pref}-palette.png
[[ $? -ne 0 ]] && fferr=true
[[ ! $fferr ]] && echo "pass 2..." && ffmpeg -loglevel 24 \
- -ss $start $t $length \
+ -ss $start ${length[@]} \
-i "${tmp_pref}-in" -i ${tmp_pref}-palette.png -copyts -filter_complex \
"${substr}setsar=1/1,fps=$fps,scale=${width}:${height}:flags=lanczos[x];[x][1:v]paletteuse=dither=${dithalg}${rectmode}" \
"$2"
@@ -221,6 +188,7 @@ ffmpeg -loglevel 16 -y -ss "$start" $t $length -i "${tmp_pref}-in" -copyts -filt
[[ $? -ne 0 ]] && fferr=true
[[ $fferr ]] && abort
+
rm_tmps
# gifsicle