#!/bin/sh

#===============================================================================
# extract-frame
# extract a frame from a video as a png or jpg
#===============================================================================

# dependencies:
# ffmpeg

#===============================================================================
# script usage
#===============================================================================

usage()
{
# if argument passed to function echo it
[ -z "${1}" ] || echo "! ${1}"
# display help
echo "\
# extract a frame from a video as a png or jpg
https://trac.ffmpeg.org/wiki/Seeking

$(basename "$0") -i input.(mp4|mov|mkv|m4v|webm) -s 00:00:00.000 -t (png|jpg) -x width -y height -o output.(png|jpg)
-i input.(mp4|mov|mkv|m4v)
-s 00:00:00.000     : optional argument # if option not provided defaults to 00:00:00
-t (png|jpg)        : optional argument # if option not provided defaults to png
-x width            : optional argument # 
-y height           : optional argument # 
-o output.(png|jpg) : optional argument # if option not provided defaults to input-name-timecode"
exit 2
}


#===============================================================================
# error messages
#===============================================================================

INVALID_OPT_ERR='Invalid option:'
REQ_ARG_ERR='requires an argument'
WRONG_ARGS_ERR='wrong number of arguments passed to script'


#===============================================================================
# check the number of arguments passed to the script
#===============================================================================

[ $# -gt 0 ] || usage "${WRONG_ARGS_ERR}"


#===============================================================================
# getopts check the options passed to the script
#===============================================================================

while getopts ':i:s:t:x:y:o:h' opt
do
  case ${opt} in
     i) infile="${OPTARG}";;
     s) seconds="${OPTARG}";;
     t) image="${OPTARG}";;
     x) width="${OPTARG}";;
     y) height="${OPTARG}";;
     o) outfile="${OPTARG}";;
     h) usage;;
     \?) usage "${INVALID_OPT_ERR} ${OPTARG}" 1>&2;;
     :) usage "${INVALID_OPT_ERR} ${OPTARG} ${REQ_ARG_ERR}" 1>&2;;
  esac
done
shift $((OPTIND-1))


#===============================================================================
# variables
#===============================================================================

# input, input name
infile_nopath="${infile##*/}"
infile_name="${infile_nopath%.*}"

# seconds default
seconds_default='00:00:00'

# image default
image_default="png"

# output file recording destination
outfile_default="${infile_name}-[${seconds:=${seconds_default}}].${image:=${image_default}}"


#===============================================================================
# functions
#===============================================================================

# image to video function
extract () {
  ffmpeg \
  -hide_banner \
  -stats -v panic \
  -ss "${seconds:=${seconds_default}}" \
  -i "${infile}" \
  -q:v 2 -f image2 \
  -vframes 1 \
  "${outfile:=${outfile_default}}"
}

# image to video with scale function
extract_scale () {
  ffmpeg \
  -hide_banner \
  -stats -v panic \
  -ss "${seconds:=${seconds_default}}" \
  -i "${infile}" \
  -q:v 2 -f image2 \
  -vframes 1 \
  -vf scale="${width:=-1}:${height:=-1}" \
  "${outfile:=${outfile_default}}"
}


#===============================================================================
# run function
#===============================================================================

if [ -n "${width}" ] || [ -n "${height}" ]; then
  extract_scale "${infile}"
else
  extract "${infile}"
fi