Skip to content

Instantly share code, notes, and snippets.

@ikoba
Last active May 2, 2023 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ikoba/2852944738431d82ebffe6d9e407cc32 to your computer and use it in GitHub Desktop.
Save ikoba/2852944738431d82ebffe6d9e407cc32 to your computer and use it in GitHub Desktop.
Generate a dummy video or an image on macOS
#!/usr/bin/env bash
#
# Generate a dummy video or an image.
#
# The caption text and size (width x height) are drawn on the video or image.
# The countdown timer is drawn in the upper right corner of the video.
#
# This script is intended to be run on macOS.
# And it depends on the following packages.
# - imagemagick
# - ffmpeg
# You can install these packages with Homebrew.
# `brew install imagemagick ffmpeg`
#
# To see the usage, execute `dummy -h`
#
set -euo pipefail
output_image="false"
size="600x600"
caption="Test"
font=""
default_font1="ヒラギノ角ゴシック-W3"
default_font2="Hiragino-Sans-W3"
font_size=""
foreground="white"
background="gray"
image_type="png"
audio_path=""
time="15"
out_dir="$(pwd)"
function show_usage() {
cat << EOS
usage: dummy [-i, --image | -v, --video]
[-s, --size <geometry>]
[-c, --caption <text>]
[-f, --font <font_name>]
[--list-fonts]
[--font-size]
[--foreground <color>]
[--background <color>]
[--image-type <type>]
[-a, --audio <file>]
[-t, --time <sencond>]
[-o, --out <directory>]
[-h, --help]
options:
-i, --image generate an image
-v, --video generate a video (default)
-s, --size <geometry> specify width and height (default: 600x600)
-c, --caption <text> specity caption text (default: Test)
-f, --font <name> specify font name (default: "ヒラギノ角ゴシック-W3" or "Hiragino-Sans-W3")
--list-fonts list available fonts and exit
--font-size <number> specify font point size
(default: appropriate value depending on the size)
--foreground <color> specify caption text color (default: white)
--background <color> specify background color (default: gray)
examples of color formats:
'lime', '#0f0', '#00ff00', 'rgb(0,255,0)', 'rgb(0,100%,0)'
--image-type <type> specify image type (default: png)
-a, --audio <file> specify an audio file if you want to merge it with the video
-t, --time <second> specify video length in seconds (default: 15)
-o, --out <directory> specify output directory (default: working directory)
-h, --help show usage and exit
EOS
}
function err() {
echo "$*" >&2
}
while getopts ivs:c:f:a:t:o:h-: opt; do
if [[ -n "${OPTARG+x}" ]]; then
optarg="${OPTARG}"
if [[ "${opt}" == "-" ]]; then
opt="-${OPTARG%%=*}"
optarg="${OPTARG/${OPTARG%%=*}/}"
optarg="${optarg#=}"
if [[ -z "${optarg}" ]] && [[ -n "${!OPTIND+x}" ]] && [[ "${!OPTIND}" != -* ]]; then
optarg="${!OPTIND}"
shift
fi
fi
fi
case "-${opt}" in
-i|--image)
output_image="true"
;;
-v|--video)
output_image="false"
;;
-s|--size)
size="${optarg}"
;;
-c|--caption)
caption="${optarg}"
;;
-f|--font)
font="${optarg}"
;;
--list-fonts)
convert -list font | grep 'Font:' | sed 's/^ *Font: //'
exit
;;
--font-size)
font_size="${optarg}"
;;
--foreground)
foreground="${optarg}"
;;
--background)
background="${optarg}"
;;
--image-type)
image_type="${optarg}"
;;
-a|--audio)
audio_path="${optarg}"
;;
-t|--time)
time="${optarg}"
;;
-o|--out)
out_dir=${optarg%/}
;;
-h|--help)
show_usage
exit
;;
--)
break
;;
-\?)
exit 1
;;
--*)
err "$0: illegal option -- ${opt##-}"
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ -z "${font}" ]]; then
found=$(convert -list font | grep 'Font:' | sed 's/^ *Font: //' | grep -E "${default_font1}|${default_font2}")
if [[ "${found}" == "${default_font1}" ]]; then
font="${default_font1}"
elif [[ "${found}" == "${default_font2}" ]]; then
font="${default_font2}"
else
err "Default font not found. Please specify --font option."
exit 1
fi
fi
if [[ -z "${font_size}" ]] && [[ "${size}" =~ ^([0-9]+)x([0-9]+)$ ]]; then
width="${BASH_REMATCH[1]}"
height="${BASH_REMATCH[2]}"
if (( height < width )) ; then
font_size=$((height / 10))
else
font_size=$((width / 10))
fi
fi
escaped_caption="${caption//[:./\\¥*?\"<>|]/_}"
file="${escaped_caption}_${size}"
temp_dir=$(mktemp -d)
if [[ "${output_image}" == "true" ]]; then
image_path="${out_dir}/${file}.${image_type}"
else
image_path="${temp_dir}/${file}.${image_type}"
fi
# generate an image
convert \
-size "${size}" -gravity center \
-font "${font}" -pointsize "${font_size}" \
-fill "${foreground}" -background "${background}" \
caption:"${caption}\n${size}" "${image_path}"
if [[ "${output_image}" == "true" ]]; then
echo "image generated: ${image_path}"
exit
fi
video_path="${out_dir}/${file}.mp4"
video_cmd="ffmpeg -y -loglevel error -loop 1 -i '${image_path}' \
-pix_fmt yuv420p -t ${time} -r 30 '${video_path}' \
-filter_complex \"drawtext=fontfile=/System/Library/Fonts/Menlo.ttc:text='%{eif\:(${time}*1000+999-t*1000)/1000\:d}':fontcolor=white:fontsize=30:x=w-tw-30:y=30:box=1:boxcolor=black@0.5:boxborderw=10:enable='between(t,0,${time})'\""
if [[ -n "${audio_path}" ]]; then
video_cmd+=" -stream_loop -1 -i ${audio_path}"
fi
video_cmd+=" < /dev/null"
# generate a video
eval "${video_cmd}"
echo "video generated: ${video_path}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment