#!/bin/bash
# convert recording to avi compatable with San Disk Sansa Fuze
# Based on and/or inspired by myth2xvid, the video4fuze project:
# http://code.google.com/p/video4fuze
# and the fuzemux project:
# http://code.google.com/p/fuzemux
#
# version 0.2
#
# usage:
# first parameter must be %DIR%/%FILE% of the recording
# second parameter must be the %TITLE% for the output filename
# third parameter must be the %SUBTITLE% for the output filename
# fourth parameter must be %CHANID% if you set USECUTLIST=Y
# fifth parameter must be %STARTTIME% for USECUTLIST=Y and output filename
# sixth parameter must be %JOBID% for the User Job status to be updated
# in MythTV
# In the mythtv setup screen invoke this script like this:
# MYTHTV User Job Command:
# /usr/LH/bin/myth2fuze "%DIR%/%FILE%" "%TITLE%" "%SUBTITLE%" "%CHANID%" "%STARTTIME%" "%JOBID%"

# options:
USECUTLIST=Y		# Y or N

# check prerequesites
for APP in {fuzemux,mencoder,ffmpeg,mythcommflag,mythtranscode,mysql}; do
   /usr/bin/which ${APP} &>/dev/null
   ERROR=$?
   if [ $ERROR -ne 0 ]; then
      echo "${APP} not found in your PATH! Aborting! ERROR: ${ERROR}" >> ${LOGFILE}
      update_status 304
      update_comment "${APP} not found in your PATH! Aborting! Exit status: ${ERROR}"
      exit 1
   fi
done

# where the converted video is stored
OUT_DIR=/myth/video/fuze

# Ensure output directory exists
if [ ! -d ${OUT_DIR} ] ; then
  mkdir -p -m777 ${OUT_DIR}
fi

# database settings
BACKEND_HOSTNAME=${BACKEND_HOSTNAME:-"localhost"}
DBUSERNAME=${DBUSERNAME:-"mythtv"}
DBPASSWORD=${DBPASSWORD:-"mythtv"}
SQLCMD="mysql -u ${DBUSERNAME} --password=${DBPASSWORD} -h ${BACKEND_HOSTNAME} mythconverg -e"

# create temp filename so multiple instances won't conflict
TMPNAME=toFUZE-$$
TMPDIR=/myth/tmp
TMPFILE=${TMPDIR}/${TMPNAME}.avi
TMPCUTFILE=${TMPDIR}/${TMPNAME}.mpg
MENINPUTFILE=${1}
TWOPASSFILE=/tmp/${TMPNAME}-2pass.log
STATUSFILE=/tmp/${TMPNAME}-status.log
MENCODER_RETURN_CODE=/tmp/${TMPNAME}-mencoder_return_code
MENCODER_RETURN_CODE_2=/tmp/${TMPNAME}-mencoder_return_code_2

# Ensure temp directory exists
if [ ! -d ${TMPDIR} ] ; then
  mkdir -p -m777 ${TMPDIR}
fi

#------FUNCTIONS---------------
update_comment()
# Arg_1 = COMMENT
{
if [ ${NO_JOBID} = 0 ]; then
    `${SQLCMD} "update jobqueue set comment=\"${1}\" where id=\"${JOBID}\";"`
fi
}

update_status()
# Arg_1 = status code
{
if [ ${NO_JOBID} = 0 ]; then
    `${SQLCMD} "update jobqueue set status=\"${1}\" where id=\"${JOBID}\";"`
fi
}

check_background_progress()
# check mencoder progress in background
# Arg_1 = PROGRESS CALCULATION
{
while [ `tail -1 ${STATUSFILE} | grep -cE "^Audio stream:|^Video stream:"` = 0 ]
do
    sleep 10
    check_myth_jobcmds
    current_status=`tail -1 ${STATUSFILE} | grep "([ 0-9]\{1,\}%)"`
    prog_percent=`echo ${current_status} | sed 's_.*(\([ 0-9][ 0-9]\)%).*_\1_'`
    current_FPS=`echo ${current_status} | sed 's_.*\([ 0-9][ 0-9].[ 0-9][ 0-9]\)fps.*_\1_'`
    if [ -n ${prog_percent} ]; then
        prog_percent=`expr ${prog_percent} / ${1}`
        echo "${prog_percent}% Completed @ ${current_FPS} fps"
        update_comment "${prog_percent}% Completed @ ${current_FPS} fps"
    fi
    sleep 10
done
}

