Friday, March 30, 2012

My /etc/fstab for Root partition and Root Clone partition

ssdRoot /etc/fstab




# /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=grubPartition /boot/grub ext4 defaults 0 1
LABEL=ssdSwap swap swap defaults 0 0
LABEL=mythMovies /home/david/MythTV/Glenelg 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

ssdRootClone /etc/fstab

# /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=grubPartition /boot/grub ext4 defaults 0 1
LABEL=ssdSwap swap swap defaults 0 0
LABEL=mythMovies /home/david/MythTV/Glenelg 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


Thursday, March 29, 2012

Move recordings from one MythTV folder to another - update the database

This script is built up from several transcode scripts and database scripts I have found elsewhere. 

It will take a recording from MythTV, give it a human readable filename, and then shift the recording to a new folder that you can specify. Finally, it will update the database to point to the new location.



#!/usr/bin/perl -w
# ============================================================================
# = NAME 
# move_myth_recordings.pl
#
# = PURPOSE
# Move recording from MythTV to a different folder, updating the database
# Hs the ability to change the filename to human readable
#
# = USAGE
my $usage = 'Usage:
move_myth_recordings.pl -j %JOBID%
move_myth_recordings.pl -f %FILE% 
';
# ============================================================================
use strict;
use MythTV;
use XML::Simple;
use File::Copy;


# 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;


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


# Set your desired output directory
# Make sure this folder is writable by your myth user
my $outputdir = "/home/david/Recordings/Sturt"; # Don't use a trailing slash


# ============================================================================
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"  }


# ============================================================================
# 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 "$outputdir/$newfilename" );


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




# ============================================================================
# Next, move the file.
#
chdir $dir;
copy($file, "$outputdir/$newfilename") or die "File cannot be copied.";
#move($file, "$outputdir/$newfilename") or die "File cannot be moved.";




# ============================================================================
# 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 "$outputdir/$newfilename") . "\n";
}
$ref->{'filesize'}  = -s "$outputdir/$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"   }


# ============================================================================
# Now, delete the old recording and png files, keeping the new transcoded recording
if ( -e "$outputdir/$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
    my $filepng2 = $file . join(',','.-1.100x75.png'); # Create the file png filename
    unlink($file); # remove the original file
    unlink($filepng); # remove the file's png
    unlink($filepng2); # remove the file's png
}


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


$db->disconnect;
1;


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

Wednesday, March 28, 2012

Waiting for boot device

Waiting 30 seconds for device /dev/archiso ...
ERROR: boot device didn't show up after 30 seconds ...
Falling back to interactive prompt
You can try to fix the problem manually, logout when you are finished
ramfs$

The solution for me was to type the following at the ramfs$ prompt

ramfs$ udevadm trigger
ramfs$ exit


Tuesday, March 27, 2012

When /var/lib/mysql gets really large

I followed the instructions given here


In short,


Add this line to your /etc/mysql/my.cnf and it'll need to be in the 
[mysqld] section. 

expire_logs_days = 7 

Then log into Mysql (# mysql -u root -p mysql) and run this command to set the variable without 

having to restart Mysql. 

SET GLOBAL expire_logs_days=7; 

Immediately expire the old logs:

PURGE BINARY LOGS BEFORE DATE_SUB( NOW( ), INTERVAL 7 DAY); 

Monday, March 12, 2012

XBMC configuration screenshots

Here is a screenshot of the settings used to get sound working in XBMC from the HDMI connection on my GPU (NVidia GT430).

To find the correct name for the card, I used the command

$aplay -l

**** List of PLAYBACK Hardware Devices ****
card 0: SB [HDA ATI SB], device 0: ALC889A Analog [ALC889A Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: SB [HDA ATI SB], device 1: ALC889A Digital [ALC889A Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 7: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 8: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 9: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
It was important for me to do it this way rather than simply a card number because these numbers can change on reboot.

Each card/device combination was tested using
$ aplay -D plughw:1,9 /usr/share/sounds/alsa/Front_Center.wav
until sound was heard. The important part in the above command is "plughw:1,9" which is the card number and device number from the ****List of PLAYBACK Hardware Devices****, respectively.

Once this was working, the card number can be replaced by the name of the card. For me, I replaced "1" with "Nvidia" to give the command
$ aplay -D plughw:NVidia,9 /usr/share/sounds/alsa/Front_Center.wav
I then used this information in my XBMC settings.

Notice the information is being used in the Custom audio device section.

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