Saturday, May 18, 2013

Samba blocked - smbclient returns Error for a configured user

I was getting two errors,

$ smbclient -L hostname -U%
params.c:OpenConfFile() - Unable to open configuration file "/etc/samba/smb.conf":
    No such file or directory
smbclient: Can't load /etc/samba/smb.conf - run testparm to debug it
Domain=[WORKGROUP] OS=[Unix] Server=[Samba 4.0.5]
tree connect failed: NT_STATUS_ACCESS_DENIED


and

$ smbclient -L hostname --user username
params.c:OpenConfFile() - Unable to open configuration file "/etc/samba/smb.conf":
    No such file or directory
smbclient: Can't load /etc/samba/smb.conf - run testparm to debug it
Connection to hostname failed (Error NT_STATUS_CONNECTION_REFUSED)


The first error was due to samba not being public and therefore i needed to specify a samba username (who must also be a system user, see here) who was granted samba permissions.

The second error I was receiving simply because I had my firewall set using iptables, and had not opened the correct ports for samba!

I performed the following commands and corrected it.
  1. sudo iptables -A TCP -p tcp --dport 139 -j ACCEPT
  2. sudo iptables -A TCP -p tcp --dport 445 -j ACCEPT
  3. sudo iptables -A UDP -p udp --sport 137 -j ACCEPT
  4. sudo iptables -A UDP -p udp --dport 137 -j ACCEPT 
  5. sudo iptables -A UDP -p udp --dport 138 -j ACCEPT 
  6. su -c 'iptables-save > /etc/iptables/iptables.rules'
This opens the samba ports and then saves the rules to the iptables config file. Finally, restart the iptables service. For Arch, this is
  • sudo systemctl restart iptables.service
Done. Now my correctly configured /etc/samba/smb.conf file is enough to grant me access to my shares.

Saturday, March 9, 2013

Mythconverg - how to remove odd entries

I had some entries that kept appearing again and again in my mythbackend logs. An example is

2013-03-08 19:11:36.832276 E [979/979] CoreContext programinfo.cpp:2284 (GetPlaybackURL) - ProgramInfo(Superman_II_06.02.2012.mkv): GetPlaybackURL: 'Superman_II_06.02.2012.mkv' should be local, but it can not be found.

So, these would repeat themselves and fill my log files, but when I went to Mythfrontend and looked for them, they did not appear.

Related to this was a strange entry in Information Centre -> System Status -> Machine Status. Under the Total Disk Space I would have an entry that said I had years of time remaining, using my average rate of 1 kbps.

If I went to Mythfrontend -> Manage Recordings -> Delete Recordings, and with an entry in the Group Filter highlighted, open up the Group List Menu (the letter 'm' key), and selected 'Change Group Filter', I would see that I had some entries in either LiveTV or Default (or any other group I had saved a recording to), but if I selected that group the recordings would not appear.

For the LiveTV ones, the recording name was listed, but it was greyed out, with an 'x' at the start. Going and deleting this would remove it from the list, but it would always reappear if I did the process again.

For the Default ones, the list was just empty!

To solve this, I went straight to mysql and the mythconverg database itself. At a terminal type

$ mysql -u root -p

To see what databases you have, type

mysql> show databases;

To select the correct one, type

mysql> use mythconverg;

Mythconverg has lots of tables, and are stored like

| chanid | starttime | endtime | title | subtitle | description | season | episode | category | hostname | bookmark | editing | cutlist | autoexpire | commflagged | recgroup | recordid | seriesid | programid | inetref | lastmodified | filesize | stars | previouslyshown | originalairdate | preserve | findid | deletepending | transcoder | timestretch | recpriority | basename | progstart | progend | playgroup | profile | duplicate | transcoded | watched |

I started by seeing what was actually stored in the LiveTV recording group.

mysql> select * from recorded where recgroup = "LiveTV";

This listed some recordings, and these were indeed some of the ones repeating again and again in the logs. A commonly used SQL row selector is

WHERE chanid = "xxxx" and starttime = "xxxx-xx-xx xx:xx:xx"

I would look at the very first 2 outputs for any entry I wanted to delete to find the chanid and starttime. Firstly make sure this resulted in precisely what I wanted.

mysql> SELECT * FROM recorded WHERE chanid = "1009" and starttime = "2013-03-01 09:57:03";

Confirm that his would output only 1 entry, and was one that needed deleting. Then,


mysql> DELETE FROM recorded WHERE chanid = "1009" and starttime = "2013-03-01 09:57:03";

Gone! Continue for all entries you wanted deleted. Using the same approach I firstly listed everything in the Default recgroup.

mysql> select * from recorded where recgroup = "Default";

I had to take car of some unusual ones, which had chanid of "0" and some even had endtime of "0000-00-00 00:00:00". These seem to exist from recordings that were interrupted, whether through an unscheduled reboot or stopping a transcode from completing.

Not to worry. First check with the SELECT command, confirm it is correct, and the up-arrow and replace with DELETE. Done.

This also fixed my ridiculously low average kbps in the Machine Status page.

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;