check_myth_jobcmds()
# check the myth database for stop pause or resume commands
{
if [ ${NO_JOBID} = 0 ]; then
    CURRENT_CMD=`${SQLCMD} "select cmds from jobqueue where id=\"${JOBID}\";" | sed '/[0-9]/!d'`
    case ${CURRENT_CMD} in
	# JOB_RUN
	0) ;;
	# JOB_PAUSE
	1) update_status 6
	   kill -s STOP ${mencoder_pid} ;;
	# JOB_RESUME
	2) update_status 4
       `${SQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"`
       kill -s CONT ${mencoder_pid} ;;
	# JOB_STOP
	4) update_status 5
	   `${SQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"`
       kill -9 ${mencoder_pid} ${command_pid}
       clean_up_files
       echo "Encode Cancelled" >> ${LOGFILE}
       update_status 320
 	   exit ;;
    esac
fi
}

get_mencoder_pid()
{
process_name=""
i1=1
while [ "$process_name" != "found" ]; do
   if [ "`ps ${mencoder_pid} | grep mencoder | sed 's_.*\(mencoder\).*_\1_'`" = "mencoder" ]; then
      process_name="found"
   else
      mencoder_pid=`expr ${mencoder_pid} + 1`
   fi
   i1=`expr $i1 + 1`
   if [ $i1 -gt 20 ]; then
      break
   fi
done
}

run_fuzemux()
# Remux avi to fuze compatable avi
{
/usr/bin/nice -n19 /usr/bin/fuzemux ${TMPFILE} ${OUTPUTFILE} >> ${LOGFILE} || return 1
}

create_thumbnail()
# Create a thumbnail image for the fuze
{
/usr/bin/nice -n19 /usr/bin/ffmpeg -y -v -1 -i ${OUTPUTFILE} -t 1 -ss 3 \
-s 224x176 -f image2 ${OUTPUTFILE%.*}.thm >> ${LOGFILE}
}

clean_up_files()
# clean up left over files
{
unlink ${TMPFILE} 2> /dev/null
unlink ${TMPCUTFILE} 2> /dev/null
unlink ${TMPCUTFILE}.map 2> /dev/null
unlink ${TWOPASSFILE} 2> /dev/null
unlink ${TWOPASSFILE}.tmp 2> /dev/null
unlink ${STATUSFILE} 2> /dev/null
unlink ${MENCODER_RETURN_CODE} 2> /dev/null
unlink ${MENCODER_RETURN_CODE_2} 2> /dev/null
}

#-------MAIN SCRIPT------------

# check if %JOBID% is passed from command line
JOBID=${6}
if [ -z ${JOBID} ]; then
    NO_JOBID=1
else
    NO_JOBID=0
fi

# log file location
LOGFILE=/var/log/mythtv/myth2fuze.log
CDate="`date`"
echo "" >> ${LOGFILE}
echo ${CDate} >> ${LOGFILE}
echo "File to encode: ${MENINPUTFILE}     Name: ${2} - ${3}" >> ${LOGFILE}

# start timer
beforetime="$(date +%s)"

check_myth_jobcmds

# check if using cutlist
if [ ${USECUTLIST} = Y ]; then
    MYTHCOMMFRAMES=$( mythcommflag --getcutlist -f ${MENINPUTFILE} | grep 'Cutlist:' | cut -d \  -f 2 )
    if [ -n "$MYTHCOMMFRAMES" ]; then
        echo "Extracting Cutlist..." >> ${LOGFILE}
        update_comment "Extracting Cutlist"
        /usr/bin/nice -n19 /usr/bin/mythtranscode --chanid ${4} --starttime ${5} --outfile ${TMPCUTFILE} --mpeg2 --honorcutlist
        MENINPUTFILE=${TMPCUTFILE}
    fi
fi

# run mencoder in background to do 1st pass conversion
echo "Encoding 1st Pass for the Fuze..." >> ${LOGFILE}
( /usr/bin/nice -n19 /usr/bin/mencoder ${MENINPUTFILE} -passlogfile \
${TWOPASSFILE} -ffourcc DX50 -ofps 20 \
-vf pp=li,expand=:::::224/176,scale=224:176,harddup -ovc lavc \
-lavcopts vcodec=mpeg4:vbitrate=683:vmax_b_frames=0:keyint=15:turbo:vpass=1 \
-nosound -o /dev/null > ${STATUSFILE} 2>&1 ; echo $? > \
${MENCODER_RETURN_CODE} ) &
mencoder_pid=$!
command_pid=${mencoder_pid}
get_mencoder_pid

