#!/bin/sh
# Generate/transcode smaller (fewer bytes) (.mp4) video file from source.
# This is intended to minimise bandwidth and data charges
# at some cost in fidelity and our CPU time.
# 
# Usage:
#     $0 [-force] inputvideofile
#
# This requires the input file to be an .mp4 under /img/a/v
# and will if successful write a lo-fi smaller file beside it
# with an added 'L' suffixed to the name.
#
# This aims to generate a more-compact version with same pixel size,
# duration, etc.
#
# This will refuse to generate output that is not smaller,
# or where the duration is significantly reduced for example.
#
# Returns on stdout the path to the new file,
# or nothing if no lower-fi file can (or need) be made.
#
# Atomically creates/replaces result file.
# Concurrency-safe (with mutex).
#
# Will not generate a new file if the target already exists
# and is newer than the source (and any rebuild flag).
# Also will not try to generate a new file if a fail flag is set.
# With -force the target is unconditionally rebuilt.
#
# Output format is always .mp4.
# avconv -i input-spin.mp4 -preset veryslow -crf 35 output.mp4 
force=false
if [ "-force" = "$1" ]; then force=true; shift; fi
inputvideofile=$1

# Working directory for input and output files.
AUTOGENFILEDIR=img/a/v
# If the rebuild flags exists and is newer than any cached result flag,
# unconditionally try to rebuild/refresh everything (carefully, under a lock).
# Else return immediately on cached positive or negative results.
# Assumes that a cached positive result is the most likely.
# Should be touched after any substantive change to image selection/generation.
REBUILDFLAG=img/a/.rebuild.flag

case $inputvideofile in
    $AUTOGENFILEDIR/*.mp4) ;;
    *) echo "ERROR: invalid source video $inputvideofile" 1>&2; exit 1;;
esac

# Temporary files.
TEMPFILEBASE=$AUTOGENFILEDIR/.tmp.$$.
# Pre-tidy!
rm -f ${TEMPFILEBASE}*
# Hook in handler to tidy up temp files on forced exit.
trap "rm -f $TEMPFILEBASE*; exit 1" 1 2 15

# CRF 1 is best; 58 is min quality.
# x264 defaults to 23, x265 to 28.
# Adjusting +5 or +6 should nominally about halve the bit rate.
# Thus CRF of 35 should be ~25% of size of default CRF 23 (delta +12).
# https://slhck.info/video/2017/02/24/crf-guide.html
CRF=35


TARGETBASENAME="`basename $inputvideofile`"
TARGETNAME=${inputvideofile}L

# If target already exists and is newer, silently leave it in place.
if [ "true" != "$force" -a -s $TARGETNAME -a $TARGETNAME -nt $inputvideofile -a ! $REBUILDFLAG -nt $TARGETNAME ]; then
    echo $TARGETNAME
    exit 0
fi

# Failure flag, if used.
FLAGSDIR=$AUTOGENFILEDIR/.flags
FAILFLAG=$FLAGSDIR/${TARGETBASENAME}L.FAIL
# If failure flag already exists and is newer, leave things as they are.
if [ "true" != "$force" -a -f $FAILFLAG -a $FAILFLAG -nt $inputvideofile -a ! $REBUILDFLAG -nt $FAILFLAG ]; then
    echo "INFO: $0: not attempting to rebuild failed $TARGETNAME" 1>&2
    exit 0
fi
# Clear fail flag since (re)build is happening.
rm -f $FAILFLAG

TEMPNAME=$TEMPFILEBASE.$TARGETBASENAME

# Convert to minimise output size (and minimise browser metadata load effort).
# Force audio to mono and not-quite-lowest-fi.
VIDOPTS="-crf $CRF"
AUDOPTS="-ac 1 -q:a 8"
# DHD20231227: libav no longer supported nor part of Homebrew.
#FFMPEG=avconv
FFMPEG=ffmpeg
# DHD20190704: "-preset veryslow" can cause Bus error on RPi.
if $FFMPEG </dev/null -loglevel error -i $inputvideofile -movflags +faststart -preset veryslow $VIDOPTS $AUDOPTS $TEMPNAME; then
    : All OK...
else
    echo "WARNING: $0: errored generating (will retry) lo-fi file from $inputvideofile." 1>&2
    rm -f $TEMPNAME
    # Recover with slightly more conservative settings.
    if $FFMPEG </dev/null -loglevel error -i $inputvideofile -preset slow $VIDOPTS $AUDOPTS $TEMPNAME; then
        : All OK...
    else
        echo "WARNING: $0: errored generating (will not retry) lo-fi file from $inputvideofile." 1>&2
        if [ ! -d "$FLAGSDIR" ]; then mkdir $FLAGSDIR; fi; touch $FAILFLAG
        exit 1
    fi
fi

if [ ! -s $TEMPNAME ]; then
    echo "WARNING: $0: failed to generate lo-fi file from $inputvideofile." 1>&2
    rm -f $TEMPNAME ${TARGETNAME}L
    if [ ! -d "$FLAGSDIR" ]; then mkdir $FLAGSDIR; fi; touch $FAILFLAG
    exit 1
fi

TARGETSIZE="`wc -c < $TEMPNAME`"
if [ "$TARGETSIZE" -ge "`wc -c < $inputvideofile`" ]; then
    #echo "INFO: $0: failed to generate smaller file from $inputvideofile." 1>&2
    rm -f $TEMPNAME ${TARGETNAME}L
    if [ ! -d "$FLAGSDIR" ]; then mkdir $FLAGSDIR; fi; touch $FAILFLAG
    exit 0
fi

DURORIG="`mediainfo --Inform='Video;%Duration%' $inputvideofile`"
DURNEW="`mediainfo --Inform='Video;%Duration%' $TEMPNAME`"
if [ "$DURORIG" != "$DURNEW" ]; then
    echo "WARNING: $0: failed to correctly generate lo-fi file (duration: new $DURNEW vs old $DURORIG) from $inputvideofile." 1>&2
    rm -f $TEMPNAME ${TARGETNAME}L
    if [ ! -d "$FLAGSDIR" ]; then mkdir $FLAGSDIR; fi; touch $FAILFLAG
    exit 1
fi

chmod -f u+w $TARGETNAME > /dev/null 2>&1
mv -f $TEMPNAME $TARGETNAME
chmod a+r ${TARGETNAME}
echo "INFO: $0: generated lo-fi video $TARGETNAME from $inputvideofile." 1>&2
echo $TARGETNAME

exit 0
