From 858b19d7635e8228315daa2f2808b28f80fac67b Mon Sep 17 00:00:00 2001 From: Michael Hanson Date: Tue, 10 Jul 2012 21:12:52 +0000 Subject: linhes-scripts: Add myth2mkv to convert recordings to x264/mkv. --- abs/core/linhes-scripts/PKGBUILD | 9 +- abs/core/linhes-scripts/myth2mkv | 425 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+), 3 deletions(-) create mode 100644 abs/core/linhes-scripts/myth2mkv diff --git a/abs/core/linhes-scripts/PKGBUILD b/abs/core/linhes-scripts/PKGBUILD index aed756e..829694b 100644 --- a/abs/core/linhes-scripts/PKGBUILD +++ b/abs/core/linhes-scripts/PKGBUILD @@ -3,11 +3,12 @@ pkgname=linhes-scripts pkgver=7 -pkgrel=40 +pkgrel=41 pkgdesc="Various scripts that help to make LinHES, LinHES." arch=('i686' 'x86_64') license=('GPL2') -depends=('xosd' 'cpulimit' 'screen' 'mencoder' 'tablet-encode' 'mplayer' 'normalize') +depends=('xosd' 'cpulimit' 'screen' 'mencoder' 'tablet-encode' 'mplayer' + 'normalize' 'handbrake-cli' 'mkvtoolnix') url="http://linhes.org/" install="linhes-scripts.install" source=( @@ -49,6 +50,7 @@ upgrade_linhes_script.sh find_orphans.py acl_fix_fstab.py stop_xss.sh +myth2mkv ) build() { @@ -101,4 +103,5 @@ md5sums=('f56985b2d602e11dc1e10d3e7848b2a5' 'f454faeabfa153b10389a9a3bfd51c4a' 'd8838461af9d446a1fd7e7883fdc75d1' 'cc9cdabcdfc969c2829b58c0e513488c' - '54a478660d0f8150fe10112a9ecf2740') + '54a478660d0f8150fe10112a9ecf2740' + '980423e07c8c6ffd0ea1bf9a1959fdc2') diff --git a/abs/core/linhes-scripts/myth2mkv b/abs/core/linhes-scripts/myth2mkv new file mode 100644 index 0000000..5035748 --- /dev/null +++ b/abs/core/linhes-scripts/myth2mkv @@ -0,0 +1,425 @@ +#!/bin/bash +# +# Convert video to AVC-1 / h264 +# +# version 0.25-001 +# +# Prerequisites: +# - mythtv >= 0.25 +# - handbrake-cli +# - mplayer +# - mkvtoolnix +# +# Arguments +# $1 must be the directory/file of the recording +# $2 must be chanid +# $3 must be starttime +# $4 must be title +# $5 must be subtitle +# $6 must be jobid +# $7 must be quality of encode +# +# As a MythTV user job: +# myth2mkv "%DIR%/%FILE%" "%CHANID%" "%STARTTIME%" "%TITLE%" "%SUBTITLE%" "%JOBID%" HQ|MQ|LQ + +######################## +# # +# Adjustable variables # +# # +######################## + +OUTDIR=/myth/video +LOGPATH=/var/log/mythtv +LOGFILE=${LOGPATH}/myth2mkv-$$.log + +# TMPDIR is for large transient files +TMPDIR=/myth/tmp + +# x264 tuning: +# Tune x264 based on content. Valid options for TUNING are: +# film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency +# Separate multiple options with a comma. DEFAULT: none +TUNING="" + +# Custom cropping. Useful if you have a 4:3 image in a HD frame or if +# HandBrake's autocrop smarts fail you. +# Crop 240 pixels off the left and right for 4:3 image in 1920x1080 frame +# Crop 160 pixels off the left and right for 4:3 image in 1280x720 frame +# +# i.e. 0:0:240:240 +# Default: In HQ: CROP="0:0:0:0" (no cropping). +# IN MQ and LQ: autocrop. +CROP="" + +# Force custom output resolution. +# Default: Keep same resolution as input file (less any cropping). +WIDTH="" +HEIGHT="" + +# Force use/non-use of deinterlacing filter. Y|N|G (Yes, No, Guess) +# Default: G - Guess based on source resolution. +# If the source video width is 1920, 1440, 852, 704, 640 or 528 pixels +# "G" will deinterlace the video. Change to "Y" to force use of deinterlacing +# filter or to "N" to NOT use deinterlace filter no matter the resolution. +DEINT="G" + +############################ +# # +# End adjustable variables # +# # +############################ + +if [[ -e $HOME/.mythtv/mysql.txt ]] ; then + . $HOME/.mythtv/mysql.txt +else + DBHostName=${DBHostName:-"localhost"} + DBUserName=${DBUserName:-"mythtv"} + DBPassword=${DBPassword:-"mythtv"} + DBName=${DBName:-"mythconverg"} +fi + +if [[ ! -d ${TMPDIR} ]] ; then + mkdir -p ${TMPDIR} +fi + +if [[ ! -d ${OUTDIR} ]] ; then + mkdir -p ${OUTDIR} +fi + +#------FUNCTIONS--------------- +update_comment() +# Arg_1 = COMMENT +{ +if [ ${NO_JOBID} -eq 0 ]; then + SQL_CMD="update jobqueue set comment=\"${1}\" where id=\"${JOBID}\";" + `${MYSQLCMD} "${SQL_CMD}"` +fi +} + +check_background_progress() +# check handbrake progress in background +{ +while [ `tail -2 ${STATUSFILE} | grep -c "^HandBrake has exited"` = 0 ] +do + sleep 10 + check_myth_jobcmds + pass=`tail -1 ${STATUSFILE} | egrep -o -e 'task [0-9]' | tail -1 | sed 's/task\ //g'` + prog_percent=`tail -1 ${STATUSFILE} | egrep -o -e '[0-9]*\.[0-9]. %' | tail -1 | sed 's/\ %//g'` + current_FPS=`tail -1 ${STATUSFILE} | egrep -o -e 'avg [0-9.]*\.[0-9]* fps' | tail -1 | sed -e 's/avg\ //g' -e 's/\ fps//g'` + current_ETA=`tail -1 ${STATUSFILE} | egrep -o -e 'ETA [0-9.][0-9.]h[0-9.][0-9.]m[0-9.][0-9.]s' | tail -1` + if [ -n "$prog_percent" ]; then + echo "Pass ${pass}, ${prog_percent}% @ ${current_FPS} fps. ${current_ETA}" + update_comment "Pass ${pass} of 2, ${prog_percent}% @ ${current_FPS} fps. ${current_ETA}" + fi + sleep 10 +done +} + +check_myth_jobcmds() +# check the myth database for stop pause or resume commands +{ +if [[ ${NO_JOBID} -eq 0 ]] ; then + CURRENT_CMD=`${MYSQLCMD} "select cmds from jobqueue where id = \"${JOBID}\";"` + case "${CURRENT_CMD}" in + # JOB_RUN + 0) ;; + # JOB_PAUSE + 1) `${MYSQLCMD} "update jobqueue set status=\"6\" where id=\"${JOBID}\";"` + kill -s STOP ${handbrake_pid} ;; + # JOB_RESUME + 2) `${MYSQLCMD} "update jobqueue set status=\"4\" where id=\"${JOBID}\";"` + `${MYSQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"` + kill -s CONT ${handbrake_pid} ;; + # JOB_STOP + 4) `${MYSQLCMD} "update jobqueue set status=\"5\" where id=\"${JOBID}\";"` + `${MYSQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"` + kill -9 ${handbrake_pid} ${command_pid} + clean_up_files + echo "Encode Cancelled" >> ${LOGFILE} + `${MYSQLCMD} "update jobqueue set status=\"320\" where id=\"${JOBID}\";"` + exit ;; + esac +fi +} + +get_info_for_hb() { +# Collect some info about source file + +/usr/bin/mplayer -nolirc -identify -frames 0 "${HBINPUTFILE}" \ + 2>/dev/null 1>"${IDFILE}" + +VIDEOW=$( grep ID_VIDEO_WIDTH= "${IDFILE}" | awk -F= '{ print $NF }' ) +FPS=$( grep ID_VIDEO_FPS= "${IDFILE}" | awk -F= '{ print $NF }' ) + +# HandBrake does not like a framerate of 29.970, so let's drop the 0 +if [[ ${FPS} = "29.970" ]] ; then + FPS="29.97" +fi + +# A rough guestimation that if the video width is 1920, 1440, 852, 704, 640 or +# 528 pixels it is probably interlaced. +if [[ ${DEINT} = Y ]] ; then + DEINT="-d slow" +else + if [[ ${DEINT} = N ]] ; then + DEINT="" + else + if [[ ${VIDEOW} = 1920 || ${VIDEOW} = 1440 || ${VIDEOW} = 852 || \ + ${VIDEOW} = 704 || ${VIDEOW} = 640 || ${VIDEOW} = 528 ]] ; then + DEINT="-d slow" + else + DEINT="" + fi + fi +fi + +if [[ -n ${DEINT} ]] ; then + if [[ ${QUALITY} = LQ ]] ; then + DEINT="-d fast" + fi +fi + +if [[ -n ${TUNING} ]] ; then + TUNING="--x264-tune ${TUNING}" +fi + +if [[ -n ${CROP} ]] ; then + CROP="--crop ${CROP}" +fi + +if [[ -n ${WIDTH} ]] ; then + WIDTH="-w ${WIDTH} -X ${WIDTH}" +fi + +if [[ -n ${HEIGHT} ]] ; then + HEIGHT="-l ${HEIGHT} -Y ${HEIGHT}" +fi + +if [[ ${QUALITY} = HQ ]] ; then + if [[ -n ${CROP} ]] ; then + CROP="--crop ${CROP}" + else + CROP="--crop 0:0:0:0" + fi + HB_OPTS="-o ${TMPFILE} -f mkv -m -e x264 ${TUNING} -x b-adapt=2:rc-lookahead=50 -b 5000 -2 -T ${WIDTH} ${HEIGHT} -r ${FPS} --cfr ${CROP} ${DEINT} -a 1 -E copy -s 1" +else + if [[ ${QUALITY} = LQ ]] ; then + HB_OPTS="-o ${TMPFILE} -f mkv -m -e x264 ${TUNING} -b 1250 ${WIDTH} ${HEIGHT} -r ${FPS} --pfr ${CROP} ${DEINT} -a 1 -E lame -B 128 -Q 8 -6 stereo -s 1" + else + # Fallback to "MQ" + HB_OPTS="-o ${TMPFILE} -f mkv -m -e x264 ${TUNING} -b 2500 -2 -T ${WIDTH} ${HEIGHT} -r ${FPS} --pfr ${CROP} ${DEINT} -a 1 -E lame -B 256 -Q 3 -6 stereo -s 1" + fi +fi +} + +get_handbrake_pid() +{ +process_name="" +i1=1 +while [ "${process_name}" != "found" ]; do + handbrake_pid=`expr ${handbrake_pid} + 1` + i1=`expr ${i1} + 1` + if [ "`ps ${handbrake_pid} | grep HandBrakeCLI | sed 's_.*\(HandBrakeCLI\).*_\1_'`" = "HandBrakeCLI" ]; then + process_name="found" + fi + if [ ${i1} -gt 20 ]; then + break + fi +done +} + +tag_file() { +DATE=`date` + +# Create a tag file here +echo "" > "${TAG_FILE}" +echo "" >> "${TAG_FILE}" +echo "" >> "${TAG_FILE}" +echo "" >> "${TAG_FILE}" +echo "" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " TITLE" >> "${TAG_FILE}" +echo " ${TITLE}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " SUBTITLE" >> "${TAG_FILE}" +echo " ${SUBTITLE}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " SUMMARY" >> "${TAG_FILE}" +echo " ${DESCR}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " DATE_RELEASED" >> "${TAG_FILE}" +echo " ${OAD}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " ENCODER" >> "${TAG_FILE}" +echo " HandBrakeCLI ${HBCLIVER}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " DATE_TAGGED" >> "${TAG_FILE}" +echo " ${DATE}" >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo " " >> "${TAG_FILE}" +echo "" >> "${TAG_FILE}" + +# Add tag info into MKV file +echo "Adding tag info to ${TITLE} - ${SUBTITLE} ..." >> ${LOGFILE} + +/usr/bin/mkvpropedit -r ${LOGFILE} -t all:"${TAG_FILE}" "${TMPFILE}" +} + +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 ${STATUSFILE} 2> /dev/null +unlink ${IDFILE} 2> /dev/null +unlink ${HB_RETURN_CODE} 2> /dev/null +unlink ${TAG_FILE} 2> /dev/null +} + +#-------MAIN SCRIPT------------ + +# create temp filename so multiple instances won't conflict +TMPNAME=toX264-$$ +TMPFILE=${TMPDIR}/${TMPNAME}.mkv +TMPCUTFILE=${TMPDIR}/${TMPNAME}.mpg +HBINPUTFILE="${1}" +TITLE="${4}" +SUBTITLE="${5}" +JOBID="${6}" +QUALITY="${7}" +BASE=`basename ${HBINPUTFILE}` +HBCLIVER=`pacman -Q | grep handbrake-cli | awk '{ print $NF }' | awk -F"-" '{ print $1 }'` +STATUSFILE=/tmp/${TMPNAME}-status.log +HB_RETURN_CODE=/tmp/${TMPNAME}-hb_return_code +IDFILE=/tmp/${TMPNAME}-id.txt +TAG_FILE=/tmp/${TMPNAME}.xml +MYSQLCMD="mysql -B --skip-column-names -u ${DBUserName} -p${DBPassword} -h ${DBHostName} -D ${DBName} -e" +OAD=`${MYSQLCMD} "select originalairdate from recorded where basename LIKE '${BASE}';"` +DESCR=`${MYSQLCMD} "select description from recorded where basename LIKE '${BASE}';" | sed 's/\&/and/g'` +USER=`whoami` + +# check if %JOBID% is passed from command line +if [ -z ${JOBID} ]; then + NO_JOBID=1 +else + NO_JOBID=0 +fi + +# log file location +CDate="`date`" +echo "" >> ${LOGFILE} +echo $CDate >> ${LOGFILE} +echo "File to encode: ${HBINPUTFILE}" >> ${LOGFILE} +echo " --> Name: ${TITLE} - ${SUBTITLE}" >> ${LOGFILE} +echo " --> Temporary Files: ${TMPNAME}.*" >> ${LOGFILE} +echo "" >> ${LOGFILE} + +get_info_for_hb +ERROR=$? + +if [[ ${ERROR} != 0 ]] ; then + echo "Error parsing source file information!" >> ${LOGFILE} + cat ${IDFILE} >> ${LOGFILE} + clean_up_files + exit 1 +fi + +# start timer +beforetime="$(date +%s)" + +check_myth_jobcmds + +# If there is a cutlist, use it: +if [[ -n `mythutil --getcutlist --chanid "${2}" --starttime "${3}" | grep \ + Cutlist: | awk -F": " '{ print $NF }'` ]] ; then + echo "Applying cutlist for ${TITLE} - ${SUBTITLE} ..." >> ${LOGFILE} + mythtranscode --chanid "${2}" --starttime "${3}" -m --honorcutlist \ + -q --loglevel info --logpath "${LOGPATH}" -o "${TMPCUTFILE}" + mythtrans_pid=$! + ERROR=$? + HBINPUTFILE=${TMPCUTFILE} +fi + +if [[ ${ERROR} != 0 ]] ; then + echo "MythTranscode error!" >> ${LOGFILE} + echo "Check ${LOGPATH}/mythtranscode.date.${mythtrans_pid}.log for mythtranscode error" >> ${LOGFILE} + clean_up_files + exit 1 +fi + +# run handbrake in background to do conversion +echo "Encoding ${TITLE} - ${SUBTITLE} ..." >> ${LOGFILE} + +( /usr/bin/nice -n19 nohup /usr/bin/HandBrakeCLI -i ${HBINPUTFILE} ${HB_OPTS} \ + > ${STATUSFILE} 2>&1 ; echo $? > ${HB_RETURN_CODE} ) & +handbrake_pid=$! +command_pid=${handbrake_pid} +get_handbrake_pid + +check_background_progress + +if [[ `cat ${HB_RETURN_CODE}` != 0 ]] ; then + echo "HandBrakeCLI error!" >> ${LOGFILE} + cat ${STATUSFILE} >> ${LOGFILE} + clean_up_files + exit 1 +fi + +tag_file +ERROR=$? + +if [[ ${ERROR} != 0 ]] ; then + echo "Error creating tag file!" >> ${LOGFILE} + cat ${TAG_FILE} >> ${LOGFILE} + clean_up_files + exit 1 +fi + +# make output filename unique and do not clobber an existing file +# Build a final file name +FILE=$( echo "${TITLE,,} ${OAD} ${SUBTITLE,,}" | tr -d [:punct:] | tr [:blank:] "_" | tr -s "_" ) +OUTPUTFILE="${OUTDIR}/${FILE}.mkv" +i=1 +while [ -e "${OUTPUTFILE}" ] +do + OUTPUTFILE="${OUTDIR}/${FILE}-${i}.mkv" + i=`expr $i + 1` +done + +# move temp file to output location +chown -v "${USER}" "${TMPFILE}" >> ${LOGFILE} +mv -v "${TMPFILE}" "$OUTPUTFILE" >> ${LOGFILE} +ERROR=$? + +if [[ ${ERROR} != 0 ]] ; then + echo "Error moving ${TMPFILE} to ${OUTPUTFILE} !" >> ${LOGFILE} + clean_up_files + exit 1 +fi + +# stop timer +aftertime="$(date +%s)" +seconds="$(expr ${aftertime} - ${beforetime})" + +if [ ${ERROR} -eq 0 ]; then + 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} + `${MYSQLCMD} "update jobqueue set status = \"272\" where id = \"${JOBID}\";"` + update_comment "Encode Successful. Encoding Time: ${hours} hour\(s\) ${minutes} minute\(s\) ${seconds} second\(s\)" +else + echo "ERROR: ${ERROR}" >> ${LOGFILE} +fi + +# Clean up +clean_up_files -- cgit v0.12