Friday, February 1, 2013

Transcode 576i

 I have added a few  extras to the script, most notably a separate log for each transcode. Also, install mediainfo for a nice output at the end of the log.
###############################################################################################################################


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

###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################
#
use strict;
use MythTV;
use XML::Simple;

###############################################################################################################################
# User defined quantities. Change these parameters to suit your needs
###############################################################################################################################
#
# Transcode speed, quality and size
my $howfast             = "medium";     # Choices are none, ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
my $videoqualitynumber         = "23";        # Sensible ranges are between 20 - 25, lower values are higher quality, bigger filesize
my $videosource            = "576";    # Choices are 1080, 720, 576, Other
my $tuning            = "none";    # x264 tune options are none, film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency
my $presetprofile        = "none";    # x264 profiles are none, baseline, main, high, high10, high422, high444
my $audiochoice            = "aac";    # Choices are copy, aac, mp3
my $subtitles            = "none";    # Choices are none, english etc

###############################################################################################################################
# Prototype and Constant definitions
###############################################################################################################################
#
#Message logger so we can track what's been going on
sub logmsg(@);

# Define the Reconnect command here
sub Reconnect();

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

# do nothing?
my $noexec = 0;
# Booleans
my $true = 1;
my $false = 0;

###############################################################################################################################
# Logging
###############################################################################################################################
# Set up a tmp log to show initial errors
# $LOG will be changed once $newfilename is created
my $failLOG = "/var/log/transcode/all_576i.log";
my $LOG = $failLOG;
my $logsize = 4096;       #Maximum log size in kbytes, self pruning.
my $logdebug = $false;        #Debugging default is off.
my $logverbose = "-v 1";


###############################################################################################################################
# Transcoding
###############################################################################################################################
#
# 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 = '';
# globals for stream and resolution mapping
my ($videofilters,$framerate,$anamorphic,$displaysize,$x264speed,$x264tune,$x264profile,$deinterlace,$detelecine,$decomb,$denoise,$deblock);
my ($audiocodec,$audiobitrate,$audiochannels,$audiosamplerate,$audiodrc,$audiostream);
# transcode audio options
$audiostream = "-a 1";

if ( $audiochoice eq "copy" )
{
  $audiocodec = "-E copy:mp3";
  $audiobitrate = "";
  $audiochannels = "";
  $audiosamplerate = "";
  $audiodrc = "";
}
elsif ( $audiochoice eq "aac" )
{
  $audiocodec = "-E faac";
  $audiobitrate = "-B 256";
  $audiochannels = "-6 dpl2";
  $audiosamplerate = "-R Auto";
  $audiodrc = "-D 0.0";
}

elsif ( $audiochoice eq "mp3" )
{
  $audiocodec = "-E lame";
  $audiobitrate = "-B 128";
  $audiochannels = "-6 dpl2";
  $audiosamplerate = "-R Auto";
  $audiodrc = "-D 0.0";
}
# filters
$deinterlace = "--deinterlace=\"slower\"";
$detelecine = "--detelecine";
#$decomb = "--decomb=\"bob\"";
$decomb = "--decomb";
$denoise = "--denoise";

# strict or loose anamorphic?
if ( $videosource eq "1080" )
{
  $anamorphic = "--loose-anamorphic";
  $displaysize = "720";
}
elsif ( $videosource eq "720" )
{
  $anamorphic = "--strict-anamorphic";
  $displaysize = "720";
}
else
{
  $anamorphic = "--strict-anamorphic";
  $displaysize = "576";
}

# transcode video options
$videofilters = "$denoise $decomb";


my $videoquality = "-q $videoqualitynumber";
my $videorate = "--vfr";
my $transcodesize="-Y $displaysize";
my $videocodec = "-e x264";
my $ftype = "-f mkv";
my $chapters = "-m";

# Subtitle options
my $subtitleoptions;
if ( $subtitles eq "none")
{ $subtitleoptions = ""}
else
{ $subtitleoptions = "-s 1" }

# Assemble video options and audio options
my $videooptions = "$videocodec $videoquality $videorate $ftype $videofilters $anamorphic $transcodesize $chapters $subtitleoptions";
my $audiooptions = "$audiostream $audiocodec $audiochannels $audiobitrate $audiosamplerate";
my $transcodeoptions = "$videooptions $audiooptions";
# --x264-preset
if ( $howfast eq "none" )
{ $x264speed = "" }
else
{ $x264speed = "--x264-preset $howfast" }

# --x264-tune
if ( $tuning eq "none" )
{ $x264tune = "" }
else
{ $x264tune= "--x264-tune $tuning" }

# --x264-profile
if ( $presetprofile eq "none" )
{ $x264profile = "" }
else
{ $x264profile= "--x264-profile $presetprofile" }

# Assemble x264options
#my $x264options = "$x264speed $x264tune $x264profile -x ref=9:subme=9:me=umh:merange=32:trellis=2:rc-lookahead=80";
my $x264options = "$x264speed $x264tune $x264profile -x ref=5:rc-lookahead=60";

###############################################################################################################################
# HandBrakeCLI Builtin commands
###############################################################################################################################
# Default x264 options
# -x ref=3:bframes=3:b-pyramid=normal:weightp=2:8x8dct=1:cabac=1:b-adapt=1:me=hex:subme=7:mixed-refs=1:weightb=1:trellis=1:rc-lookahead=40:mbtree=1:analyse=p8x8,b8x8,i8x8,i4x4:aq-mode=1:direct=spatial
#
# Normal Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4  -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0
#
# High Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4  -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 --detelecine --decomb --loose-anamorphic -m -x b-adapt=2:rc-lookahead=50

# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my $medium = "-x ref=3:bframes=3:b-adapt=1:direct=spatial:me=hex:merange=16:subq=7:rc-lookahead=40:trellis=1";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";

###############################################################################################################################
# HandBrakeCLI tested commands
###############################################################################################################################
#
# HandBrakeCLI -i ?.mpg -o ?.mkv -e x264 -q 20 -r 25 --cfr -f mkv -m -Y 576 --decomb=9:2:6:20:80:16:16:10:20:20:4:2:50:24:1:-1  --loose-anamorphic -a 1 -E copy:*



###############################################################################################################################
# Kill
###############################################################################################################################
#
my $debug = 1;
sub Die($)
{
    print STDERR "@_\n";
    exit -1;
}
###############################################################################################################################
# Parse command-line arguments, check there is something to do:
###############################################################################################################################
#
if ( ! @ARGV )
{   logmsg && 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 )
{
    logmsg && 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 || logmsg && Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

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

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

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

    if ( $debug )
    {
        logmsg "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
    {
        logmsg "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || logmsg && Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   logmsg && 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 )
    {   logmsg && Die "Cannot find directory $dir that contains recordings"  }

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

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

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

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && 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, $day, $month, $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 && logmsg "$dir/$newfilename seems unique\n";

###############################################################################################################################
# Logging
###############################################################################################################################

# extra console output?
$LOG = "/var/log/transcode/transcode_$newfilename.log";

###############################################################################################################################
# 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 ($logdebug==$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 : $!";
    }
}

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


###############################################################################################################################
# Build and execute command
###############################################################################################################################
#
$command = "nice /usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $x264options";
$command = "$command $transcodeoptions";

###############################################################################################################################
# Execute the transcode command, and log the HandBrakeCLI output
###############################################################################################################################

chdir $dir;
# Open STDERR so that HandBrakeCLI outputs to the log file
open ( STDERR, ">>$LOG" );
select( STDERR );

# First log message
logmsg "###############################################################################################################################\n";
logmsg "x264_transcode_1080i.pl has been initiated\n";

$debug && logmsg "Executing: $command\n";

logmsg "Options used are:\n";
logmsg "Preset is $howfast";
logmsg "Profile is $presetprofile";
logmsg "Tune is $tuning";
logmsg "Constant Rate Factor is $videoqualitynumber";
logmsg "Audio track is $audiochoice";
logmsg "Video filters are set to $videofilters";

# Bang!
system $command;
# Used this to debug
# my $touchcommand = "touch $newfilename";
# $debug && logmsg "Executing: $touchcommand\n";
# system $touchcommand;

# Close STDERR and re-open STDOUT
close( STDERR );
select( STDOUT );

if ( ! -e "$dir/$newfilename" )
{   logmsg && 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 ||  logmsg && Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $debug && ! $noexec )
{
    logmsg 'Old file size = ' . (-s "$dir/$file")        . "\n";
    logmsg '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)  || logmsg && 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
    logmsg "Transcoding $newfilename has completed\n";
    logmsg "###############################################################################################################################\n";
}

###############################################################################################################################
# Finish the logfile off nicely
###############################################################################################################################
#
#if ( -e "$outputdir/$newfilename" )
$command = "/usr/bin/mediainfo $newfilename >> $LOG";
system $command;

###############################################################################################################################
# Tidy up
###############################################################################################################################

$db->disconnect;
1;

SSD longevity

Thanks Ash, got me thinking about my SSDs and how to prolong their life.

I've now followed the Arch Linux wiki post on SSDs and implemented many of the suggestions.

Tuesday, January 29, 2013

Transcode Bluray with HandBrakeCLI

#!/usr/bin/perl -w
# ============================================================================
# = NAME
# x264_transcode_720p.pl
#
# = PURPOSE
# Transcode Bluray mkv generated from makemkv to mkv with h264 and aac audio, keeping log files.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_720p.pl -f %FILE%
';

###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################
#
# Need HandBrakeCLI installed
# A nice toole is mediainfo, which is used after transcoding is complete to give stats on video and audio
use strict;
use XML::Simple;

###############################################################################################################################
# User defined quantities. Change these parameters to suit your needs
###############################################################################################################################
#
# Transcode speed, quality and size
my $howfast             = "veryslow";     # Choices are ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
my $videoqualitynumber         = "18";     # Sensible ranges are between 20 - 25, lower values are higher quality, bigger filesize
my $videosource            = "Bluray";    # Choices are Bluray, DVD, Other.
my $tuning            = "none";    # x264 tune options are none, film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency
my $presetprofile        = "none";    # x264 profiles are none, baseline, main, high, high10, high422, high444
my $audiochoice            = "ac3";    # Choices are copy, dtshd, dts, ac3, flac, aac, mp3
my $subtitles            = "none";    # Choices are none, english etc

###############################################################################################################################
# Paths
###############################################################################################################################
# Set your desired output directory
my $outputdir             = "/home/david/Movies";    # Not working. Currently output goes in same folder as original
# Set your desired logging directory
my $path = "/var/log/transcode";             # (no trailing slash)
# Set your personal tag
my $personaltag         = "farmerdave";    # This is added to the output filename

###############################################################################################################################
# Prototype and Constant definitions
###############################################################################################################################
#
#Message logger so we can track what's been going on
sub logmsg(@);

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

# do nothing?
my $noexec = 0;

# Booleans
my $true = 1;
my $false = 0;

###############################################################################################################################
# Transcoding
###############################################################################################################################
#
# some globals
my ($videofilters,$anamorphic,$displaysize,$x264speed,$x264tune,$x264profile,$deinterlace,$detelecine,$decomb,$denoise,$deblock);
my ($audiocodec,$audiobitrate,$audiochannels,$audiosamplerate,$audiodrc,$audiostream,$audionum);

# transcode audio options
$audiostream = "-a 1";

if ( $audiochoice eq "copy" )
{
  $audiocodec = "-E copy --audio-copy-mask ac3,dts,dtshd --audio-fallback faac";
  $audiobitrate = "";
  $audiochannels = "";
  $audiosamplerate = "";
  $audiodrc = "";
}
elsif ( $audiochoice eq "dtshd" )
{
  $audiocodec = "-E copy:dtshd";
  $audiobitrate = "";
  $audiochannels = "";
  $audiosamplerate = "";
  $audiodrc = "";
}
elsif ( $audiochoice eq "dts" )
{
  $audiocodec = "-E copy:dts";
  $audiobitrate = "";
  $audiochannels = "";
  $audiosamplerate = "";
  $audiodrc = "";
}
elsif ( $audiochoice eq "ac3" )
{
  $audiocodec = "-E ffac3";
  $audiobitrate = "-B 448";
  $audiochannels = "";
  $audiosamplerate = "";
  $audiodrc = "";
}
elsif ( $audiochoice eq "aac" )
{
  $audiocodec = "-E faac";
  $audionum = "256";
  $audiobitrate = "-B $audionum";
  $audiochannels = "-6 dpl2";
  $audiosamplerate = "-R Auto";
  $audiodrc = "-D 0.0";
}
elsif ( $audiochoice eq "mp3" )
{
  $audiocodec = "-E lame";
  $audionum = "192";
  $audiobitrate = "-B $audionum";
  $audiochannels = "-6 dpl2";
  $audiosamplerate = "-R Auto";
  $audiodrc = "-D 0.0";
}
elsif ( $audiochoice eq "flac" )
{
  $audiocodec = "-E ffflac";
  $audionum = "";
  $audiobitrate = "-C 8";
  $audiochannels = "-6 dpl2";
  $audiosamplerate = "-R Auto";
  $audiodrc = "-D 0.0";
}

# filters
$deinterlace = "--deinterlace=\"slower\"";
$detelecine = "--detelecine";
$decomb = "--decomb=\"bob\"";
$denoise = "--denoise=\"medium\"";
$deblock = "--deblock 1:0";
$videofilters = "";

# strict or loose anamorphic?
if ( $videosource eq "Bluray" )
{
  $anamorphic = "--loose-anamorphic";
  $displaysize = "1280"; # Choices are 1920, 1280 etc
  #$videofilters = "$deblock";
  #$videofilters = "$videofilters $denoise";
  #$videofilters = "$videofilters fast_pskip=0";
  #$videofilters = "$videofilters -x ref=9:subme=9:me=umh:merange=32:trellis=2:rc-lookahead=80";
  $videofilters = "$videofilters -r 23.976";
}
elsif ( $videosource eq "DVD" )
{
  $anamorphic = "--loose-anamorphic";
  $displaysize = "720";
  #$videofilters = "$detelecine $deinterlace $decomb $deblock $denoise";
  #$videofilters = "$denoise";
  $videofilters = "$videofilters -x subme=9:me=umh:merange=36:trellis=2";
}
else
{
  $anamorphic = "--loose-anamorphic";
  $displaysize = "1280";
  $videofilters = "$detelecine $deinterlace";
}

# transcode video options
my $videoquality = "-q $videoqualitynumber";
#my $videoquality = "--vb 2500 -2 -T";
my $transcodesize="-X $displaysize";
my $videocodec = "-e x264";
my $ftype = "-f mkv";
my $chapters = "-m";

# Subtitle options
my $subtitleoptions;
if ( $subtitles eq "none")
{ $subtitleoptions = ""}
else
{ $subtitleoptions = "-s 1" }

# Assemble video options and audio options
my $videooptions = "$videocodec $videoquality $ftype $chapters $subtitleoptions $transcodesize $videofilters $anamorphic";
my $audiooptions = "$audiostream $audiocodec $audiobitrate $audiochannels $audiosamplerate $audiodrc";
my $transcodeoptions = "$videooptions $audiooptions";

# --x264-preset
$x264speed = "--x264-preset $howfast";

# --x264-tune
if ( $tuning eq "none" )
{ $x264tune = "" }
else
{ $x264tune= "--x264-tune $tuning" }

# --x264-profile
if ( $presetprofile eq "none" )
{ $x264profile = "" }
else
{ $x264profile= "--x264-profile $presetprofile" }

# Assemble x264options
my $x264options = "$x264speed $x264tune $x264profile";

###############################################################################################################################
# HandBrakeCLI Builtin commands
###############################################################################################################################
# Default x264 options
# -x ref=3:bframes=3:b-pyramid=normal:weightp=2:8x8dct=1:cabac=1:b-adapt=1:me=hex:subme=7:mixed-refs=1:weightb=1:trellis=1:rc-lookahead=40:mbtree=1:analyse=p8x8,b8x8,i8x8,i4x4:aq-mode=1:direct=spatial
#
# Normal Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4  -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0
#
# High Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4  -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 --detelecine --decomb --loose-anamorphic -m -x b-adapt=2:rc-lookahead=50

# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my medium = "-x ref=3:bframes=3:b-adapt=1:direct=spatial:me=hex:merange=16:subq=7:rc-lookahead=40:analyse=most:trellis=1:no-fast-pskip=0";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";

###############################################################################################################################
# Kill
###############################################################################################################################
#
sub Die($)
{
    logmsg STDERR "@_\n";
    exit -1;
}

###############################################################################################################################
# Parse command-line arguments, check there is something to do:
###############################################################################################################################
#
my $debug = 1;
if ( ! @ARGV )
{  print && Die "$usage"  }

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 '-f' || $arg eq '--file' )
    {   $file = shift @ARGV  }
    else
    {
        unshift @ARGV, $arg;
        last;
    }
}

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


###############################################################################################################################
# Create new filename
###############################################################################################################################
#
# some globals
my $dir  = undef;
my ($newfilename,$command,$sourcetag,$displaytag,$audiotag);

# Initialise
my $iter = 0;
my $secs = 0;

# Chop file extension
(my $filetitle = $file) =~ s/\.[^.]+$//;

# Build personaltag
if ( $videosource eq "Bluray")
{ $sourcetag = ".BRRip" }
elsif ($videosource eq "DVD")
{ $sourcetag = ".DVDRip" }
else
{ $sourcetag = "" }

if ( $displaysize eq "1920")
{ $displaytag = ".1080p" }
elsif ($displaysize eq "1280")
{ $displaytag = ".720p" }
elsif ($displaysize eq "720")
{ $displaytag = ".576" }
elsif ($displaysize eq "576")
{ $displaytag = ".576" }
else
{ $displaytag = "" }

if ( $audiochoice eq "copy")
{ $audiotag = ".passthru" }
elsif ( $audiochoice eq "dtshd")
{ $audiotag = ".dtshd" }
elsif ( $audiochoice eq "dts")
{ $audiotag = ".dts" }
elsif ( $audiochoice eq "ac3")
{ $audiotag = ".ac3" }
elsif ( $audiochoice eq "flac")
{ $audiotag = ".flac" }
elsif ( $audiochoice eq "aac")
{ $audiotag = ".aac.$audionum" }
elsif ( $audiochoice eq "mp3")
{ $audiotag = ".mp3.$audionum" }
else
{ $audiotag = "" }


$personaltag = sprintf "%s%s%s%s-%s", $sourcetag, ".x264", $displaytag, $audiotag, $personaltag;

# Build newfilename
do {
  $newfilename = sprintf "%s%s", $filetitle, $personaltag;
  if ( $iter != "0" )
  {  $newfilename = sprintf "%s_%d%s", $newfilename, $iter, ".mkv"  } else { $newfilename = sprintf "%s%s", $newfilename, ".mkv" }
  $iter ++;
  $secs = $secs + $iter;
#} while  ( -e "$outputdir/$newfilename" );
} while  ( -e "$newfilename" );

#$debug && logmsg "$outputdir/$newfilename seems unique\n";
#$debug && logmsg "$newfilename seems unique\n";

###############################################################################################################################
# Logging
###############################################################################################################################
#
my $createdir="mkdir -p $path";
if (! -d $path)
{ print "mkdir failed\n" unless system($createdir) }

# extra console output?
my $LOG = "$path/transcode_$newfilename.log";
my $logsize = 4096;       #Maximum log size in kbytes, self pruning.
my $logdebug = $false;        #Debugging default is off.
my $logverbose = "-v 1";


###############################################################################################################################
# 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 ($logdebug==$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 : $!";
    }
}

###############################################################################################################################
# Build and execute command
###############################################################################################################################
#
$command = "/usr/bin/HandBrakeCLI -i $file";
#$command = "$command -o $outputdir/$newfilename";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $x264options";
$command = "$command $transcodeoptions";


#chdir $outputdir;
# Open STDERR so that HandBrakeCLI outputs to the log file
open ( STDERR, ">>$LOG" );
select( STDERR );

# Executing system command
logmsg "###############################################################################################################################\n";
$debug && logmsg "Executing: $command\n";
logmsg "Options used are:\n";
logmsg "Preset is $howfast";
logmsg "Profile is $presetprofile";
logmsg "Tune is $tuning";
logmsg "Constant Rate Factor is $videoqualitynumber";
logmsg "Audio track is $audiochoice";
logmsg "Video filters are set to $videofilters";

# Bang!
system $command;

# Used this to debug
# my $touchcommand = "touch $newfilename";
# $debug && logmsg "Executing: $touchcommand\n";
# system $touchcommand;

# Close STDERR and re-open STDOUT
close( STDERR );
select( STDOUT );

###############################################################################################################################
# Finish the logfile off nicely
###############################################################################################################################
#
#if ( -e "$outputdir/$newfilename" )
$command = "/usr/bin/mediainfo $newfilename >> $LOG";
system $command;

if ( -e "$newfilename" )
{  
    logmsg "Transcoding $newfilename has completed.\n";
    logmsg "###############################################################################################################################\n";
}
else
{
    logmsg "Transcode of $newfilename has failed\n";
    logmsg "###############################################################################################################################\n";
}

###############################################################################################################################
# Done
###############################################################################################################################
1;

Tuesday, June 5, 2012

Transcode SD


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

###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################

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

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

sub logmsg(@); #Message logger so we can track what's been going on.

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

# do nothing?
my $noexec = 0;

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

# Video Size output
my $setsize = "576";
my $howfast = "fast";
my $transcodesize;
if ($setsize eq 1080)
{
  $transcodesize = "-w 1920 -Y 1080"; # Full HDTV
}
elsif ($setsize eq 720)
{
  $transcodesize="-w 1280 -Y 720"; # HDTV
}
else
{
  $transcodesize="-w 1024 -Y 576"; # SDTV
}

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_SD.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
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 video options
my $videocodec = "-e x264";
my $videoquality = "-q 22";
my $ftype = "-f mkv";
my $anamorphic = "--loose-anamorphic";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

# transcode auido options
my $audiostream = "-a 1";
my $audiocodec = "-E faac";
my $audiobitrate = "-B 192";
my $audiochannels = "-6 dpl2";
my $audiosamplerate = "-R Auto";
my $audiodrc = "-D 0.0";


# long word options - type them here
my $videooptions = "$videocodec $videoquality $ftype $anamorphic $detelecine $chapters $transcodesize";
my $audiooptions = "$audiostream $audiocodec $audiobitrate $audiochannels $audiosamplerate $audiodrc";
my $transcodeoptions = "$videooptions $audiooptions";

# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";

my $x264speed = "--x264-preset $howfast";
my $x264tune= " --x264-tune film";
my $x264options = "$x264speed $x264tune";

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

###############################################################################################################################
# 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 ($LOGDEBUG==$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 : $!";
}
}

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 )
{   logmsg && 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 )
{
    logmsg && 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 || logmsg && Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

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

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

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

    if ( $DEBUG )
    {
        logmsg "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
    {
        logmsg "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || logmsg && Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   logmsg && 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 )
{   logmsg && Die "Cannot find directory $dir that contains recordings"  }

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

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

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

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && 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 && logmsg "$dir/$newfilename seems unique\n";


$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $transcodeoptions";
$command = "$command $x264options";

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

chdir $dir;
#open (LOG,">>$LOG");
open ( STDERR, ">>$LOG" );
#open ( STDOUT, ">>$LOG" );
select( STDERR );
system $command;
close( STDERR );
select( STDOUT );

if ( ! -e "$dir/$newfilename" )
{   logmsg && 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 ||  logmsg && Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    logmsg 'Old file size = ' . (-s "$dir/$file")        . "\n";
    logmsg '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 )
{   logmsg "# $command\n"  }

if ( ! $noexec )
{   $db->do($command)  || logmsg && 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
    logmsg "Transcoding $newfilename has completed\n\n";
}

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

$db->disconnect;
1;

Transcode HD


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

###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################

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

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

sub logmsg(@); #Message logger so we can track what's been going on.

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

# do nothing?
my $noexec = 0;

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

# Video Size output
my $setsize = "720";
my $howfast = "faster";
my $transcodesize;
if ($setsize eq 1080)
{
  $transcodesize = "-w 1920 -Y 1080"; # Full HDTV
}
elsif ($setsize eq 720)
{
  $transcodesize="-w 1280 -Y 720"; # HDTV
}
else
{
  $transcodesize="-w 1024 -Y 576"; # SDTV
}

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_HD.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
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 video options
my $videocodec = "-e x264";
my $videoquality = "-q 24";
my $ftype = "-f mkv";
my $anamorphic = "--loose-anamorphic";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

# transcode auido options
my $audiostream = "-a 1";
my $audiocodec = "-E faac";
my $audiobitrate = "-B 192";
my $audiochannels = "-6 dpl2";
my $audiosamplerate = "-R Auto";
my $audiodrc = "-D 0.0";


# long word options - type them here
my $videooptions = "$videocodec $videoquality $ftype $anamorphic $detelecine $chapters $transcodesize";
my $audiooptions = "$audiostream $audiocodec $audiobitrate $audiochannels $audiosamplerate $audiodrc";
my $transcodeoptions = "$videooptions $audiooptions";

# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";

my $x264speed = "--x264-preset $howfast";
my $x264tune= " --x264-tune film";
my $x264options = "$x264speed $x264tune";

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

###############################################################################################################################
# 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 ($LOGDEBUG==$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 : $!";
}
}

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 )
{   logmsg && 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 )
{
    logmsg && 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 || logmsg && Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

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

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

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

    if ( $DEBUG )
    {
        logmsg "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
    {
        logmsg "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || logmsg && Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   logmsg && 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 )
{   logmsg && Die "Cannot find directory $dir that contains recordings"  }

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

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

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

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && 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 && logmsg "$dir/$newfilename seems unique\n";


$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $transcodeoptions";
$command = "$command $x264options";

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

chdir $dir;
#open (LOG,">>$LOG");
open ( STDERR, ">>$LOG" );
#open ( STDOUT, ">>$LOG" );
select( STDERR );
system $command;
close( STDERR );
select( STDOUT );

if ( ! -e "$dir/$newfilename" )
{   logmsg && 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 ||  logmsg && Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    logmsg 'Old file size = ' . (-s "$dir/$file")        . "\n";
    logmsg '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 )
{   logmsg "# $command\n"  }

if ( ! $noexec )
{   $db->do($command)  || logmsg && 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
    logmsg "Transcoding $newfilename has completed\n\n";
}

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

$db->disconnect;
1;

Transcode No Delete


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

###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################

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

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

sub logmsg(@); #Message logger so we can track what's been going on.

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

# do nothing?
my $noexec = 0;

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

# Video Size output
my $setsize = "1080";
my $howfast = "superfast";
my $videoquality = "-q 24";

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_nodelete.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
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 video options
my $videocodec = "-e x264";
my $ftype = "-f mkv";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

# transcode auido options
my $audiostream = "-a 1";
my $audiocodec = "-E faac";
# my $audiocodec = "-E copy";
my $audiobitrate = "-B 192";
my $audiochannels = "-6 dpl2";
my $audiosamplerate = "-R Auto";
my $audiodrc = "-D 0.0";

my ($transcodesize,$filters,$anamorphic);
if ($setsize eq 1080)
{
  $transcodesize = " "; # Full HDTV
  $filters = "$detelecine -x me=umh:subme=7:merange=16";
  $anamorphic = "--strict-anamorphic";
}
elsif ($setsize eq 720)
{
  $transcodesize="-w 1280 -Y 720"; # HDTV
  $filters = "$detelecine $decomb -x me=umh:subme=7:merange=16";
  $anamorphic = "--loose-anamorphic";
}
else
{
  $transcodesize="-w 1024 -Y 576"; # SDTV
  $filters = "$detelecine $decomb";
  $anamorphic = "--loose-anamorphic";
}


# long word options - type them here
my $videooptions = "$videocodec $videoquality $ftype $anamorphic $filters $chapters $transcodesize";
my $audiooptions = "$audiostream $audiocodec $audiobitrate $audiochannels $audiosamplerate $audiodrc";
my $transcodeoptions = "$videooptions $audiooptions";

# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";

my $x264speed = "--x264-preset $howfast";
my $x264tune= " --x264-tune film";
my $x264options = "$x264speed $x264tune";

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

###############################################################################################################################
# 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 ($LOGDEBUG==$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 : $!";
}
}

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 )
{   logmsg && 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 )
{
    logmsg && 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 || logmsg && Die "Unable to query jobqueue table";
    $ref       = $query->fetchrow_hashref;
    $chanid    = $ref->{'chanid'};
    $starttime = $ref->{'starttime'};
    $query->finish;

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

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

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

    if ( $DEBUG )
    {
        logmsg "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
    {
        logmsg "File $file has a strange name. Searching in recorded table\n";
        $query = $db->prepare("SELECT chanid, starttime " .
                              "FROM recorded WHERE basename='$file';");
        $query->execute || logmsg && Die "Unable to query recorded table";
        ($chanid,$starttime) = $query->fetchrow_array;
        $query->finish;

        if ( ! $chanid || ! $starttime )
        {   logmsg && 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 )
{   logmsg && Die "Cannot find directory $dir that contains recordings"  }

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

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

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

$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && 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 && logmsg "$dir/$newfilename seems unique\n";


$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $transcodeoptions";
$command = "$command $x264options";

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

chdir $dir;
#open (LOG,">>$LOG");
open ( STDERR, ">>$LOG" );
#open ( STDOUT, ">>$LOG" );
select( STDERR );
system $command;
close( STDERR );
select( STDOUT );

if ( ! -e "$dir/$newfilename" )
{   logmsg && 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 ||  logmsg && Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;

$ref->{'starttime'} = $newstarttime;
$ref->{'basename'}  = $newfilename;
if ( $DEBUG && ! $noexec )
{
    logmsg 'Old file size = ' . (-s "$dir/$file")        . "\n";
    logmsg '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 )
{   logmsg "# $command\n"  }

#if ( ! $noexec )
#{   $db->do($command)  || logmsg && 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
    logmsg "Transcoding $newfilename has completed\n\n.";
}

# ============================================================================
# give log file a few blank lines
print "\n\n\n\n" >>$LOG;

$db->disconnect;
1;