Showing posts with label scripts. Show all posts
Showing posts with label scripts. Show all posts

Saturday, October 26, 2013

Shepherd cron job for MythTV

This cron job is run as my regular user, the same one who uses MythFrontend

$ crontab -l
36 * * * * nice ~/.shepherd/shepherd --daily --quiet && nice /usr/bin/mythfilldatabase --update --file --sourceid 1 --xmlfile ~/.shepherd/output.xmltv --quiet 2>&1> /dev/null

Tuesday, May 8, 2012

kill processes with kill-byname.sh

#!/bin/bash
# kill-byname.sh: Killing processes by name.
# Compare this script with kill-process.sh.

#  For instance,
#+ try "./kill-byname.sh xterm" --
#+ and watch all the xterms on your desktop disappear.

#  Warning:
#  -------
#  This is a fairly dangerous script.
#  Running it carelessly (especially as root)
#+ can cause data loss and other undesirable effects.

E_BADARGS=66

if test -z "$1"  # No command-line arg supplied?
then
  echo "Usage: `basename $0` Process(es)_to_kill"
  exit $E_BADARGS
fi


PROCESS_NAME="$1"
ps ax | grep "$PROCESS_NAME" | awk '{print $1}' | xargs -i kill {} 2&>/dev/null
#                                                       ^^      ^^

# ---------------------------------------------------------------
# Notes:
# -i is the "replace strings" option to xargs.
# The curly brackets are the placeholder for the replacement.
# 2&>/dev/null suppresses unwanted error messages.
#
# Can  grep "$PROCESS_NAME" be replaced by pidof "$PROCESS_NAME"?
# ---------------------------------------------------------------

exit $?

#  The "killall" command has the same effect as this script,
#+ but using it is not quite as educational.

Tuesday, April 17, 2012

Clone Arch root

This script will make a clone of my root directory onto a mounted partition. It is far from perfect!
It requires my root /etc/fstab plus a "clone-version" /etc/fstabClone (see below)
It requires folders /mnt/ssdRootClone on the original and /mnt/ssdRoot on the Clone.
I run it as root.

##############################################################################

#!/bin/bash
# fileinfo.sh
LOG="/var/log/cloneArch.log"
date=`date "+%a %b %e %H:%M:%S %Y"`
DESTINATION="/mnt/ssdRootClone"
#DESTINATION="/home/david/Clone"
FSTAB="/etc/fstab"
FSTABCLONE="/etc/fstabClone"


FILES="/bin
/boot
/dev
/etc
/lib
/lib64
/opt
/root
/run
/sbin
/srv
/tmp
/usr
/var"


FOLDERTOREMOVE="$DESTINATION/boot/grub/grub"


echo "$date : " >>$LOG
echo "$date : System is being cloned." >>$LOG
echo "$date : Clone mounted at $DESTINATION" >>$LOG
echo "$date : " >>$LOG


# Clear the variable directpries from the destination first
#rm -rf $DESTINATION/tmp
#rm -rf $DESTINATION/var


for file in $FILES
do
  
  if [ ! -e "$file" ]       # Check if file exists.
  then
    echo "$date : $file does not exist and has not been cloned." >>$LOG
  else


    # Clone the system here
    echo "$date : $file is next." >>$LOG
    # rsync options used
    #
    # -a, --archive
    # This is equivalent to -rlptgoD. It is a quick way of saying  you
    # want  recursion  and want to preserve almost everything (with -H
    # being a notable omission).  The  only  exception  to  the  above
    # equivalence  is when --files-from is specified, in which case -r
    # is not implied.
    #
    # Note that -a does not preserve hardlinks, because finding multiply
    # linked  files is expensive.  You must separately specify -H.
    #
    # -u, --update
    # This  forces rsync to skip any files which exist on the destination 
    # and have a modified time that  is  newer  than  the  source file.
    # (If an existing destination file has a modification time equal to
    # the source file's, it will be updated if the sizes  are different.)
    #
    # Note  that this does not affect the copying of symlinks or other
    # special files.  Also, a difference of file  format  between  the
    # sender  and receiver is always considered to be important enough
    # for an update, no matter what date is on the objects.  In  other
    # words, if the source has a directory where the destination has a
    # file, the transfer would occur regardless of the timestamps.
    #
    # This option is a transfer rule, not an exclude,  so  it  doesn't
    # affect  the  data  that  goes  into  the file-lists, and thus it
    # doesn't affect deletions.  It just limits  the  files  that  the
    # receiver requests to be transferred.
    #
    # -H, --hard-links
    # This tells rsync to look for hard-linked files in the source and
    # link together the corresponding files on the destination.  With-
    # out  this option, hard-linked files in the source are treated as
    # though they were separate files.
    #
    # This option does NOT necessarily ensure that the pattern of hard
    # links  on  the  destination  exactly matches that on the source.
    # Cases in which the destination may end up with extra hard  links
    # include the following:
    #
    #
    # o      If  the  destination contains extraneous hard-links (more
    #        linking than what is present in the  source  file  list),
    #        the  copying  algorithm  will  not break them explicitly.
    #        However, if one or more of the paths have content differ-
    #        ences,  the  normal  file-update process will break those
    #        extra links (unless you are using the --inplace  option).
    #
    # o      If you specify a --link-dest directory that contains hard
    #        links, the linking of the destination files  against  the
    #        --link-dest files can cause some paths in the destination
    #        to become linked together due to the --link-dest associa-
    #        tions.
    #
    #
    # Note  that  rsync  can only detect hard links between files that
    # are inside the transfer set.  If rsync updates a file  that  has
    # extra  hard-link connections to files outside the transfer, that
    # linkage will be broken.  If you are tempted to use the --inplace
    # option to avoid this breakage, be very careful that you know how
    # your files are being updated so that you  are  certain  that  no
    # unintended  changes  happen due to lingering hard links (and see
    # the --inplace option for more caveats).
    #
    # If incremental recursion is active (see --recursive), rsync  may
    # transfer a missing hard-linked file before it finds that another
    # link for that contents exists elsewhere in the hierarchy.   This
    # does  not  affect the accuracy of the transfer (i.e. which files
    # are hard-linked together), just its efficiency (i.e. copying the
    # data for a new, early copy of a hard-linked file that could have
    # been found later in  the  transfer  in  another  member  of  the
    # hard-linked  set  of files).  One way to avoid this inefficiency
    # is to disable incremental recursion using the --no-inc-recursive
    # option.
    rsync -aqHu --delete --stats --log-file=$LOG --log-file-format="%n" $file $DESTINATION
    # make use of "$ du" command
    # du -s $file | awk '{ print $2 "is " $1 }'
    echo "$date : $file successfully cloned." >>$LOG
  fi
done  


# After cloning, fstab is going to mount the wrong partition
# Use the fstabClone for the corrected mounts.
if [ ! -e "$FSTABCLONE" ]       # Check if file exists.
then
  echo "$date : $FSTABCLONE does not exist and has not been copied." >>$LOG
else
  rm $DESTINATION$FSTAB
  cp -Rp $FSTABCLONE "$DESTINATION$FSTAB"
  echo "$date : /etc/fstab corrected for the clone." >>$LOG
fi


# After cloning ac opy of grub needs to be removed
rm -rf $FOLDERTOREMOVE
echo "$date : grub has been cleaned up for the clone." >>$LOG
echo "$date : " >>$LOG


echo "$date : System cloning is complete." >>$LOG
echo "$date : " >>$LOG


exit 0


##############################################################################


Here are the /etc/fstab and /etc/fstabClone files, respectively.


##############################################################################


# /etc/fstab: static file system information
#
# <file system> <dir> <type> <options> <dump> <pass>
tmpfs /tmp tmpfs nodev,nosuid 0 0
#
LABEL=htpcHome /home ext4 defaults 0 1
LABEL=ssdRoot / ext4 defaults 0 1
LABEL=ssdRootClone /mnt/ssdRootClone ext4 defaults 0 1
LABEL=slackRoot /mnt/slackware ext4 defaults 0 1
LABEL=grubPartition /boot/grub ext4 defaults 0 1
#LABEL=ssdSwap swap swap defaults 0 0
/dev/sda2 swap swap defaults 0 0
LABEL=mythGlenelg /home/david/MythTV/Glenelg xfs defaults,noatime,nodiratime,nosuid,nodev,allocsize=64m 0 1
LABEL=mythSturt /home/david/MythTV/Sturt xfs defaults,noatime,nodiratime,nosuid,nodev,allocsize=64m 0 1
# NFS client
#david-desktop:/ /mnt nfs4 rw,hard,intr,bg 0 0
#david-desktop:/music /home/david/Music nfs4 rw,hard,intr,bg 0 0
#david-desktop:/movies /home/david/Movies nfs4 rw,hard,intr,bg 0 0
# Samba
//david-desktop/movies /home/david/Movies cifs noatime,nodiratime,bg,username=david,password=david 0 0
//david-desktop/music /home/david/Music cifs noatime,nodiratime,bg,username=david,password=david 0 0
//david-desktop/tvseries        /home/david/TVseries    cifs    noatime,nodiratime,bg,username=david,password=david        0 0
##############################################################################

and 

##############################################################################

# /etc/fstab: static file system information
#
# <file system> <dir> <type> <options> <dump> <pass>
tmpfs /tmp tmpfs nodev,nosuid 0 0
#
LABEL=htpcHome /home ext4 defaults 0 1
LABEL=ssdRoot /mnt/ssdRoot ext4 defaults 0 1
LABEL=ssdRootClone / ext4 defaults 0 1
LABEL=slackRoot         /mnt/slackware  ext4    defaults        0 1
LABEL=grubPartition /boot/grub ext4 defaults 0 1
#LABEL=ssdSwap swap swap defaults 0 0
/dev/sda2 swap swap defaults 0 0
LABEL=mythGlenelg /home/david/MythTV/Glenelg xfs defaults 0 1
LABEL=mythSturt /home/david/MythTV/Sturt xfs defaults 0 1
# NFS client
#david-desktop:/ /mnt nfs4 rw,hard,intr,bg 0 0
#david-desktop:/music /home/david/Music nfs4 rw,hard,intr,bg 0 0
#david-desktop:/movies /home/david/Movies nfs4 rw,hard,intr,bg 0 0
# Samba
//david-desktop/movies /home/david/Movies cifs noatime,bg,username=david,password=david 0 0
//david-desktop/music /home/david/Music cifs noatime,bg,username=david,password=david 0 0
//david-desktop/tvseries        /home/david/TVseries    cifs    noatime,bg,username=david,password=david        0 0
##############################################################################


Monday, March 12, 2012

High quality user job transcode script for MythTV using Handbrake

A high quality user job script for MythTV that transcodes a recording to .mkv format and gives the resulting filename a human readable name.

#!/usr/bin/perl -w
# ============================================================================
# = NAME 
# x264_transcode_high.pl
#
# = PURPOSE
# Convert mpeg2 file from myth to h264 with aac audio.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_high.pl -j %JOBID% 
x264_transcode_high.pl -f %FILE% 
';

# ============================================================================

use strict;
use MythTV;
use XML::Simple;

# What file are we copying/transcoding?
my $file  = '';
my $jobid = -1;

# do nothing?
my $noexec = 0;

# extra console output?
my $DEBUG = 1;

# some globals
my ($chanid, $command, $query, $ref, $starttime, $showtitle, $episodetitle);
my ($seasonnumber, $episodenumber, $episodedetails);
my ($newfilename, $newstarttime);
my $xmlparser = new XML::Simple;
my $xmlstring;
# globals for stream and resolution mapping
my ($output, $videostream, $audiostreamsurround, $audiostreamstereo, $framerate);

# transcode options
my $videocodec = "x264";
my $videoquality = "22"; # target quality
my $videobitrate = "1500k"; # target bitrate - not used if using videoquality
my $audiostream = 1; # default audio channel
my $audiocodec = "copy:ac3";
my $audiobitrate = "256";
my $audiochannels = "auto"; 
my $audiosamplerate = "Auto";
my $audiodrc = "1.0";
my $ftype = "mkv";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $looseanamorphic = "--loose-anamorphic";
my $chapters = "-m";

# x264 options
my $badapt = "b-adapt=2";
my $rclookahead = "rc-lookahead=50";
my $x264options = "$badapt:$rclookahead";
# ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0

my $mt = '';
my $db = '';

sub Reconnect()
{
    $mt = new MythTV();
    $db = $mt->{'dbh'};
}

# ============================================================================
sub Die($)
{
    print STDERR "@_\n";
    exit -1;
}
# ============================================================================
# Parse command-line arguments, check there is something to do:
#
if ( ! @ARGV )
{   Die "$usage"  }
Reconnect;

while ( @ARGV && $ARGV[0] =~ m/^-/ )
{
    my $arg = shift @ARGV;

    if ( $arg eq '-d' || $arg eq '--debug' )
    {   $DEBUG = 1  }
    elsif ( $arg eq '-n' || $arg eq '--noaction' )
    {   $noexec = 1  }
    elsif ( $arg eq '-j' || $arg eq '--jobid' )
    {   $jobid = shift @ARGV  }
    elsif ( $arg eq '-f' || $arg eq '--file' )
    {   $file = shift @ARGV  }
    else
    {
        unshift @ARGV, $arg;
        last;
    }
}

if ( ! $file && $jobid == -1 )
{
    Die "No file or job specified. $usage";
}

# ============================================================================
# If we were supplied a jobid, lookup chanid
# and starttime so that we can find the filename
#
if ( $jobid != -1 )
{
    $query = $db->prepare("SELECT chanid, starttime " .
                          "FROM jobqueue WHERE id=$jobid;");
    $query->execute || Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

    if ( ! $chanid || ! $starttime )
    {   Die "Cannot find details for job $jobid"  }

    $query = $db->prepare("SELECT basename FROM recorded " .
                          "WHERE chanid=$chanid AND starttime='$starttime';");
    $query->execute || Die "Unable to query recorded table";
    ($file) = $query->fetchrow_array;
    $query->finish;

    if ( ! $file )
    {   Die "Cannot find recording for chan $chanid, starttime $starttime"  }

    if ( $DEBUG )
    {
        print "Job $jobid refers to recording chanid=$chanid,",
              " starttime=$starttime\n"
    }
}
else
{
    if ( $file =~ m/(\d+)_(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ )
    {   $chanid = $1, $starttime = "$2-$3-$4 $5:$6:$7"  }
    else
    {
        print "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   Die "Cannot find details for filename $file"  }
    }
}


# A commonly used SQL row selector:
my $whereChanAndStarttime = "WHERE chanid=$chanid AND starttime='$starttime'";

# ============================================================================
# Find the directory that contains the recordings, check the file exists
#
my $dir  = undef;
my $dirs = $mt->{'video_dirs'};

foreach my $d ( @$dirs )
{
 if ( ! -e $d )
 {   Die "Cannot find directory $dir that contains recordings"  }

 if ( -e "$d/$file" )
 {
  $dir = $d;
  last
 }
 else
 {   print "$d/$file does not exist\n"   }
}

if ( ! $dir )
{   Die "Cannot find recording"  }

# ============================================================================
# Do you want to implement the deinterlace feature?
#my $dowedeinterlace = " ";
# ============================================================================
# Use ffmpeg to find if we need to deinterlace
my $deinterlace = " ";
#if ( $dowedeinterlace == '1')
# {
# $command = "ffmpeg -i $dir/$file ";
# open(FF_info, "$command 2>&1 |");
# while ( defined(my $line = ) ) {
#  chomp($line);
#  if ( $line =~ /^\s*Stream.*#(\S\.\S).*:\sVideo.*\s(\S*)\stbr/ )
#  {
#   $framerate = $2;
#   next;
#  }
# }
# if ( $framerate <= 30.0 ) { $deinterlace = "--deinterlace" } elsif ( $framerate > 30 && $framerate <= 60 ) { $deinterlace = "" }
#}

# ============================================================================
# First, generate a new filename,
#

$query = $db->prepare("SELECT title FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$showtitle = $query->fetchrow_array;
$query->finish;

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$episodetitle = $query->fetchrow_array;
$query->finish;

if ( $episodetitle ne "" ) 
{
  $seasonnumber = "";
  $episodenumber = "";
  $xmlstring = `/usr/share/mythtv/metadata/Television/ttvdb.py -N "$showtitle" "$episodetitle"`;
  if ( $xmlstring ne "" ) {
    $episodedetails =$xmlparser->XMLin($xmlstring);
    $seasonnumber = $episodedetails->{item}->{season};
    $episodenumber = $episodedetails->{item}->{episode};
  }
}
my ($year,$month,$day,$hour,$mins,$secs) = split m/[- :]/, $starttime;
my $oldShortTime = sprintf "%04d%02d%02d",
                   $year, $month, $day;
my $iter = 0;

do {
  if ( $episodetitle eq "" || $seasonnumber eq "" || $episodenumber eq "" )
  {
    $newfilename = sprintf "%s_%s.%s.%s", $showtitle, $month, $day, $year;
  } else {
    $newfilename = sprintf "%s_S%0sE%0s_%s", $showtitle, $seasonnumber, $episodenumber, $episodetitle;
  }
  $newfilename =~ s/\;/   AND   /g;
  $newfilename =~ s/\&/   AND   /g;
  $newfilename =~ s/\s+/ /g;
  $newfilename =~ s/\s/_/g;
  $newfilename =~ s/:/_/g;
  $newfilename =~ s/__/_/g;
  $newfilename =~ s/\(//g;
  $newfilename =~ s/\)//g;
  $newfilename =~ s/'//g;
  $newfilename =~ s/\!//g;
  $newfilename =~ s/\///g;
  if ( $iter != "0" ) 
  {  $newfilename = sprintf "%s_%d%s", $newfilename, $iter, ".mkv"  } else { $newfilename = sprintf "%s%s", $newfilename, ".mkv" }
  $iter ++;
  $secs = $secs + $iter;
  $newstarttime = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
                    $year, $month, $day, $hour, $mins, $secs;
} while  ( -e "$dir/$newfilename" );

$DEBUG && print "$dir/$newfilename seems unique\n";


# ============================================================================
# Third, do the transcode
#
# $audiochannels = 6;
# $audiostream = $audiostreamsurround;
# if ( $audiostreamsurround eq "" )
# {
#   $audiochannels = 2;
#   $audiostream = $audiostreamstereo;
# } 

$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command -e $videocodec";
$command = "$command -q $videoquality";
$command = "$command -a $audiostream";
$command = "$command -E $audiocodec";
$command = "$command -B $audiobitrate";
$command = "$command -6 $audiochannels";
$command = "$command -R $audiosamplerate";
$command = "$command -D $audiodrc";
$command = "$command -f $ftype";
$command = "$command $detelecine";
$command = "$command $decomb";
$command = "$command $looseanamorphic";
$command = "$command $chapters";
$command = "$command $deinterlace";
$command = "$command -x $x264options";

$DEBUG && print "Executing: $command\n";

chdir $dir;
system $command;

if ( ! -e "$dir/$newfilename" )
{   Die "Transcode failed\n"  }


# ============================================================================
# Last, copy the existing recorded details with the new file name.
#
Reconnect;
$query = $db->prepare("SELECT * FROM recorded $whereChanAndStarttime;");
$query->execute ||  Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    print 'Old file size = ' . (-s "$dir/$file")        . "\n";
    print 'New file size = ' . (-s "$dir/$newfilename") . "\n";
}
$ref->{'filesize'}  = -s "$dir/$newfilename";

my $extra = 'Copy';


#
# The new recording file has no cutlist, so we don't insert that field
#
my @recKeys = grep(!/^cutlist$/, keys %$ref);

#
# Build up the SQL insert command:
#
$command = 'INSERT INTO recorded (' . join(',', @recKeys) . ') VALUES ("';
foreach my $key ( @recKeys )
{
    if (defined $ref->{$key})
    {   $command .= quotemeta($ref->{$key}) . '","'   }
    else
    {   chop $command; $command .= 'NULL,"'   }
}

chop $command; chop $command;  # remove trailing comma quote

$command .= ');';

if ( $DEBUG || $noexec )
{   print "# $command\n"  }

if ( ! $noexec )
{   $db->do($command)  || Die "Couldn't create new recording's record, but transcoded file exists $newfilename\n"   }

# Delete the old recording, keeping the new transcoded recording
if ( -e "$dir/$newfilename" )
{   $command = 'DELETE from recorded ' . join(',', $whereChanAndStarttime) . '; '; # Build up the SQL delete command:
    $db->do($command); # remove the mysql entry for the recording
    my $filepng = $file . join(',','.png'); # Create the file png filename
    unlink($file); # remove the original file
    unlink($filepng); # remove the file's png
}

# ============================================================================

$db->disconnect;
1;

Normal Transcode user job script for MythTV using Handbrake

This is a script that uses Handbrake and can be run from a User Job to transcode a recording to .mkv giving the resulting file a human readable filename.
#!/usr/bin/perl -w
# ============================================================================
# = NAME 
# x264_transcode_high.pl
#
# = PURPOSE
# Convert mpeg2 file from myth to h264 with aac audio.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_normal.pl -j %JOBID% 
x264_transcode_normal.pl -f %FILE% 
';

# ============================================================================

use strict;
use MythTV;
use XML::Simple;

# What file are we copying/transcoding?
my $file  = '';
my $jobid = -1;

# do nothing?
my $noexec = 0;

# extra console output?
my $DEBUG = 1;

# some globals
my ($chanid, $command, $query, $ref, $starttime, $showtitle, $episodetitle);
my ($seasonnumber, $episodenumber, $episodedetails);
my ($newfilename, $newstarttime);
my $xmlparser = new XML::Simple;
my $xmlstring;
# globals for stream and resolution mapping
my ($output, $videostream, $audiostreamsurround, $audiostreamstereo, $framerate);

# transcode options
my $videocodec = "x264";
my $videoquality = "22"; # target quality
my $audiostream = 1; # default audio channel
my $audiocodec = "copy:ac3";
my $audiobitrate = "160";
my $audiochannels = "auto"; 
my $audiosamplerate = "Auto";
my $audiodrc = "0.0";
my $ftype = "mkv";
my $anamorphic = "--strict-anamorphic";
my $chapters = "-m";
my $deinterlace = "--deinterlace";

# long word options - type them here
my $wordoptions = "$anamorphic";

# x264 options
my $refoption = "ref=2";
my $bframeoption = "bframes=2";
my $submeoption = "subme=6";
my $mixedrefsoption = "mixed-refs=0";
my $weightboption = "weightb=0";
my $eightbyeightoption = "8x8dct=0";
my $trellisoption = "trellis=0";
my $x264options = "$refoption:$bframeoption:$submeoption:$mixedrefsoption:$weightboption:$eightbyeightoption:$trellisoption";

my $mt = '';
my $db = '';

sub Reconnect()
{
    $mt = new MythTV();
    $db = $mt->{'dbh'};
}

# ============================================================================
sub Die($)
{
    print STDERR "@_\n";
    exit -1;
}
# ============================================================================
# Parse command-line arguments, check there is something to do:
#
if ( ! @ARGV )
{   Die "$usage"  }
Reconnect;

while ( @ARGV && $ARGV[0] =~ m/^-/ )
{
    my $arg = shift @ARGV;

    if ( $arg eq '-d' || $arg eq '--debug' )
    {   $DEBUG = 1  }
    elsif ( $arg eq '-n' || $arg eq '--noaction' )
    {   $noexec = 1  }
    elsif ( $arg eq '-j' || $arg eq '--jobid' )
    {   $jobid = shift @ARGV  }
    elsif ( $arg eq '-f' || $arg eq '--file' )
    {   $file = shift @ARGV  }
    else
    {
        unshift @ARGV, $arg;
        last;
    }
}

if ( ! $file && $jobid == -1 )
{
    Die "No file or job specified. $usage";
}

# ============================================================================
# If we were supplied a jobid, lookup chanid
# and starttime so that we can find the filename
#
if ( $jobid != -1 )
{
    $query = $db->prepare("SELECT chanid, starttime " .
                          "FROM jobqueue WHERE id=$jobid;");
    $query->execute || Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

    if ( ! $chanid || ! $starttime )
    {   Die "Cannot find details for job $jobid"  }

    $query = $db->prepare("SELECT basename FROM recorded " .
                          "WHERE chanid=$chanid AND starttime='$starttime';");
    $query->execute || Die "Unable to query recorded table";
    ($file) = $query->fetchrow_array;
    $query->finish;

    if ( ! $file )
    {   Die "Cannot find recording for chan $chanid, starttime $starttime"  }

    if ( $DEBUG )
    {
        print "Job $jobid refers to recording chanid=$chanid,",
              " starttime=$starttime\n"
    }
}
else
{
    if ( $file =~ m/(\d+)_(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ )
    {   $chanid = $1, $starttime = "$2-$3-$4 $5:$6:$7"  }
    else
    {
        print "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   Die "Cannot find details for filename $file"  }
    }
}


# A commonly used SQL row selector:
my $whereChanAndStarttime = "WHERE chanid=$chanid AND starttime='$starttime'";

# ============================================================================
# Find the directory that contains the recordings, check the file exists
#
my $dir  = undef;
my $dirs = $mt->{'video_dirs'};

foreach my $d ( @$dirs )
{
 if ( ! -e $d )
 {   Die "Cannot find directory $dir that contains recordings"  }

 if ( -e "$d/$file" )
 {
  $dir = $d;
  last
 }
 else
 {   print "$d/$file does not exist\n"   }
}

if ( ! $dir )
{   Die "Cannot find recording"  }

# ============================================================================
# Do you want to implement the deinterlace feature?
my $dowedeinterlace = "false";
# ============================================================================
# Use ffmpeg to find if we need to deinterlace
# my $deinterlace = " ";
if ( $dowedeinterlace == "true")
 {
 $command = "ffmpeg -i $dir/$file ";
 open(FF_info, "$command 2>&1 |");
 while ( defined(my $line = ) ) {
  chomp($line);
  if ( $line =~ /^\s*Stream.*#(\S\.\S).*:\sVideo.*\s(\S*)\stbr/ )
  {
   $framerate = $2;
   next;
  }
 }
 if ( $framerate <= 30.0 ) { $deinterlace = "--deinterlace" } elsif ( $framerate > 30 && $framerate <= 60 ) { $deinterlace = "" }
}

# ============================================================================
# First, generate a new filename,
#

$query = $db->prepare("SELECT title FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$showtitle = $query->fetchrow_array;
$query->finish;

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$episodetitle = $query->fetchrow_array;
$query->finish;

if ( $episodetitle ne "" ) 
{
  $seasonnumber = "";
  $episodenumber = "";
  $xmlstring = `/usr/share/mythtv/metadata/Television/ttvdb.py -N "$showtitle" "$episodetitle"`;
  if ( $xmlstring ne "" ) {
    $episodedetails =$xmlparser->XMLin($xmlstring);
    $seasonnumber = $episodedetails->{item}->{season};
    $episodenumber = $episodedetails->{item}->{episode};
  }
}
my ($year,$month,$day,$hour,$mins,$secs) = split m/[- :]/, $starttime;
my $oldShortTime = sprintf "%04d%02d%02d",
                   $year, $month, $day;
my $iter = 0;

do {
  if ( $episodetitle eq "" || $seasonnumber eq "" || $episodenumber eq "" )
  {
    $newfilename = sprintf "%s_%s.%s.%s", $showtitle, $month, $day, $year;
  } else {
    $newfilename = sprintf "%s_S%0sE%0s_%s", $showtitle, $seasonnumber, $episodenumber, $episodetitle;
  }
  $newfilename =~ s/\;/   AND   /g;
  $newfilename =~ s/\&/   AND   /g;
  $newfilename =~ s/\s+/ /g;
  $newfilename =~ s/\s/_/g;
  $newfilename =~ s/:/_/g;
  $newfilename =~ s/__/_/g;
  $newfilename =~ s/\(//g;
  $newfilename =~ s/\)//g;
  $newfilename =~ s/'//g;
  $newfilename =~ s/\!//g;
  $newfilename =~ s/\///g;
  if ( $iter != "0" ) 
  {  $newfilename = sprintf "%s_%d%s", $newfilename, $iter, ".mkv"  } else { $newfilename = sprintf "%s%s", $newfilename, ".mkv" }
  $iter ++;
  $secs = $secs + $iter;
  $newstarttime = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
                    $year, $month, $day, $hour, $mins, $secs;
} while  ( -e "$dir/$newfilename" );

$DEBUG && print "$dir/$newfilename seems unique\n";


# ============================================================================
# Third, do the transcode
#
# $audiochannels = 6;
# $audiostream = $audiostreamsurround;
# if ( $audiostreamsurround eq "" )
# {
#   $audiochannels = 2;
#   $audiostream = $audiostreamstereo;
# } 

$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command -e $videocodec";
$command = "$command -q $videoquality";
$command = "$command -a $audiostream";
$command = "$command -E $audiocodec";
$command = "$command -B $audiobitrate";
$command = "$command -6 $audiochannels";
$command = "$command -R $audiosamplerate";
$command = "$command -D $audiodrc";
$command = "$command -f $ftype";
$command = "$command $wordoptions";
$command = "$command $chapters";
$command = "$command $deinterlace";
$command = "$command -x $x264options";

$DEBUG && print "Executing: $command\n";

chdir $dir;
system $command;

if ( ! -e "$dir/$newfilename" )
{   Die "Transcode failed\n"  }


# ============================================================================
# Last, copy the existing recorded details with the new file name.
#
Reconnect;
$query = $db->prepare("SELECT * FROM recorded $whereChanAndStarttime;");
$query->execute ||  Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    print 'Old file size = ' . (-s "$dir/$file")        . "\n";
    print 'New file size = ' . (-s "$dir/$newfilename") . "\n";
}
$ref->{'filesize'}  = -s "$dir/$newfilename";

my $extra = 'Copy';


#
# The new recording file has no cutlist, so we don't insert that field
#
my @recKeys = grep(!/^cutlist$/, keys %$ref);

#
# Build up the SQL insert command:
#
$command = 'INSERT INTO recorded (' . join(',', @recKeys) . ') VALUES ("';
foreach my $key ( @recKeys )
{
    if (defined $ref->{$key})
    {   $command .= quotemeta($ref->{$key}) . '","'   }
    else
    {   chop $command; $command .= 'NULL,"'   }
}

chop $command; chop $command;  # remove trailing comma quote

$command .= ');';

if ( $DEBUG || $noexec )
{   print "# $command\n"  }

if ( ! $noexec )
{   $db->do($command)  || Die "Couldn't create new recording's record, but transcoded file exists $newfilename\n"   }

# Delete the old recording, keeping the new transcoded recording
if ( -e "$dir/$newfilename" )
{   $command = 'DELETE from recorded ' . join(',', $whereChanAndStarttime) . '; '; # Build up the SQL delete command:
    $db->do($command); # remove the mysql entry for the recording
    my $filepng = $file . join(',','.png'); # Create the file png filename
    unlink($file); # remove the original file
    unlink($filepng); # remove the file's png
}

# ============================================================================

$db->disconnect;
1;

Efficient Transcode script for MythTV using Handbrake

Efficient Transcode - transcodes to .mkv format, giving an improved human readable filename.

#!/usr/bin/perl -w
# ============================================================================
# = NAME 
# x264_transcode_efficient.pl
#
# = PURPOSE
# Convert mpeg2 file from myth to h264 with aac audio.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_efficient.pl -j %JOBID% 
x264_transcode_efficient.pl -f %FILE% 
';

# ============================================================================

use strict;
use MythTV;
use XML::Simple;

# What file are we copying/transcoding?
my $file  = '';
my $jobid = -1;

# do nothing?
my $noexec = 0;

# extra console output?
my $DEBUG = 1;

# some globals
my ($chanid, $command, $query, $ref, $starttime, $showtitle, $episodetitle);
my ($seasonnumber, $episodenumber, $episodedetails);
my ($newfilename, $newstarttime);
my $xmlparser = new XML::Simple;
my $xmlstring;
# globals for stream and resolution mapping
my ($videostream, $audiostreamsurround, $audiostreamstereo, $framerate);

# Set your desired output directory
# my $outputdir = "/home/david/tmp";

# transcode options
my $videocodec = "x264";
my $videoquality = "24"; # target quality
my $audiostream = 1; # default audio channel
my $audiocodec = "copy:ac3";
my $audiobitrate = "160";
my $audiochannels = "auto"; 
my $audiosamplerate = "Auto";
my $audiodrc = "0.0";
my $ftype = "mkv";
my $anamorphic = "--strict-anamorphic";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $deinterlace = "--deinterlace";
my $chapters = "-m";

# long word options - type them here
# 
# my $wordoptions = "$anamorphic $decomb $detelecine $deinterlace";
my $wordoptions = "$anamorphic $deinterlace";

# x264 options
my $refoption = "ref=1";
my $bframeoption = "bframes=2";
my $submeoption = "subme=5";
my $mixedrefsoption = "mixed-refs=0";
my $weightboption = "weightb=0";
my $eightbyeightoption = "8x8dct=0";
my $trellisoption = "trellis=0";
my $entropyoption = "cabac=0";
my $partitionoption = "partitions=i4x4,i8x8";
my $motionoption = "me=dia";
my $x264options = "$refoption:$bframeoption:$submeoption:$mixedrefsoption:$weightboption:$eightbyeightoption:$trellisoption:$entropyoption:$partitionoption:$motionoption";

my $mt = '';
my $db = '';

sub Reconnect()
{
    $mt = new MythTV();
    $db = $mt->{'dbh'};
}

# ============================================================================
sub Die($)
{
    print STDERR "@_\n";
    exit -1;
}
# ============================================================================
# Parse command-line arguments, check there is something to do:
#
if ( ! @ARGV )
{   Die "$usage"  }
Reconnect;

while ( @ARGV && $ARGV[0] =~ m/^-/ )
{
    my $arg = shift @ARGV;

    if ( $arg eq '-d' || $arg eq '--debug' )
    {   $DEBUG = 1  }
    elsif ( $arg eq '-n' || $arg eq '--noaction' )
    {   $noexec = 1  }
    elsif ( $arg eq '-j' || $arg eq '--jobid' )
    {   $jobid = shift @ARGV  }
    elsif ( $arg eq '-f' || $arg eq '--file' )
    {   $file = shift @ARGV  }
    else
    {
        unshift @ARGV, $arg;
        last;
    }
}

if ( ! $file && $jobid == -1 )
{
    Die "No file or job specified. $usage";
}

# ============================================================================
# If we were supplied a jobid, lookup chanid
# and starttime so that we can find the filename
#
if ( $jobid != -1 )
{
    $query = $db->prepare("SELECT chanid, starttime " .
                          "FROM jobqueue WHERE id=$jobid;");
    $query->execute || Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

    if ( ! $chanid || ! $starttime )
    {   Die "Cannot find details for job $jobid"  }

    $query = $db->prepare("SELECT basename FROM recorded " .
                          "WHERE chanid=$chanid AND starttime='$starttime';");
    $query->execute || Die "Unable to query recorded table";
    ($file) = $query->fetchrow_array;
    $query->finish;

    if ( ! $file )
    {   Die "Cannot find recording for chan $chanid, starttime $starttime"  }

    if ( $DEBUG )
    {
        print "Job $jobid refers to recording chanid=$chanid,",
              " starttime=$starttime\n"
    }
}
else
{
    if ( $file =~ m/(\d+)_(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ )
    {   $chanid = $1, $starttime = "$2-$3-$4 $5:$6:$7"  }
    else
    {
        print "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   Die "Cannot find details for filename $file"  }
    }
}


# A commonly used SQL row selector:
my $whereChanAndStarttime = "WHERE chanid=$chanid AND starttime='$starttime'";

# ============================================================================
# Find the directory that contains the recordings, check the file exists
#
my $dir  = undef;
my $dirs = $mt->{'video_dirs'};

foreach my $d ( @$dirs )
{
 if ( ! -e $d )
 {   Die "Cannot find directory $dir that contains recordings"  }

 if ( -e "$d/$file" )
 {
  $dir = $d;
  last
 }
 else
 {   print "$d/$file does not exist\n"   }
}

if ( ! $dir )
{   Die "Cannot find recording"  }

# ============================================================================
# Do you want to implement the deinterlace feature?
#my $dowedeinterlace = "";
# ============================================================================
# Use ffmpeg to find if we need to deinterlace
#my $deinterlace = "";
#if ( $dowedeinterlace == '1')
# {
# $command = "ffmpeg -i $dir/$file ";
# open(FF_info, "$command 2>&1 |");
# while ( defined(my $line = ) ) {
#  chomp($line);
#  if ( $line =~ /^\s*Stream.*#(\S\.\S).*:\sVideo.*\s(\S*)\stbr/ )
#  {
#   $framerate = $2;
#   next;
#  }
# }
# if ( $framerate <= 30.0 ) { $deinterlace = "--deinterlace" } elsif ( $framerate > 30 && $framerate <= 60 ) { $deinterlace = "" }
#}

# ============================================================================
# First, generate a new filename,
#

$query = $db->prepare("SELECT title FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$showtitle = $query->fetchrow_array;
$query->finish;

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$episodetitle = $query->fetchrow_array;
$query->finish;

if ( $episodetitle ne "" ) 
{
  $seasonnumber = "";
  $episodenumber = "";
  $xmlstring = `/usr/share/mythtv/metadata/Television/ttvdb.py -N "$showtitle" "$episodetitle"`;
  if ( $xmlstring ne "" ) {
    $episodedetails =$xmlparser->XMLin($xmlstring);
    $seasonnumber = $episodedetails->{item}->{season};
    $episodenumber = $episodedetails->{item}->{episode};
  }
}
my ($year,$month,$day,$hour,$mins,$secs) = split m/[- :]/, $starttime;
my $oldShortTime = sprintf "%04d%02d%02d",
                   $year, $month, $day;
my $iter = 0;

do {
  if ( $episodetitle eq "" || $seasonnumber eq "" || $episodenumber eq "" )
  {
    $newfilename = sprintf "%s_%s.%s.%s", $showtitle, $month, $day, $year;
  } else {
    $newfilename = sprintf "%s_S%0sE%0s_%s", $showtitle, $seasonnumber, $episodenumber, $episodetitle;
  }
  $newfilename =~ s/\;/   AND   /g;
  $newfilename =~ s/\&/   AND   /g;
  $newfilename =~ s/\s+/ /g;
  $newfilename =~ s/\s/_/g;
  $newfilename =~ s/:/_/g;
  $newfilename =~ s/__/_/g;
  $newfilename =~ s/\(//g;
  $newfilename =~ s/\)//g;
  $newfilename =~ s/'//g;
  $newfilename =~ s/\!//g;
  $newfilename =~ s/\///g;
  if ( $iter != "0" ) 
  {  $newfilename = sprintf "%s_%d%s", $newfilename, $iter, ".mkv"  } else { $newfilename = sprintf "%s%s", $newfilename, ".mkv" }
  $iter ++;
  $secs = $secs + $iter;
  $newstarttime = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
                    $year, $month, $day, $hour, $mins, $secs;
} while  ( -e "$dir/$newfilename" );

$DEBUG && print "$dir/$newfilename seems unique\n";


# ============================================================================
# Third, do the transcode
#
# $audiochannels = 6;
# $audiostream = $audiostreamsurround;
# if ( $audiostreamsurround eq "" )
# {
#   $audiochannels = 2;
#   $audiostream = $audiostreamstereo;
# } 

$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command -e $videocodec";
$command = "$command -q $videoquality";
$command = "$command -a $audiostream";
$command = "$command -E $audiocodec";
$command = "$command -B $audiobitrate";
$command = "$command -6 $audiochannels";
$command = "$command -R $audiosamplerate";
$command = "$command -D $audiodrc";
$command = "$command -f $ftype";
$command = "$command $wordoptions";
$command = "$command $chapters";
$command = "$command $deinterlace";
$command = "$command -x $x264options";

$DEBUG && print "Executing: $command\n";

chdir $dir;
system $command;

if ( ! -e "$dir/$newfilename" )
{   Die "Transcode failed\n"  }


# ============================================================================
# Last, copy the existing recorded details with the new file name.
#
Reconnect;
$query = $db->prepare("SELECT * FROM recorded $whereChanAndStarttime;");
$query->execute ||  Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    print 'Old file size = ' . (-s "$dir/$file")        . "\n";
    print 'New file size = ' . (-s "$dir/$newfilename") . "\n";
}
$ref->{'filesize'}  = -s "$dir/$newfilename";

my $extra = 'Copy';


#
# The new recording file has no cutlist, so we don't insert that field
#
my @recKeys = grep(!/^cutlist$/, keys %$ref);

#
# Build up the SQL insert command:
#
$command = 'INSERT INTO recorded (' . join(',', @recKeys) . ') VALUES ("';
foreach my $key ( @recKeys )
{
    if (defined $ref->{$key})
    {   $command .= quotemeta($ref->{$key}) . '","'   }
    else
    {   chop $command; $command .= 'NULL,"'   }
}

chop $command; chop $command;  # remove trailing comma quote

$command .= ');';

if ( $DEBUG || $noexec )
{   print "# $command\n"  }

if ( ! $noexec )
{   $db->do($command)  || Die "Couldn't create new recording's record, but transcoded file exists $newfilename\n"   }

# Delete the old recording, keeping the new transcoded recording
if ( -e "$dir/$newfilename" )
{   $command = 'DELETE from recorded ' . join(',', $whereChanAndStarttime) . '; '; # Build up the SQL delete command:
    $db->do($command); # remove the mysql entry for the recording
    my $filepng = $file . join(',','.png'); # Create the file png filename
    unlink($file); # remove the original file
    unlink($filepng); # remove the file's png
}

# ============================================================================

$db->disconnect;
1;

MythTV scripts used

mythbackup.sh (I don't use this one)
#Dumps the mythconverg database - daily backup
#Keeps the last 7 days
#!/bin/sh
LOG="/var/log/archmyth.log"
DAY=`/bin/date +%u`
DUMPFILE="~/.mythtv/mythbackup/mythdb_$DAY.sql"
DATE=`date`
echo "$DATE : Backuping up myth database to $DUMPFILE" >> $LOG
/usr/bin/mysqldump -u mythtv -pmythtv mythconverg -c > $DUMPFILE
DATE=`date`
echo "$DATE : Backup completed." >> $LOG
exit 0

mythidle.pl - courtesy of Arkay @ pcmediacenter.com.au
(be careful if cutting and pasting this, due to narrow width of the printout, some lines may have carried over to a new line, when they should not have)
Pre-shutdown-check command: /home/david/scripts/mythidle.pl
#!/usr/bin/perl -w
###############################################################################################################################
## Name: mythidle.pl
##
## Purpose: Checks to determine if the mythbackend server is idle and ready for shutdown.
## 
## (C)opyright 2008 Arksoft.
##                                    
## Author: Arkay
## 
## Ver 1.0: 14-07-2008. Initial version.
## 
###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################

use strict;    #Keeps code neat.
use Getopt::Std;   #Getopt module for option preprocessing.
use vars qw/ $opt_d $opt_h /;  #Option Processing vars.
use POSIX qw(strftime);   #Time routine we need.

###############################################################################################################################
# Prototype definitions
###############################################################################################################################

sub logmsg(@);  #Message logger so we can track what's been going on.
sub process_opts(); #Option processing.. Nothing exiting for this script.
sub do_command($); #Execute a shell command for lazy perl programmers :)
sub check_hosts(); #Are any server dependant hosts currently up?
sub check_procs(); #Check if the server is running anything that should keep us awake.
sub check_myth(); #Check if mythbackend is busy doing anything.

###############################################################################################################################
# Constant Definitions.
###############################################################################################################################

my ($TRUE) = 1;
my ($FALSE) = 0;

###############################################################################################################################
# Global vars, paths, commands to call.
###############################################################################################################################

my ($LOG) = "/var/log/archmyth.log";     #Log location.
my ($LOGSIZE) = 1024;       #Maximum log size in kbytes, self pruning.
my ($DEBUG) = $FALSE;       #Debugging default is off.
my ($BASENAME) = $0;       #How was the program called?

my ($MYTHSTATUS)="/usr/bin/mythshutdown --status;echo \$\?";  #Command to query myth status
my ($PING)="/bin/ping";       #Where is ping

###############################################################################################################################
# These are the only 2 lines in this file that should be edited.
# The first (@procs) is a list of processes that when running should keep the server awake.
# The second (@HOSTS) lists hosts that, if active, prevents the server from sleeping.
# To acertain the name of a process to see if it still running use ps -ef | grep -i 
# The names of the hosts listed below need to exist in your /etc/hosts file. You can check 
# that they are up with ping 
# Both lines consist of a comma separate list of quoted strings. i.e.
#my (@PROCS)=("dpexpress","ktorrent","shepherd","altbinz");  #Stay awake if active.
#my (@HOSTS)=("debs","htpc-lounge","quadarch");    #stay awake if any of these are up
###############################################################################################################################
my (@PROCS)=("shepherd","xbmc","chromium","vlc","smplayer","HandBrakeCLI"); #stay awake?
my (@HOSTS)=("myth-frontend");      #stay awake if any of these are up

###############################################################################################################################
# The Mainline.
###############################################################################################################################
MAIN:
{
 my ($blocked)=$FALSE;

 process_opts();

 logmsg "$BASENAME started : PID($$)";

 SWITCH: #blocked
 {
  if (check_hosts() != $FALSE) #Check it any client hosts are up
  {
   $blocked=$TRUE;
   last;
  }

  if (check_procs() != $FALSE) #Check if any processes are blocking shutdown
  {
   $blocked=$TRUE;
   last;
  }

  if (check_myth() != $FALSE) #Check if myth is busy
  {
   $blocked=$TRUE;
   last;
  }
  $blocked=$FALSE;
 }

 if ($blocked == $TRUE)
 {
  logmsg "Mythbackend is not idle - blocking Shutdown.";
 }
 else
 {
  logmsg "Mythbackend is currently idle - Allowing Shutdown.";
 }

 logmsg "$BASENAME Completed."; logmsg " ";
 exit($blocked);
}

###############################################################################################################################
# check_procs()
# Check if processes are running that should stop shutdown from occuring.
###############################################################################################################################
sub check_procs()
{
 my (@output);
 my ($proc);
 my ($command);
 my ($count)=0;
 my ($running)=$FALSE;

 logmsg "PROC  - Checking active processes that block shutdown.";

 foreach $proc (@PROCS)
 {
  $command="ps -ef | grep $proc | grep -v grep |wc -l"; 
  @output=do_command($command);

  if (@output)
  {
   $count=$output[0];
   chomp ($count);
  }

  if ($count > 0)
  {
   logmsg "PROC  - Found active process : $proc ($count running).";
   $running=$TRUE;
  }
 }
 logmsg "PROC  - No blocking processes currently running." if ($running) == $FALSE;
 return($running);
}

###############################################################################################################################
# check_myth()
# Check if we have active samba connections.
###############################################################################################################################
sub check_myth()
{
 my (@output);
 my ($status)=69;
 my ($text)="Unknown";

 logmsg "MYTH  - Checking mythbackend status.";
 @output=do_command($MYTHSTATUS);

 if (@output)
 {
  $status=$output[0]; chomp($status);

  $text="Idle."       if ($status) == 0;
  $text="Transcoding."      if ($status) == 1;
  $text="Flagging Commercials."     if ($status) == 2;
  $text="Grabbing EPG Data."     if ($status) == 4;
  $text="Recording."      if ($status) == 8;
  $text="Locked."      if ($status) == 16;
  $text="Jobs running/pending."     if ($status) == 32;
  $text="In a daily wakeup/shutdown period."   if ($status) == 64;
  $text="Less than 15 minutes to next wakeup period."  if ($status) == 128;
  $text="Setup is running."    if ($status) == 255;

  logmsg "MYTH  - Mythbackend status ($status) : $text";
 }
 else
 {
  logmsg "MYTH  - Failed to get mythbackend status.";
 }

 return($status);
}

###############################################################################################################################
# check_hosts()
# Checkif we have active samba connections.
###############################################################################################################################
sub check_hosts()
{
 my ($line);
 my ($host);
 my (@output,@ping);
 my ($up)=$FALSE;
 my ($command);

 logmsg "HOSTS - Checking for active client hosts.";

 foreach $host (@HOSTS)
 {
  $command="$PING -c2 $host | grep received | awk '{print \$4}'";
  
  @output=do_command($command);

  if ($output[0] != 0)
  {
   logmsg "HOSTS - Client host \"$host\" is still up.";
   $up=$TRUE;
   last;
  }
  else
  {
   logmsg "HOSTS - Client host \"$host\" is currently down.";
  }

 }
 logmsg "HOSTS - No active client hosts found." if ($up == $FALSE);

 return($up);
}


###############################################################################################################################
# logmsg
# Little routine to write to the log file.
# Rotates around $LOGSIZE bytes.
###############################################################################################################################
sub logmsg(@)
{ 
 my ($string)=@_;
 my $time=scalar localtime;
 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
 my (@lines,$line);

 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat("$LOG");

 if (defined($size))
 {
  $size=$size/1024;    #size in kbyte

  if ($size >= $LOGSIZE)
  {
   unlink ("$LOG.old") if (-e("$LOG.old"));
   rename ($LOG,"$LOG.old");
  }
 }

 print "$time : $string\n" if ($DEBUG==$TRUE);

 if (open (LOG,">>$LOG"))
 {
  if ($string =~ /\n/)
  {
   @lines=split(/\n/,$string);
   foreach $line (@lines)
   {
    print LOG "$time : $line\n"; 
   }
  }
  else
  {
    print LOG "$time : $string\n"; 
  }
  close (LOG);
 }
 else
 {
  print "Unable to open LOG $LOG : $!";
 }
}

###############################################################################################################################
# process_opts()
# Set Global option flags dependant on command line input.
###############################################################################################################################
sub process_opts()
{
 getopts('dh');

 $DEBUG=$TRUE if ($opt_d); 
 exit(usage(1)) if ($opt_h);
}

###############################################################################################################################
# usage()
# Output Relevant Usage strings if incorrect opts are given.
###############################################################################################################################
sub usage()
{
 my($ucode)=@_;

 if ($ucode == 1) 
 {
  print "Usage: $BASENAME [-dh]\n";
  return(0);
 }
}

###############################################################################################################################
# sub do_command($)
# use system call to execute command. Returns output of command in array.
###############################################################################################################################
sub do_command($)
{
    my ($command)=@_;
    my (@output);
    my ($exit_value)=0;

    logmsg "Executing $command" if ($DEBUG == $TRUE);

    @output=`$command`; 

    $exit_value = $? >> 8;

    if ($exit_value != 0)
    {
        logmsg "Error executing $command : $!";
    }
    return(@output);
}

setwakeup.sh
sudo sh -c "/home/david/scripts/setwakeup.sh $time"
#!/bin/sh
#$1 is the first argument to the script. It is the time in seconds since 1970
#this is defined in mythtv-setup with the time_t argument

LOG="/var/log/archmyth.log"
#Set the wakeup timers.
echo 0 > /sys/class/rtc/rtc0/wakealarm      #this clears your alarm.
echo $1 > /sys/class/rtc/rtc0/wakealarm     #this writes the alarm.
date=`date "+%a %b %e %H:%M:%S %Y"`
schedutc=`date -u -d @$1 +%F" "%T`
sched=`date -d @$1 +%F" "%T`
echo "$date : Next scheduled recording : $sched ($schedutc UTC)" >>$LOG
#cat /proc/driver/rtc | head -4 >>$LOG

shutdown.sh
sudo sh -c "/home/david/scripts/shutdown.sh"
#!/bin/sh

LOG="/var/log/archmyth.log"
date=`date "+%a %b %e %H:%M:%S %Y"`
echo "$date : System Shutting down." >>$LOG
/sbin/shutdown -h now