Skip to content

Instantly share code, notes, and snippets.

@mplewis
Last active November 10, 2017 21:58
Show Gist options
  • Save mplewis/62fb4868f92db166744f9a94ee12610d to your computer and use it in GitHub Desktop.
Save mplewis/62fb4868f92db166744f9a94ee12610d to your computer and use it in GitHub Desktop.
Turn screencasts into high-quality gifs with FFmpeg
#!/bin/bash
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
set -x
# http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html
# Default settings work great for screencasts. For video from an actual camera, set MOTION=1.
MOTION=${MOTION:-}
if [ ! -z "${MOTION}" ]; then
DITHER=sierra2_4a
STATS_MODE=diff
NO_DIFF_RECT=1
fi
FPS=${FPS:-15}
WIDTH=${WIDTH:--1}
HEIGHT=${HEIGHT:--1}
DITHER=${DITHER:-none}
STATS_MODE=${STATS_MODE:-full}
START_TIME=${START_TIME:-0}
DURATION=${DURATION:-0}
NO_DIFF_RECT=${NO_DIFF_RECT:-}
AND_DIFF_MODE=${AND_DIFF_MODE:-:diff_mode=rectangle}
if [ ! -z "${NO_DIFF_RECT}" ]; then
AND_DIFF_MODE=''
fi
filters="fps=$FPS,scale=$WIDTH:$HEIGHT:flags=lanczos"
palette="/tmp/palette.png"
ffmpeg -ss "$START_TIME" -t "$DURATION" -i "$1" -vf "$filters,palettegen=stats_mode=$STATS_MODE" -y $palette
ffmpeg -ss "$START_TIME" -t "$DURATION" -i "$1" -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse=dither=$DITHER$AND_DIFF_MODE" -y "$2"
@davec-gusto
Copy link

Line 4: There's no need to change IFS. The link that recommends this misses a more effective fix that doesn't either change behavior of downstream scripts (export IFS) or require applying it in every new context (including stuff like "$(command)"):

✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:27 $ ARR=($'element\t1' $'element\n'2); for e in ${ARR[@]}; do echo "element: $e"; done
element: element
element: 1
element: element
element: 2
✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:28 $ ARR=($'element\t1' $'element\n'2); for e in "${ARR[@]}"; do echo "element: $e"; done
element: element        1
element: element
2

Just quote it. This also misses the entire point of [@] element access:

# Never do this, if you're quoting [*] you're using it wrong:
✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:29 $ ARR=($'element\t1' $'element\n'2); for e in "${ARR[*]}"; do echo "element: $e"; done
element: element        1 element
2

# Neither way duplicates the quoted [@] behavior.
14:31 $ ARR=($'element\t1' $'element\n'2); for e in ${ARR[*]}; do echo "element: $e"; done
element: element
element: 1
element: element
element: 2

I didn't read that page super closely, but I think the author has an inflated sense of their own competence and should understand bash better before trying to create even an unofficial standard.

@davec-gusto
Copy link

Always quote when testing with -z. Otherwise anything in IFS will expand to separate words:

✘-127 ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4]
14:46 $ IFS=$'\t\n'; ARR=($'element\t1' $'element\n2' $'element3\t'); printf -- '---%s---\n' "${ARR[@]}"; for e in "${ARR[@]}"; do if [ -z $e ]; then echo 'Empty!'; else echo 'Not!'; fi; done
---element      1---
---element
2---
---element3     ---
-bash: [: element: binary operator expected
Not!
-bash: [: element: binary operator expected
Not!
Not!
✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4]
14:46 $ IFS=$'\t\n'; ARR=($'element\t1' $'element\n2' $'element3\t'); printf -- '---%s---\n' "${ARR[@]}"; for e in "${ARR[@]}"; do if [ -z "$e" ]; then echo 'Empty!'; else echo 'Not!'; fi; done
---element      1---
---element
2---
---element3     ---
Not!
Not!
Not!

Of course, my second advice is to use [[ instead, then you don't need to worry about it:

✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:48 $ IFS=$'\t\n'; ARR=($'element\t1' $'element\n2' $'element3\t'); printf -- '---%s---\n' "${ARR[@]}"; for e in "${ARR[@]}"; do if [[ -z $e ]]; then echo 'Empty!'; else echo 'Not!'; fi; done
---element      1---
---element
2---
---element3     ---
Not!
Not!
Not!

[ has a bunch of backward-compatibility cruft, it's a bash built-in that emulates /bin/[, which is just an alias for /bin/test. [[ is a bash keyword that applies more friendly parsing rules within [[/]].

@davec-gusto
Copy link

On lines 28-29, what happens if $pallette has space in it? Always quote variable expansion unless you know for sure you don't need to. In this case, you do know for sure since it's defined 1 line above, so nevermind.

Except don't! Quote your ENV=${ENV:-default} stuff.

@davec-gusto
Copy link

davec-gusto commented Nov 10, 2017

Oh yeah, re: the ARR stuff, check this out:

✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4]
14:43 $ IFS=$'\t\n'; ARR=($'element\t1' $'element\n'2); printf -- '---%q---\n' "${ARR[@]}"
---$'element\t1'---
---$'element\n2'---

%q rules!

e: Not appropriate for your use-case right now, but I really like it for displaying "File not found: $(printf '%q' "$FILE")" so people can actually see what the value passed was.

✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:58 $ printf 'File not found: %s\n' "File with a space at the end "
File not found: File with a space at the end 
✔ ~/workspace/zenpayroll [davec-employment-verification L|●1✚ 4] 
14:58 $ printf 'File not found: %q\n' "File with a space at the end "
File not found: File\ with\ a\ space\ at\ the\ end\ 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment