MythBrake


Overview


What is MythBrake?

MythBrake is a Bash-script that is designed to allow automated recoding of recorded shows in MythTV.

MythBrake is based on the really cool script mythnuv2mkv mythnuv2mkv by Auric.

I originally intended to use this, but there where two major drawbacks.
  1. For reasons, that have to do with the way the stream is encoded (and I didn't fully understand yet), mythtranscode can't work with the BBC-HD streams. This is why the transcoding of HD programmes fails.
  2. Encoding HD-Streams with ffmpeg and mencoder takes ages, even on my 4-core intel machine. Since I don't have NVIDIA Hardware, CUDA doesn't work for me either.

A working solution for both problems is HandBrake. It's both fast, even without CUDA Hardware, and it works flawless with the BBC-HD streams.

What it does

The script can be called from the MythWeb-Interface or from the command line. Once started, it uses HandBrake to transcode your recordings into Matroska, AVI or OGG Theora files. The new files will be stored in a subdirectory of your default [http://www.mythtv.org/wiki/Mythvideo MythVideo]] storage.
If you have recorded a TV-Series, there will be a subdirectory Season<n> and the files will be named S<n>E<e>EpisodeTitle, where <n> and <e> are the season and episode number.

For example the Doctor Who Episode The Eleventh Hour will be stored in

./Doctor Who (2005)/Season5/S05E01TheEleventhHour.mkv


as it is the first episode of season 5 of Doctor Who (2005) (which is the new Doctor Who series, opposed to the classic series).

The script can also use the cut-list from mythtv, also there are some serious restrictions still.

Prerequisites

You need some programs installed on your system to make the script work:

Usage

There are two ways to use the mythbrake script. On the command line or as a job from the mythtv interface.
To be able to set up the script in the mythtv interface, you need to look at the commandline interface first.

Preparing your recordings

If you want to convert a previously recorded show, sometimes you might want to edit the stream, e.g. to cut the beginning and the end.
I usually let recording start 5-10 minutes early, and give it an addional 5-10 Minutes, in case the program doesn't start exactly on time. When I wish to keep recordings, I usually cut away those extra minutes.

You can use the edit functions from MythTV to prepare a cut-list.
Caution: At this point comes one of the major drawbacks! Since HandBrake cannot cut the video in between (you can only give it a start position and an end position), the script takes the end of the first cut-point, and the beginning of the last, and transcodes everything in between! The only exception occurs, if there is only one cut-point. In that case, the video is transcoded from the beginning to the start of the cut-point.
This may seem useless, but it was easy to implement, and I record 99% of my shows from the BBC, so I usually have no need to cut out commercials.
If you implement a solution for this, please contact me.

The Command Line Interface

If you wish to use the command line interface, you need to find out how to access the show, you want to transcode. MythTV stores the recordings by channel-id and start time. If you use MythTV's job interface, these values are set automatically for you, but on the command line, you will most likely not know them by heart.
To find them out, you can use this command:
mythbrake.sh --findtitle "Name"


mythbrake will list all shows beginning with Name, that have been recorded. The result may look something like this:
title		subtitle		chanid		date_format(starttime, '%Y%m%d%H%i%s')		storagegroup
Doctor Who		Let's Kill Hitler	7941	20110827200500	Default
Doctor Who		Night Terrors	7941	20110903195500	Default
Doctor Who		The Girl Who Waited	7941	20110910201000	Default
Doctor Who		The God Complex	7941	20110917200500	Default
Doctor Who		Closing Time	7941	20110924200500	Default
Doctor Who		The Wedding of River Song	7941	20111001200000	Default
Doctor Who		The Doctor, the Widow and the Wardrobe	7941	20111225195500	LiveTV

The third column is the channel id, and the fourth column is the start time. They can be passed to the script exactly as they where.
Armed with this information, we can begin transcoding. The following example, transcodes the episode Closing Time:

mythbrake.sh --chanid 7941 --starttime 20110924200500 --format mkv --passthrough --quality 22 --height 1080 /home/user/MyVideos

This will transcode the episode into a MKV file in full HD, preserving the original AC3-Sound, if there is one. It will create an additional AAC track in stereo, for those poor fellows without a dolby surround system.

Parameters

The command line interface offers a lot of parameters to tweak the output.
--jobid %JOBID%
-j
	Add this when run as a mythtv user job. Enables update status in the System Status Job Queue screen and the Job Queues Comment dield in MythWeb. Also enables stop/pause/resume of jobs.
--format avi|mp4|mkv|ogm
-f
	Output format for HandBrake
	avi - Video mpeg4 Audio aac
	mp4 - Video h.264 Audio aac
	mkv - Video h.264 Audio aac in Matroska container (default)
	ogm - Video ogg-theora audio ogg-vorbis
--passthrough
-p
	For use with sources that have ac3 sound. Uses the copy parameter on handbrake to preserve the sound.
--quality n
-q
	Set the qualtiy of the output
	The script only supports constant quality, meaning it only needs to do 1-pass encoding.
	n is a value between 0 and 51, where 0 means highest quality (but big file sizes) and 51 is lowest, but with high compression
	Use something around 20 for SD and 22-24 for HD films.
--height n
-h
	The height of the output in pixels. The width is calculated, preserving the aspect ratio of the source
	If --height is not given, the size of the video will be preserved from the source
--chanid chanid
-c
	The channel id given for the desired recording
--starttime starttime
-s
	The starttime given for the desired recording
--findtitle
-t
	Returns a list of show, including chanid and starttime, if you only now the shows name
--episode
-e
   Sets the metadata for season and episode in the MythTv database. Needs --chanid and --starttime to work
	
basedirectory
	The directory to save the transcoded files to. Usually that would be your video folder.


Setting up MythTV Jobs

MythTV allows for creation of jobs, that can be started either from the MythTV frontend, or from MythWeb. One of the things you can do with them, is schedule the transcoding of your recordings.
To see how to setup a user job, see the MythTV Wiki.

I usually use MythWeb for this task, because it's more comfortable to setup. To set up user jobs in mythweb, go to the Settings page, select MythTV in the menu on the left and activate the Settings-tab. When you scoll down, you will find the fields User Job 1, User Job 2, User Job 3 and User Job 4. Here you can put in your calls to the script.
For a high quality transcode with sound passthrough, it may look like this:

mythbrake.sh --jobid %JOBID% --passthrough --quality 22 --chanid %CHANID% --starttime %STARTTIME% /var/lib/mythtv/videos


You can populate all 4 fields, with different calls to mythbrake.sh if you like, e.g. without sound passthrough or a fixed resolution.

To give the jobs proper names, so you can find them in the menus, fill the fields UserJobDesc1-4 with a matching description. Then click on Save

That's it, your mythtv is now set up to transcode.

Season and Episode numbers

Some stations do not broadcast the season and episode numbers for their shows, when airing. In this case an episode will always be marked S00E00 in the file name and meta-data. Since this is inconvenient, you can use the command line interface to set the season and episode manually. Just type
mythbrake --chanid <id> --starttime <starttime> --episode S<n>E<m>

and replace <id> with the channel id, <starttime> with the actual starttime, <n> with the season id you wish to set and <e> with the episode number. This will update the mythtv database.

Code

You can, of course, download the script here. Feel free to modify and adapt it to your needs. If you have added a cool new feature, found a bug etc. notify me in the comments.

mythbrake.sh
#!/bin/bash
# @(#)$Header: ./mythbrake/mythbrake.sh, v 0.1.0
# Created On: Sat 17 Sep 2011 07:57:25 PM CEST
# Last Modified: Sun 16 Oct 2011 06:20:23 PM CEST
# Jali <jali@orca-central.de> 2011/09/17 http://www.evildaystar.de
# Original script by Auric:
# Auric 2007/07/10 http://web.aanet.com.au/auric/
#####################################################################
# Convert your mythtv recordings into mkv using handbrake
#####################################################################

# Variables, you might want to set.

# The default aspect ratio for your recordings
readonly DEFAULTMPEGASPECT="NA" # 4:3 or 16:9

# The temporary directory to perform tasks in
readonly LOGBASEDIR="/var/tmp/"

# delete the source file after the conversion was complete
DELETEREC="OFF" # ON | OFF

# Defaut setting for quality
QUALITY="20" # Default setting is medium quality, 20 with constant quality

# the default format
FORMAT=mkv

# Turn passthrough off
PASSTHROUGH="OFF"

# Usage message
USAGE='mythbrake.sh [--jobid |-j %JOBID%] [--format -f avi|mp4|mkv|ogm] [--passthrough|-p] [--quality |-q n] [--height |-h n] --chanid | -c chanid --starttime | -s starttime --findtitle searchtitle basedirectory
Converts a recording into an output file using handbrake to encode

Options:
--jobid %JOBID%
-j
    Add this when run as a mythtv user job. Enables update status in the System Status Job Queue screen and the Job Queues Comment dield in MythWeb. Also enables stop/pause/resume of jobs.
--format avi|mp4|mkv|ogm
-f
    Output format for HandBrake
    avi - Video mpeg4 Audio aac
    mp4 - Video h.264 Audio aac
    mkv - Video h.264 Audio aac in Matroska container (default)
    ogm - Video ogg-theora audio ogg-vorbis
--passthrough
-p
    For use with sources that have ac3 sound. Uses the copy parameter on handbrake to preserve the sound.
--quality n
-q
    Set the qualtiy of the output
    The script only supports constant quality, meaning it only needs to do 1-pass encoding.
    n is a value between 0 and 51, where 0 means highest quality (but big file sizes) and 51 is lowest, but with high compression
    Use something around 20 for SD and 22-24 for HD films.
--height n
-h
    The width of the output in pixels. The height is calculated, preserving the aspect ratio of the source
    If --width is not given, the size of the video will be preserved from the source
--chanid chanid
-c
    The channel id given for the desired recording
--starttime starttime
-s
    The starttime given for the desired recording
--findtitle
-t
    Returns a list of show, including chanid and starttime, if you only now the shows name
   
basedirectory
    The directory to save the transcoded files to. Usually that would be your video folder.'

   
###########################################################
# ON or OFF
# debug mode
DEBUG="OFF"
DEBUGSQL="OFF"
DEBUGSG="OFF"
# Print INFO messages
INFO="ON"

#####################################################################
# Functions
#####################################################################

scriptlog() {
local LEVEL="$1"
shift
local PRIORITY
local HIGHLIGHTON
local HIGHLIGHTOFF
    if [ "$LEVEL" = "BREAK" ]
    then
        echo "--------------------------------------------------------------------------------" | tee -a $LOGFILE
        return 0
    elif [ "$LEVEL" = "ERROR" ]
    then
        PRIORITY=4
        HIGHLIGHTON="${REDFG}"
        HIGHLIGHTOFF="${COLOURORIG}"
        FINALEXIT=1 # Global
    elif [ "$LEVEL" = "WARN" ]
    then
        PRIORITY=4
        HIGHLIGHTON="${REDFG}"
        HIGHLIGHTOFF="${COLOURORIG}"
    elif [ "$LEVEL" = "SUCCESS" ]
    then
        PRIORITY=5
        HIGHLIGHTON="${GREENFG}"
        HIGHLIGHTOFF="${COLOURORIG}"
    elif [ "$LEVEL" = "START" -o "$LEVEL" = "STOP" ]
    then
        PRIORITY=5
        HIGHLIGHTON="${BOLDON}"
        HIGHLIGHTOFF="${ALLOFF}"
    elif [ "$LEVEL" = "DEBUG" ]
    then
        [ "$DEBUG" = "ON" ] || return
        PRIORITY=7
        HIGHLIGHTON=""
        HIGHLIGHTOFF=""
    elif [ "$LEVEL" = "NOHEADER" ]
    then
        # Also no db logging
        echo "$*" | tee -a $LOGFILE
        return
    else
        [ "$INFO" = "ON" ] || return
        LEVEL="INFO"
        PRIORITY=6
        HIGHLIGHTON=""
        HIGHLIGHTOFF=""
    fi
    echo "${HIGHLIGHTON}$(date +%d/%m,%H:%M) [${$}] $LEVEL $*${HIGHLIGHTOFF}" | tee -a $LOGFILE

    [ "$DBLOGGING" -eq 1 ] && insertmythlogentry "$PRIORITY" "$LEVEL" "${$}" "$*"
}

insertmythlogentry() {
local PRIORITY="$1"
local LEVEL="$2"
local PID="$3"
local DETAILS="$(echo $4 | tr -d '[:cntrl:]' | tr -d '[\\\"]')"
local DATETIME=$(date '+%Y%m%d%H%M%S')
local HOST=$(hostname)
    mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    insert into mythlog set
    module = "mythnuv2mkv.sh",
    priority = $PRIORITY,
    acknowledged = 0,
    logdate = $DATETIME,
    host = "$HOST",
    message = "mythnuv2mkv.sh [$PID] $LEVEL",
    details = "$DETAILS";
    EOF
}

getjobqueuestatus() {
local JOBID="$1"
local DATA
local JQSTATUSSTR[0]="UNKNOWN"
local JQSTATUSSTR[1]="QUEUED"
local JQSTATUSSTR[2]="PENDING"
local JQSTATUSSTR[3]="STARTING"
local JQSTATUSSTR[4]="RUNNING"
local JQSTATUSSTR[5]="STOPPING"
local JQSTATUSSTR[6]="PAUSED"
local JQSTATUSSTR[7]="RETRY"
local JQSTATUSSTR[8]="ERRORING"
local JQSTATUSSTR[9]="ABORTING"
local JQSTATUSSTR[256]="DONE"
local JQSTATUSSTR[272]="FINISHED"
local JQSTATUSSTR[288]="ABORTED"
local JQSTATUSSTR[304]="ERRORED"
local JQSTATUSSTR[320]="CANCELLED"
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select status from jobqueue where id = $JOBID;
    EOF
    )
    echo ${JQSTATUSSTR[$DATA]}
}

setjobqueuestatus() {
local JOBID="$1"
local STATUSSTR="$2"
local STATUS
    if echo "$STATUSSTR" | egrep '^[0-9]+$' >/dev/null 2>&1
    then
        STATUS=$STATUSSTR
    elif [ "$STATUSSTR" = "RUNNING" ]
    then
        STATUS=4
    elif [ "$STATUSSTR" = "PAUSED" ]
    then
        STATUS=6
    elif [ "$STATUSSTR" = "ABORTING" ]
    then
        STATUS=9
    elif [ "$STATUSSTR" = "FINISHED" ]
    then
        STATUS=272
    elif [ "$STATUSSTR" = "ERRORED" ]
    then
        STATUS=304
    fi
    if [ -n "$STATUS" ]
    then
        mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
        update jobqueue set status = $STATUS where id = $JOBID;
        EOF
    else
        scriptlog ERROR "Invalid Job Queue Status."
    fi
}

getjobqueuecmds() {
local JOBID="$1"
local DATA
local JQCMDSTR[0]="RUN"
local JQCMDSTR[1]="PAUSE"
local JQCMDSTR[2]="RESUME"
local JQCMDSTR[4]="STOP"
local JQCMDSTR[8]="RESTART"
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select cmds from jobqueue where id = $JOBID;
    EOF
    )
    echo ${JQCMDSTR[$DATA]}
}

setjobqueuecmds() {
local JOBID="$1"
local CMDSSTR="$2"
local CMDS
    if echo "$CMDSSTR" | egrep '^[0-9]+$' >/dev/null 2>&1
    then
        CMDS=$CMDSSTR
    elif [ "$CMDSSTR" = "RUN" ]
    then
        CMDS=0
    fi
    if [ -n "$CMDS" ]
    then
        mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
        update jobqueue set cmds = $CMDS where id = $JOBID;
        EOF
    else
        scriptlog ERROR "Invalid Job Queue Command."
    fi
}

is21orless() {
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select subtitle from videometadata limit 1;
    EOF
    )
    echo "$DATA" | egrep 'ERROR.*Unknown column' >/dev/null 2>&1 && return 0 || return 1
}

lookupinetref() {
# : is used to separate Title and SubTitle in SEARCHTITLE
local SEARCHTITLE="$1"
local CHANID="$2"
local STARTTIME="$3"
local IMDBCMD
local IMDBRES
local IMDBSTR=""
# INETREF will be 00000000 if not found
local INETREF=00000000
local SERIES
local EPISODE
local YEAR
local TMP
    {
        IMDBCMD=$(getsetting MovieListCommandLine)
    # This is dependent on imdb.pl and will not work with any MovieListCommandLine due to use of s=ep option.
    set - $IMDBCMD
    IMDBCMD="$1 $2"
        IMDBRES=$($IMDBCMD "$SEARCHTITLE")
        if [ -n "$IMDBRES" -a $(echo "$IMDBRES" | wc -l) -eq 1 ]
        then
        IMDBSTR="$IMDBRES"
    elif [ -n "$CHANID" ]
    then
        YEAR=$(getyear $CHANID $STARTTIME)
        if [ "$YEAR" -gt 1800 ]
        then
            for C in 0 1 -1
            do
                TMP=$(echo "$IMDBRES" | grep $(( $YEAR + $C )))
                [ -n "$TMP" -a $(echo "$TMP" | wc -l) -eq 1 ] && IMDBSTR="$TMP" && break
            done
        fi
        fi
    if [ -n "$IMDBSTR" ]
    then
                INETREF=$(echo "$IMDBSTR" | awk -F'[^0-9]' '{print $1}')
                echo $INETREF | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]*$' >/dev/null 2>&1 || INETREF=00000000
    fi
        if [ "$INETREF" -eq 00000000 ]
        then
        # Try looking for episode
                OLDIFS="$IFS"; IFS=":"; set - $SEARCHTITLE; IFS="$OLDIFS"
        SERIES="$1" ; EPISODE="$2"
        if [ -n "$SERIES" -a -n "$EPISODE" ]
        then
            # option s=ep is for episode lookup
            IMDBSTR=$($IMDBCMD s=ep "$EPISODE")
            if which agrep >/dev/null 2>&1
            then
                IMDBSTR=$(echo "$IMDBSTR" | agrep -i -s -2 "$SERIES" | sort -n | head -1 | cut -d':' -f2-)
            else
                IMDBSTR=$(echo "$IMDBSTR" | grep -i "$SERIES")
            fi
            if [ $(echo "$IMDBSTR" | wc -l) -eq 1 ]
            then
                INETREF=$(echo "$IMDBSTR" | awk -F'[^0-9]' '{print $1}')
                echo $INETREF | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]*$' >/dev/null 2>&1 || INETREF=00000000
            fi
        fi
        fi
    scriptlog DEBUG "inetref $INETREF"
    } >/dev/null 2>&1
        echo $INETREF
}

getjobqueuecomment() {
local JOBID="$1"
local COMMENT="$2"
    mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select comment from jobqueue where id = $JOBID;
    EOF
}

setjobqueuecomment() {
local JOBID="$1"
local COMMENT="$2"
    mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    update jobqueue set comment = "$COMMENT" where id = $JOBID;
    EOF
}

findchanidstarttime() {
local SEARCHTITLE="$1"
    mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select title, subtitle, chanid, date_format(starttime, '%Y%m%d%H%i%s'), storagegroup from recorded where title like "%${SEARCHTITLE}%";
    EOF
}

gettitle() {
local CHANID="$1"
local STARTTIME="$2"
    [ -n "$CHANID" ] || return 1
    mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select title from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
}

getsubtitle() {
local CHANID="$1"
local STARTTIME="$2"
    [ -n "$CHANID" ] || return 1
    mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select subtitle from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
}

getsearchtitle() {
local CHANID="$1"
local STARTTIME="$2"
local TI
local ST
local SEARCHTITLE
    [ -n "$CHANID" ] || return 1
    TI=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select title from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    ST=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select subtitle from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    if [ -n "$TI" -a -n "$ST" ]
    then
        SEARCHTITLE="${TI}:${ST}"
    elif [ -n "$TI" ]
    then
        SEARCHTITLE="${TI}"
    fi
    echo $SEARCHTITLE
}

getseriesepisode() {
local CHANID="$1"
local STARTTIME="$2"
local INETREF="$3"
local DATA
local SE
    [ -n "$CHANID" ] || return 1
    {
    # STARTTIME is not always the same in both tables for matching programs. ???
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select season,episode from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    DATA=$(echo "$DATA" | awk -F ' ' '{printf("S%02dE%02d\n",$1,$2)}')
    if echo "$DATA" | grep '^S[0-9][0-9]E[0-9][0-9]$' >/dev/null 2>&1
    then
        SE="$DATA"
    elif [ $INETREF -gt 0 ]
    then
        # Lets try passing imdb page
        wget -o /dev/null -O "${FIFODIR}/${INETREF}.html" "http://www.imdb.com/title/tt${INETREF}/"
        SE=$(awk '/Season.*Episode/ {
        a=match($0,/Season ([0-9]+)/,s);b=match($0,/Episode ([0-9]+)/,e);if(a>0 && b>0){printf("S%02dE%02d\n",s[1],e[1]);exit}
        }'
"${FIFODIR}/${INETREF}.html")
    fi
    scriptlog DEBUG "series episode $SE"
    } >/dev/null 2>&1
    echo "$SE" | grep '^S[0-9][0-9]E[0-9][0-9]$'
}

createfiletitleSEsubtitle() {
local CHANID="$1"
local STARTTIME="$2"
local SE="$3"
local DATA
local T
local S
    [ -n "$CHANID" ] || return 1
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select title from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    T=$(echo $DATA | tr -d '[:cntrl:]' | tr -d '[:punct:]' | tr '[:space:]' '_')

    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select subtitle from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    S=$(echo $DATA | tr -d '[:cntrl:]' | tr -d '[:punct:]' | tr '[:space:]' '_')

    if [ -n "$T" -a -n "$SE" -a -n "$S" ]
    then
        echo "${T}${SEP}${SE}${SEP}${S}"
    elif [ -n "$T" -a -n "$S" ]
    then
        echo "${T}${SEP}${S}"
    elif [ -n "$T" -a -n "$SE" ]
    then
        echo "${T}${SEP}${SE}"
    else
        echo "${T}"
    fi
}

getrecordfile() {
local CHANID="$1"
local STARTTIME="$2"
local DEBUGSG="$3"
local DATA
local DATALINE
local RECFILE
local SGHOST
    [ -n "$CHANID" ] || return 1
    # Storage groups
    if [ "$DEBUGSG" = "ON" ]
    then
        scriptlog INFO "CHANID $CHANID STARTTIME $STARTTIME"
        DATA=$(mysql --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
        select * from storagegroup;
        select chanid,starttime,title,subtitle,basename,storagegroup from recorded where chanid = $CHANID and starttime = "$STARTTIME";
        EOF
        )
        scriptlog INFO "Tables"
        scriptlog NOHEADER "$DATA"
    fi
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select a.hostname,':::',concat(a.dirname, "/", b.basename) from storagegroup a, recorded b where b.chanid = $CHANID and b.starttime = "$STARTTIME" and b.storagegroup = a.groupname;
    EOF
    )
    [ "$DEBUGSG" = "ON" ] && scriptlog INFO "Try 1 Data $DATA"
    while read DATALINE
    do
        SGHOST=$(echo "$DATALINE" | awk -F':::' '{print $1}' | sed -e 's/[ \t]*\(.*\)[ \t]*/\1/')
        RECFILE=$(echo "$DATALINE" | awk -F':::' '{print $2}' | sed -e 's/[ \t]*\(.*\)[ \t]*/\1/')
        [ "$DEBUGSG" = "ON" ] && scriptlog INFO "Try 1 Check SGHost $SGHOST RecFile $RECFILE"
        [ -f "${RECFILE}" ] && break
    done < <(echo "$DATA")
    if [ ! -f "$RECFILE" ]
    then
        # Pre Storage groups
        local RFP=$(getsetting RecordFilePrefix)
        DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
        select concat("$RFP", "/", basename) from recorded where chanid = $CHANID and starttime = "$STARTTIME" limit 1;
        EOF
        )
        [ "$DEBUGSG" = "ON" ] && scriptlog INFO "Try 2 $RFP,$DATA"
        RECFILE="$DATA"
    fi
    [ -f "$RECFILE" ] && echo "$RECFILE"
}

cleanup() {
local SIG="$1"
local JOBID="$2"
local OUTPUT="$3"
local TRANPID
    scriptlog DEBUG "$SIG Cleaning up."
    if [ "$SIG" = "ABRT" ]
    then
        scriptlog ERROR "Job Aborted. Removing incomplete $OUTPUT."
        [ "$DEBUG" != "ON" ] && rm -f "$OUTPUT" >/dev/null 2>&1      
    fi
    TRANPID=$(jobs -l |awk '/mythtranscode/ {P=$2" "P} END {print P}')
    if [ -n "$TRANPID" ]
    then
        scriptlog DEBUG "Killing mythtranscode [$TRANPID]"
        ps -p $TRANPID >/dev/null 2>&1 && kill $TRANPID >/dev/null 2>&1
    fi
   
    if [ "$FINALEXIT" -eq 0 ]
    then
        if [ "$DEBUG" != "ON" ]
        then
            rm -rf "${FIFODIR}*" >/dev/null 2>&1
        fi
        scriptlog INFO "Exiting. Successful."
        if [ "$JOBID" -ne 99999999 ]
        then
            setjobqueuestatus "$JOBID" "FINISHED"
            setjobqueuecomment "$JOBID" "[${$}] Successfully Completed"
        fi
        exit 0
    else
        scriptlog INFO "Exiting. Errored."
        if [ "$JOBID" -ne 99999999 ]
        then
            setjobqueuestatus "$JOBID" "ERRORED"
            setjobqueuecomment "$JOBID" "[${$}] Errored"
        fi
        exit 1
    fi
}

hascutlist() {
local CHANID="$1"
local STARTTIME="$2"
local DATA
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select mark from recordedmarkup where chanid = ${CHANID} and starttime = "${STARTTIME}" and (type = 0 or type = 1);
    EOF
    )
    [ -z "$DATA" ] && return 0 || return 1
}

getcutstart() {
local CHANID="$1"
local STARTTIME="$2"
local DATA
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select mark from recordedmarkup where chanid = ${CHANID} and starttime = "${STARTTIME}" and (type = 0) ORDER BY mark limit 1;
    EOF
    )
    echo "$DATA"   
}

getcutend() {
local CHANID="$1"
local STARTTIME="$2"
local CUTSTART="$3"
local DATA
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select mark from recordedmarkup where chanid = ${CHANID} and starttime = "${STARTTIME}" and (type = 1) ORDER BY mark DESC limit 1;
    EOF
    )
    [ $DATA -gt $CUTSTART ] && echo "--stop-at frame:$(expr $DATA - $CUTSTART)" || echo ""
}

createmetadatafile(){
local CHANID="$1"
local STARTTIME="$2"
local OUTFILE="$3"
local OIFS="$IFS"
local DATA
    DATA=$(mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    select concat_ws('|',title,subtitle,category,description,season,episode) from recorded where chanid = $CHANID and starttime = "$STARTTIME";
    EOF
    )
    IFS='|'
    METAARRAY=($DATA)
    IFS=$OIFS
    cat >$OUTFILE <<-EOF
    <?xml version="1.0" encoding="utf-8"?>
    <Tags>
      <Tag>
        <!-- show -->
        <Targets>
          <TargetTypeValue>70</TargetTypeValue>
        </Targets>
        <Simple>
          <Name>TITLE</Name>
          <String>${METAARRAY[0]}</String>
        </Simple>
        <Simple>
          <Name>CONTENT_TYPE</Name>
          <String>${METAARRAY[2]}</String>
        </Simple>
      </Tag>
      <Tag>
        <!-- Season -->
        <Targets>
          <TargetTypeValue>60</TargetTypeValue>
        </Targets>
        <Simple>
          <Name>PART_NUMBER</Name>
          <String>${METAARRAY[4]}</String>
        </Simple>
      </Tag>
      <Tag>
        <!-- Episode info -->
        <Targets>
          <TargetTypeValue>50</TargetTypeValue>
        </Targets>
        <Simple>
          <Name>TITLE</Name>
          <String>${METAARRAY[1]}</String>
        </Simple>
        <Simple>
          <Name>SUMMARY</Name>
          <String>${METAARRAY[3]}</String>
        </Simple>
        <Simple>
          <Name>PART_NUMBER</Name>
          <String>${METAARRAY[5]}</String>
        </Simple>
      </Tag>
    </Tags>
    EOF
}

setepisode() {
local CHANID="$1"
local STARTTIME="$2"
local SE=$3
local S
local E
local DATA
    S=$(echo $SE | awk -F [SE] '{print $2}')
    E=$(echo $SE | awk -F [SE] '{print $3}')
    mysql --batch --skip-column-names --user="${DBUserName}" --password="${DBPassword}" -h "${DBHostName}" "${DBName}" <<-EOF
    update recorded set season = $S, episode = $E  where chanid = ${CHANID} and starttime = "${STARTTIME}";
    EOF
}

getaudiotracks() {
local INPUT="$1"
local LANGUAGE
local TYPE
local DATA
local STREAM=0
local AUDIO="--audio "
local ENCODER="--aencoder "
local ANAME="--aname "
    DATA=$(ffmpeg -i ${INPUT} 2>&1 | grep Audio: | sed 's/ //g')
    for track in $DATA
    do
        STREAM=$(expr $STREAM + 1)
        LANGUAGE=$(echo "$track" | sed 's/.*(\([[:alpha:]]\{3\}\)).*/\1/')
        TYPE=$(echo "$track" | sed 's/.*\(ac3\|mp2\).*/\1/')
        if [ "$LANGUAGE" != "NAR" ]
        then
            AUDIO="${AUDIO}${STREAM},"
            ANAME="${ANAME}${LANGUAGE},"
            if [ "$TYPE" == "ac3" ]
            then
                if [ "$PASSTHROUGH" == "ON" ]
                then
                    ENCODER="${ENCODER}copy,"
                else
                    ENCODER="${ENCODER}faac,"
                fi
            else
                ENCODER="${ENCODER}faac,"
            fi
        fi
    done
    AUDIO=$(echo "$AUDIO" | sed 's/\(.*\),$/\1/')
    ENCODER=$(echo "$ENCODER" | sed 's/\(.*\),$/\1/')
    ANAME=$(echo "$ANAME" | sed 's/\(.*\),$/\1/')
    echo "$AUDIO $ENCODER $ANAME"
}

### Globals ######################################################
readonly CMD="$0"
readonly LOGFILE="${LOGBASEDIR}/mythbrake${$}.log"
readonly FIFODIR="${LOGBASEDIR}/mythbrake${$}"
readonly TRANSOP="${FIFODIR}/transcode.op"
readonly HANDOP="${FIFODIR}/handbrake.op"
if ! tty >/dev/null 2>&1
then
    readonly BOLDON=""
    readonly ALLOFF=""
    readonly REDFG=""
    readonly GREENFG=""
    readonly COLOURORIG=""
    [ "$DEBUG" = "ON" ] && exec 3>"${LOGBASEDIR}/DEBUG" || exec 3>/dev/null
    exec 1>&3
    exec 2>&3
else
    readonly BOLDON=`tput bold`
    readonly ALLOFF=`tput sgr0`
    readonly REDFG=`tput setaf 1`
    readonly GREENFG=`tput setaf 2`
    readonly COLOURORIG=`tput op`
fi

# Turn DBLOGGING off by default
DBLOGGING=0
SEPI=_

# If we run from the command line, we don't care about job handling
JOBID=99999999

# The FINALEXIT variable contains the return value for the script.
FINALEXIT=0

OUTPUT=""

# Use --strict-anamorphic by default
STRICTPAR="--strict-anamorphic"

### Main #########################################################

# Get the database access parameters from the configuration. If this fails, set $MYSQLTXT at the beginning of the file
MYSQLLIST="$MYSQLTXT /home/mythtv/.mythtv/mysql.txt ${HOME}/.mythtv/mysql.txt /.mythtv/mysql.txt /usr/local/share/mythtv/mysql.txt /usr/share/mythtv/mysql.txt /etc/mythtv/mysql.txt /usr/local/etc/mythtv/mysql.txt mysql.txt"
for m in $MYSQLLIST
do
    [ -f $m ] && . $m && break
done
if [ -z "$DBName" ]
then
    echo "Can'
t find mysql.txt. Change MYSQLTXT variable at top of script with your mysql.txt path"
    exit 1
fi

##### BG Monitor #####################################
# This will be fired off in background to update the jobqueue comment and process stop/pause/resume requests.
if echo "
$1" | egrep -i '\-\-monitor=' >/dev/null 2>&1
then
    readonly MONJOBID=$(echo "
$1" | cut -d'=' -f2)
    readonly MONPID="
$2"
    readonly MONTRANSOP="
$3"
    readonly LOGFILE="
$4"
    readonly DBLOGGING=$(getsetting "
LogEnabled")

    [ "
$MONJOBID" -ne 99999999 -a -n "$MONPID" ] || exit 1

    PAUSEALREADYPRINTED="
" ; RESUMEALREADYPRINTED=""
   
    scriptlog INFO "
Starting monitoring process."
    sleep 5
    while ps -p $MONPID >/dev/null 2>&1
    do
        JQCMD=$(getjobqueuecmds "
$MONJOBID")
        if [ "
$JQCMD" = "PAUSE" ]
        then
            JQSTATUS=$(getjobqueuestatus "
$MONJOBID")
            if [ "
$JQSTATUS" != "PAUSED" ]
            then
                MENCODERPID=$(ps --ppid $MONPID | awk '/mencoder/ {print $1}')
                if [ -n "
$MENCODERPID" ]
                then
                    PAUSEALREADYPRINTED="
"
                    STARTPAUSESECS=$(date +%s)
                    kill -s STOP $MENCODERPID
                    setjobqueuestatus "
$MONJOBID" "PAUSED"
                    SAVEDCC=$(getjobqueuecomment "
$MONJOBID")
                    setjobqueuecomment "
$MONJOBID" "[$MONPID] Paused for 0 Seconds"
                    scriptlog STOP "
Job Paused due to job queue pause request."
                else
                    [ -z "
$PAUSEALREADYPRINTED" ] && scriptlog ERROR "Sorry, could not pause. Will keep trying"
                    PAUSEALREADYPRINTED=TRUE
                fi
            else
                NOW=$(date +%s)
                PAUSESECS=$(( $NOW - $STARTPAUSESECS ))
                PAUSEMINS=$(( $PAUSESECS / 60 ))
                PAUSEHOURS=$(( $PAUSEMINS / 60 ))
                PAUSEMINS=$(( $PAUSEMINS - ( $PAUSEHOURS * 60 ) ))
                PAUSESECS=$(( $PAUSESECS - ( ( $PAUSEHOURS * 60 * 60 ) + ( $PAUSEMINS * 60 ) ) ))
                setjobqueuecomment "
$MONJOBID" "[$MONPID] Paused for $PAUSEHOURS Hrs $PAUSEMINS Mins $PAUSESECS Secs"
            fi
        elif [ "
$JQCMD" = "RESUME" ]
        then
            JQSTATUS=$(getjobqueuestatus "
$MONJOBID")
            if [ "
$JQSTATUS" != "RUNNING" ]
            then
                MENCODERPID=$(ps --ppid $MONPID | awk '/mencoder/ {print $1}')
                if [ -n "
$MENCODERPID" ]
                then
                    RESUMEALREADYPRINTED="
"
                    kill -s CONT $MENCODERPID
                    setjobqueuestatus "
$MONJOBID" "RUNNING"
                    setjobqueuecomment "
$MONJOBID" "$SAVEDCC"
                    scriptlog START "
Job resumed due to job queue resume request."
                    setjobqueuecmds "
$MONJOBID" "RUN"
                else
                    [ -z "
$RESUMEALREADYPRINTED" ] && scriptlog ERROR "Sorry, could not resume. Will keep trying"
                    RESUMEALREADYPRINTED=TRUE
                fi
            fi
        elif [ "
$JQCMD" = "STOP" ]
        then
            setjobqueuestatus "
$MONJOBID" "ABORTING"
            setjobqueuecomment "
$MONJOBID" "[$MONPID] Stopping"
            scriptlog STOP "
Stopping due to job queue stop request."
            setjobqueuecmds "
$MONJOBID" "RUN"
            kill -s ABRT $MONPID
            sleep 2
            kill $MONPID
        elif [ "
$JQCMD" = "RESTART" ]
        then
            scriptlog ERROR "
Sorry, can't restart job."
            setjobqueuecmds "$MONJOBID" "RUN"
        else
            CC=$(getjobqueuecomment "$MONJOBID")
            if echo "$CC" | grep '
audio pass' >/dev/null 2>&1
            then
                PASSNU="audio pass"
            elif echo "$CC" | grep '
Single video pass' >/dev/null 2>&1
            then
                PASSNU="Single video pass"
            elif echo "$CC" | grep '
1st video pass' >/dev/null 2>&1
            then
                PASSNU="1st video pass"
            elif echo "$CC" | grep '
2nd video pass' >/dev/null 2>&1
            then
                PASSNU="2nd video pass"
            else
                sleep 15
                continue
            fi
            PCTLINE=$(tail -10 "$MONTRANSOP" | grep '
mythtranscode:' | cut -c39- | tail -1)
            [ -n "$PASSNU" -a -n "$PCTLINE" ] && setjobqueuecomment "$MONJOBID" "[$MONPID] $PASSNU $PCTLINE"
        fi
        sleep 15
    done
    exit
fi

# Parse the argument list.
set -- `getopt -u -o hj:f:pq:w:c:s:t:e:ml: -l usage,jobid:,format:,passthrough,quality:,height:,chanid:,starttime:,findtitle:,episode:monitor -- "$@"`
[ $# -lt 1 ] && exit 1 # getopt failed
while [ $# -gt 0 ]
do
    case "$1" in
        -h | usage)
            echo >&2 "${USAGE}"
            exit 1;;
        -j | --jobid)
            shift
            JOBID="$1";;
        -f | --format)
            shift
            FORMAT="$1";;
        -p | --passthrough)
            PASSTHROUGH="ON";;
        -q | --quality)
            shift
            QUALITY="$1";;
       -l | --height)
           shift
           HEIGHT="$1"i
           STRICTPAR="";;
       -c | --chanid)
           shift
           CHANID="$1";;
       -s | --starttime)
           shift
           STARTTIME="$1";;
       -t | --findtitle)
           shift
           MATCHTITLE=$(findchanidstarttime "$1")
       echo "$MATCHTITLE"
           exit 0;;
       -e | --episode)
       shift
           SEPI="$1"
           setepisode ${CHANID} "${STARTTIME}" "${SEPI}"
           exit 0;;
       --)
           shift; break;;
       -*)
           echo >&2 ${USAGE}
           exit 1;;
       *)
           break;;  # terminate the while loop
    esac
    shift
done

# The next parameter is the basedir
BASEDIR="$@"

# All requirements met?
if [ -z "$BASEDIR" -o -z "$CHANID" -o -z "$STARTTIME" ]
then
    scriptlog ERROR "You did not pass a program info and a target directory. You need to specifiy them, in order to tell $CMD what recording to convert."
    exit 1
fi

# set traps in case of abort
trap '
cleanup ABRT "$JOBID" "$OUTPUT"' INT ABRT
trap '
cleanup EXIT "$JOBID"' EXIT

# create logfile and fifo directories
mkdir -m 755 -p "${LOGBASEDIR}" >/dev/null 2>&1 || scriptlog ERROR "Could not create ${LOGBASEDIR}"
mkdir -m 755 -p "${FIFODIR}" >/dev/null 2>&1 || scriptlog ERROR "Could not create ${FIFIDIR}"
[ -w "${LOGBASEDIR}" ] || scriptlog ERROR "${LOGBASEDIR} not writable"
[ -w "${FIFODIR}" ] || scriptlog ERROR "${FIFODIR} not writable"
[ ${FINALEXIT} ] || exit $FINALEXIT

# Find the file name for the show to convert.
INPUT=$(getrecordfile "$CHANID" "$STARTTIME")
if [ -z "$INPUT" ]
then
    scriptlog ERROR "Skipping $CHANID $STARTTIME. Did not match recording."
    scriptlog BREAK
    exit 1
fi
if [ ! -f "$INPUT" ]
then
    scriptlog ERROR "Could not find recording. ($INPUT)"
    scriptlog BREAK
    exit 1
fi

# get the shows title and episode info
TITLE=$(gettitle "$CHANID" "$STARTTIME")
SUBTITLE=$(getsubtitle "$CHANID" "$STARTTIME")
SEARCHTITLE=$(getsearchtitle "$CHANID" "$STARTTIME")
is21orless && INETREF=$(lookupinetref "$SEARCHTITLE" "$CHANID" "$STARTTIME") || INETREF="00000000"
SERIESEPISODE=$(getseriesepisode "$CHANID" "$STARTTIME" "$INETREF")
if [ "-$SERIESEPISODE" = "-"  ]
then
    SERIESEPISODE="$SEPI"
fi
OUTPUTTITLE=$(createfiletitleSEsubtitle "$CHANID" "$STARTTIME" "$SERIESEPISODE")

# create the output file name
OUTPUT="${BASEDIR}/${TITLE}/${OUTPUTTITLE}.${FORMAT}"

# Fireoff a background monitoring job to update the job queue details
[ "$JOBID" -ne 99999999 ] && $CMD --monitor=$JOBID ${$} "$TRANSOP" "$LOGFILE" &

scriptlog START "Transcoding $TITLE ($SERIESEPISODE) to $OUTPUT"
[ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}]" Transcoding started

# now begin creating the target path, if neccessary
mkdir -m 755 -p "${BASEDIR}/${TITLE}" >/dev/null 2>&1 || scriptlog ERROR "Could not create ${BASEDIR}/${TITLE}"
[ -w "${BASEDIR}/${TITLE}" ] || scriptlog ERROR "${BASEDIR}/${TITLE} not writable."
[ ${FINALEXIT} ] || exit ${FINALEXIT}

# set audio options for handbrake
AUDIO=$(getaudiotracks "$INPUT")

scriptlog INFO "Encoding audio as $AUDIO"

# Get the width of the video, if not given
if [ ! -v  HEIGHT ]
then
    scriptlog INFO "Getting the Height of the video"
    ID_VIDEO_HEIGHT=$(mplayer -nosound -vo null -frames 0 -identify $INPUT | grep ID_VIDEO_HEIGHT)
    HEIGHT=${ID_VIDEO_HEIGHT#ID_VIDEO_HEIGHT=}
fi

scriptlog INFO "Using video size $HEIGHT"

# Add the cutlist, if possible
# For now the script takes the end of the first cut, and the start of the last, and only includes what is inbetween.
# Other cuts will be ignored.
STARTAT=""
STOPAT=""
hascutlist "$CHANID" "$STARTTIME"
if [ $? -ne 0 ]
then
    scriptlog INFO "Honoring cutlist"
    STARTAT="--start-at frame:$(getcutstart "$CHANID" "$STARTTIME")"
    STOPAT=$(getcutend $CHANID "$STARTTIME" $(getcutstart "$CHANID" "$STARTTIME"))
fi

# run handbrake
scriptlog START "Encoding $OUTPUTTITLE with HandBrake started"
[ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}] Encoding $OUTPUTFILE started"
#echo "Running /usr/bin/HandBrakeCLI -e x264 -q $QUALITY $AUDIO -F $FORMAT -l $HEIGHT $STRCTPAR $STARTAT $STOPAT -d slow -m -x ref=2:bframes=2:me=umh -i "${INPUT}" -o "$OUTPUT"" 2>&1 | tee -a $LOGFILE
nice -n 19 /usr/bin/HandBrakeCLI -e x264 -q $QUALITY $AUDIO -F $FORMAT -l $HEIGHT $STRICTPAR $STARTAT $STOPAT -d slow -m -x ref=2:bframes=2:me=umh -i "${INPUT}" -o "$OUTPUT" 2>&1
scriptlog STOP "Encoding $OUTPUTTITLE with HandBrake ended"
[ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}] Encoding $OUTPUTFILE finished"

if [ "-$FORMAT" = "-mkv" ]
then
    scriptlog INFO "Adding Title, Subtitle and Episodeinfo"
    [ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}] Modifying metadata"
    createmetadatafile "$CHANID" "$STARTTIME" "${FIFODIR}/metadata.xml"
    mkvpropedit "$OUTPUT" -t all:${FIFODIR}/metadata.xml
fi

if [ $? -eq 0 ]
then
    scriptlog SUCCESS "Encoding Successful."
    [ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}] finished successful"
else
    scriptlog ERROR "Encoding failed."
    [ "$JOBID" -ne 99999999 ] && setjobqueuecomment "$JOBID" "[${$}] failed."
fi

exit ${FINALEXIT}
There are no comments on this page.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki