Last active
February 4, 2021 21:01
-
-
Save rkfg/4463793 to your computer and use it in GitHub Desktop.
GIF animation script to cut a piece of video and produce a frame-accurate optimized GIF with a precise framerate.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
help () { | |
log "Usage: `basename $0` [-h] [-n] [-a] [-u] [-s hh:mm:ss] [-d ss] [-w px] [-f n] [-S n] [-b n] [-t subtitle.sub] [-c default | basic | fontsize | fontstyle | full] <filename> [result.gif] | |
-h show this help | |
-n turn off subtitles | |
-a don't open directory with frames in filemanager | |
-u update this script from gist. Create .gifupd file in the script's directory to automatically check for updates once per day or put the number of seconds in the file to check with that period. | |
-s start time in seconds or as hours:minutes:seconds, default: 0 | |
-d duration in seconds or as hh:mm:ss, default: 5 | |
-w resulting width in pixels (height is set according to the aspect), default: 320 | |
-f use every nth frame to get smaller file; use 2-3 for anime, default: 2 | |
-S use nth subtitles track | |
-b use n fake (dithered) bits for color, you can also use form r,g,b specifying number of levels per channel (0-255), default: 24 | |
-t add subtitles from the file | |
-c create (and replace) the subtitles file with a template of the chosen type | |
-p set FPS manually | |
<filename> input filename | |
result.gif optional output filename. If not specified, random name will be used. | |
" | |
update | |
exit 0 | |
} | |
log () { | |
if [ -z "$2" ] | |
then | |
echo "$1" >&2 | |
else | |
if [ $1 -le $DEBUG ] | |
then | |
echo "$2" >&2 | |
fi | |
fi | |
} | |
update () { | |
PERIOD=86400 | |
if [ "$1" != "do" ] | |
then | |
if [ ! -f "$UPDFILE" ] | |
then | |
log 5 "No update file found" | |
return | |
fi | |
FILEPERIOD=$( cat "$UPDFILE" ) | |
if [ -n "$FILEPERIOD" ] | |
then | |
PERIOD=$FILEPERIOD | |
fi | |
log 5 "Update file found, PERIOD=$PERIOD" | |
NOW=$( date +%s ) | |
THEN=$( stat -c %Y "$UPDFILE" ) | |
DIFF=$(( NOW - THEN )) | |
log 5 "Now: $NOW, Then: $THEN, Diff: $DIFF" | |
else | |
DIFF=86401 | |
fi | |
if [ $DIFF -ge $PERIOD ] | |
then | |
touch "$UPDFILE" | |
NEWFILE=`mktemp "/tmp/gcupdXXXXXX"` | |
trap "rm $NEWFILE 2>/dev/null" INT TERM EXIT | |
log 5 "NEWFILE: $NEWFILE" | |
wget -qO "$NEWFILE" "$UPDURL" | |
if [ $? -eq 0 -a -n "`cat $NEWFILE`" ] | |
then | |
DIFFTEXT=$( diff -u "$FULLSELFPATH" "$NEWFILE" ) | |
if [ -n "$DIFFTEXT" ] | |
then | |
log "\033[1m***New version available***\033[0m" | |
if [ "$1" = "do" ] | |
then | |
log "Diff:\n\n$DIFFTEXT" | |
read -p "Perform updating? (y/N)" UPD | |
if [ "$UPD" = "y" -o "$UPD" = "Y" ] | |
then | |
log "Updating..." | |
cp "$NEWFILE" "$FULLSELFPATH" | |
log "Done." | |
fi | |
exit 0 | |
else | |
log 5 "Update found, doing nothing." | |
fi | |
else | |
log 5 "No difference found in update" | |
fi | |
else | |
log 2 "Error downloading update." | |
fi | |
else | |
log 5 "Diff is less than $PERIOD seconds" | |
fi | |
rm $NEWFILE 2>/dev/null | |
} | |
subtitle () { | |
sed -e "s/^\s*\(.*\)$/\1/" -e "/^$/d" -e "/^;.*/d" $SUBTITLE | ( | |
FONTSIZE=20 | |
FONT=DejaVu-Sans-Bold | |
COLOR=white | |
STROKECOLOR='#000c' | |
STROKEWIDTH=2 | |
read TYPE | |
log 5 "Subtitles type is: $TYPE" | |
case $TYPE in | |
basic) | |
read FONT FONTSIZE COLOR STROKECOLOR STROKEWIDTH | |
log 5 "Set up basic subtitles defaults." | |
;; | |
fontsize) | |
read FONT COLOR STROKECOLOR STROKEWIDTH | |
log 5 "Set up font subtitles defaults." | |
;; | |
fontstyle) | |
read STROKECOLOR STROKEWIDTH | |
log 5 "Set up font subtitles defaults" | |
;; | |
esac | |
log 5 "Set up subtitles defaults. FONT: $FONT FONTSIZE: $FONTSIZE COLOR: $COLOR STROKECOLOR: $STROKECOLOR STROKEWIDTH: $STROKEWIDTH" | |
FROM= | |
for FRAME in `ls "$TEMP"` | |
do | |
FRAMENUM=${FRAME%.png} | |
log 5 "Processing frame $FRAMENUM" | |
if [ -z "$FROM" ] | |
then | |
case $TYPE in | |
basic | default) | |
log 5 "Reading subline of default or basic type" | |
read FROM TO GRAVITY GEOMETRY TEXT | |
;; | |
fontsize) | |
log 5 "Reading subline of fontsize type" | |
read FROM TO GRAVITY GEOMETRY FONTSIZE TEXT | |
;; | |
fontstyle) | |
log 5 "Reading subline of fontstyle type" | |
read FROM TO GRAVITY GEOMETRY FONT FONTSIZE COLOR TEXT | |
;; | |
full) | |
log 5 "Reading subline of full type" | |
read FROM TO GRAVITY GEOMETRY FONT FONTSIZE COLOR STROKECOLOR STROKEWIDTH TEXT | |
;; | |
esac | |
log 5 "FROM: $FROM TO: $TO GRAVITY: $GRAVITY GEOMETRY: $GEOMETRY TEXT: $TEXT" | |
fi | |
if [ -n "$FROM" ] | |
then | |
if [ $FRAMENUM -ge $FROM -a $FRAMENUM -le $TO ] | |
then | |
log 5 "Adding subtitle to $FRAMENUM" | |
mogrify -gravity $GRAVITY -pointsize $FONTSIZE -font $FONT -stroke $STROKECOLOR -strokewidth $STROKEWIDTH -annotate $GEOMETRY "$TEXT" -stroke none -fill $COLOR -annotate $GEOMETRY "$TEXT" $FRAME | |
fi | |
if [ $FRAMENUM -ge $TO ] | |
then | |
# load next line | |
FROM= | |
fi | |
fi | |
done | |
) | |
} | |
gentemplate () { | |
TEMPLATE=";This is a subtitle file. Comments start with semicolon.\n;The first non-comment line sets dynamic parameters which can be specified for each subtitle line.\n;The second non-comment line sets other (static) parameters which can't be changed elsewhere.\n;Then your actual subtitles go. Fields are separated by spaces. From/to frame numbers are actual PNG file names in the temporary directory.\n;Refer to http://www.imagemagick.org/script/command-line-options.php and look for parameters -fill, -gravity, -pointsize, -font, -stroke, -strokewidth and -annotate.\n" | |
case $SUBTITLETYPE in | |
default) | |
TEMPLATE="${TEMPLATE}default\n;Defaults are: fontsize 20, font DejaVu-Sans-Bold, color white, strokecolor '#000c', strokewidth 2\n;Fields order: from to gravity geometry text\n0 30 south 0 Hello world!\n35 50 north -50+50 Subtitles are working!" | |
;; | |
basic) | |
TEMPLATE="${TEMPLATE}basic\n;In basic type we should specify the font, its size, its color and stroke color and width\nDejaVu-Sans-Bold 25 yellow #000066aa 5\n;Fields order: from to gravity geometry text\n0 30 south 0 Hello world!\n35 50 north -50+50 Subtitles are working!" | |
;; | |
fontsize) | |
TEMPLATE="${TEMPLATE}fontsize\n;In fontsize type we should specify the font, its color and stroke color and width\nDejaVu-Sans-Bold yellow #000066aa 5\n;Fields order: from to gravity geometry fontsize text\n0 30 south 0 20 Hello world!\n35 50 north -50+50 30 Subtitles are working!" | |
;; | |
fontstyle) | |
TEMPLATE="${TEMPLATE}fontstyle\n;In fontstyle type we should only specify the stroke color and width\n#000066aa 5\n;Fields order: from to gravity geometry font fontsize color text\n0 30 south 0 DejaVu-Sans-Condensed 20 yellow Hello world!\n35 50 north -50+50 DejaVu-Serif-Italic 30 green Subtitles are working!" | |
;; | |
full) | |
TEMPLATE="${TEMPLATE}full\n;In full type we should not specify anything after the style type\n\n;Fields order: from to gravity geometry font fontsize color strokecolor strokewidth text\n0 30 south 0 DejaVu-Sans-Condensed 20 yellow #000c 2 Hello world!\n35 50 north -50+50 DejaVu-Serif-Italic 30 green blue 5 Subtitles are working!" | |
;; | |
*) | |
log "Invalid template type. Valid types are: default, basic, fontsize, fontstyle and full" | |
exit 1 | |
;; | |
esac | |
echo "$TEMPLATE" > $SUBTITLE | |
} | |
if [ -z $DEBUG ] | |
then | |
DEBUG=1 | |
fi | |
FPS= | |
UPDFILENAME=".gifupd" | |
UPDURL=https://gist.github.com/raw/4463793/gifcreate | |
FULLSELFPATH=$( readlink -f "$0" ) | |
UPDFILE=$( dirname "$FULLSELFPATH" )/$UPDFILENAME | |
STARTTIME=0 | |
DURATION=5 | |
WIDTH=320 | |
FRAMESTEP=2 | |
RUNFM=1 | |
FAKEBITS=24 | |
OUTPUT=$( mktemp -u /tmp/videoXXXXXX.gif ) | |
if [ $# -eq 0 ] | |
then | |
help | |
fi | |
OPTS= | |
while getopts hnaus:d:w:f:o:S:b:t:c:p: flag | |
do | |
case $flag in | |
h) | |
help | |
;; | |
n) | |
OPTS="-nosub -noautosub $OPTS" | |
;; | |
a) | |
RUNFM= | |
;; | |
u) | |
update "do" | |
exit 0 | |
;; | |
s) | |
STARTTIME=$OPTARG | |
;; | |
d) | |
DURATION=$OPTARG | |
;; | |
w) | |
WIDTH=$OPTARG | |
;; | |
f) | |
FRAMESTEP=$OPTARG | |
;; | |
S) | |
OPTS="-sid $OPTARG $OPTS" | |
;; | |
b) | |
FAKEBITS=$OPTARG | |
;; | |
t) | |
SUBTITLE=$OPTARG | |
;; | |
c) | |
SUBTITLETYPE=$OPTARG | |
;; | |
p) | |
FPS=$OPTARG | |
;; | |
esac | |
done | |
update & # do background update check | |
shift $(( OPTIND-1 )) | |
if [ -n "$SUBTITLETYPE" -a -n "$SUBTITLE" ] | |
then | |
gentemplate | |
exit | |
fi | |
if [ -n "$SUBTITLETYPE" ] | |
then | |
log "Specify the subtitle file with -t to create a template." | |
exit 1 | |
fi | |
if [ -z "$1" ] | |
then | |
log "No input file specified." | |
exit 1 | |
fi | |
if [ -z "$2" ] | |
then | |
log "No output file specified. The resulting GIF will be saved as $OUTPUT" | |
else | |
OUTPUT=$( readlink -f "$2" ) | |
fi | |
SUBTITLE=$( readlink -f "$SUBTITLE" ) | |
TEMP=`mktemp -d /tmp/mpgifXXXXXX` | |
trap "rm -rf \"$TEMP\"; exit" INT TERM EXIT | |
FILENAME=$( readlink -f "$1" ) | |
cd "$TEMP" | |
MPOUT=$( ffmpeg "$FILENAME" $OPTS -ss $STARTTIME -endpos $DURATION -vf scale=$WIDTH:-2,framestep=$FRAMESTEP -nosound -vo png:z=9 -speed 100 2>&1 ) | |
log 5 "Mplayer output: $MPOUT" | |
[ -z "$FPS" ] && FPS=$( echo "$MPOUT" | sed -n 's/.* \([0-9]*\)\.[0-9]* fps .*/\1/p' | head -n 1 ) | |
log 5 "FPS: $FPS" | |
DELAY=$(( 100 * FRAMESTEP / FPS )) | |
log 5 "Delay: $DELAY" | |
if [ -n "$RUNFM" ] | |
then | |
xdg-open "$TEMP" | |
read -p "Now remove any excessive frames and press Enter to combine them to a nice gif." null | |
fi | |
if [ -n "$SUBTITLE" ] | |
then | |
log "Adding subtitles..." | |
subtitle | |
fi | |
log "Rendering the final gif..." | |
convert -delay $DELAY "$TEMP/*.png" -ordered-dither o8x8,$FAKEBITS -layers optimize-transparency "$OUTPUT" |
>autoupdating
You're do it wrong.
That's ok though autoupdating was implemented a little too late. I dunno what to add to this. Anyway, new features always appear when you're using the tool to its max.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Now publish it on Steam and make truckload of money.