check_background_progress "2"

if [ `cat ${MENCODER_RETURN_CODE}` -ne 0 ]; then
   echo "Mencoder pass #1 exited with error ${MENCODER_RETURN_CODE}.  Encoding failed!"
   update_status 304
   update_comment "ERROR: Mencoder pass #1 exited with error ${MENCODER_RETURN_CODE}"
   clean_up_files
   exit 1
fi

# run mencoder in background to do 2nd pass conversion
echo "Encoding 2nd Pass for the Fuze..." >> ${LOGFILE}
( /usr/bin/nice -n19 /usr/bin/mencoder ${MENINPUTFILE} -passlogfile \
${TWOPASSFILE} -ffourcc DX50 -ofps 20 \
-vf pp=li,expand=:::::224/176,scale=224:176,harddup -ovc lavc \
-lavcopts vcodec=mpeg4:vbitrate=683:vmax_b_frames=0:keyint=15:vpass=2 \
-srate 44100 -af resample=44100:0:1,format=s16le -oac mp3lame \
-lameopts cbr:br=128 -o ${TMPFILE} > ${STATUSFILE} 2>&1 ; echo $? \
> ${MENCODER_RETURN_CODE_2} ) &
mencoder_pid=$!
command_pid=${mencoder_pid}
get_mencoder_pid

check_background_progress "2 + 50"

if [ `cat ${MENCODER_RETURN_CODE_2}` -ne 0 ]; then
   echo "Mencoder pass #1 exited with error ${MENCODER_RETURN_CODE}.  Encoding failed!"
   update_status 304
   update_comment "ERROR: Mencoder pass #1 exited with error ${MENCODER_RETURN_CODE_2}"
   clean_up_files
   exit 1
fi

# make output filename unique, remove punctuation and "special" characters,
# replace spaces with underscores and force lowercase names
OAD=$( echo ${5} | cut -b 1-8 )
FILENAME=$( echo "${2} ${OAD} ${3}" | tr -d '[:punct:]' \
          | tr -s '[:blank:]' '[_]' | tr '[:upper:]' '[:lower:]' )_fuze.avi

OUTPUTFILE=${OUT_DIR}/${FILENAME}
ERROR=$?

if [ ${ERROR} -ne 0 ]; then
   echo "Setting output file name exited with error ${ERROR}" >> ${LOGFILE}
   update_status 304
   update_comment "ERROR: Setting output file name exited with error ${ERROR}  Job failed!"
   clean_up_files
   exit 1
fi

run_fuzemux
ERROR=$?

if [ ${ERROR} -ne 0 ]; then
   echo "Fuzemux exited with error ${ERROR}" >> ${LOGFILE}
   update_status 304
   update_comment "ERROR: Fuzemux exited with error ${ERROR}  Job Failed!"
   clean_up_files
   exit 1
fi

# ensure mythtv owns the final avi
chown mythtv ${OUTPUTFILE}
ERROR=$?

if [ ${ERROR} -ne 0 ]; then
   echo "Changing ownership of avi to mythtv exited with error ${ERROR}"
   update_status 304
   update_comment "ERROR: Changing ownership of avi to mythtv exited with error ${ERROR}  Job failed!"
   clean_up_files
   exit 1
fi

create_thumbnail

# ensure mythtv owns the thumbnail
chown mythtv ${OUTPUTFILE%.*}.thm
ERROR=$?

if [ ${ERROR} -ne 0 ]; then
   echo "Changing ownership of thumbnail to mythtv exited with error ${ERROR}"
   update_status 304
   update_comment "ERROR: Changing ownership of thumbnail to mythtv exited with error ${ERROR}  Job failed!"
   clean_up_files
   exit 1
fi

# stop timer
aftertime="$(date +%s)"
seconds="$(expr $aftertime - $beforetime)"
    echo "File Encoded Successfully: ${OUTPUTFILE}" >> ${LOGFILE}
    hours=$((seconds / 3600))
    seconds=$((seconds % 3600))
    minutes=$((seconds / 60))
    seconds=$((seconds % 60))
    echo "Encoding took $hours hour(s) $minutes minute(s) $seconds second(s) @ $current_FPS fps." >> ${LOGFILE}
    update_status 272
    update_comment "Encode Successful. Encoding Time: $hours hour(s) $minutes minute(s) $seconds second(s)"

clean_up_files