Wednesday, December 11, 2013

CubieTruck - first attempt at installing a Linux distro to a 16 GB micro SD card

First thing I learned was that CubieTruck is shipped with Android 4.2 already flashed to the 8 GB NAND flash and will boot without any changes necessary. Second thing I learned was that Android wouldn't work with my Logitech wireless keyboard. Anyway, I don't want Android as the OS, began trying to put Arch on the SD card.

Currently, archlinuxARM doesn't have a dedicated page for CubieTruck, but it does have an archlinuxARM page for CubieBoard2 and this thread suggests people are having some luck with it. So I gave it a try!

The instructions are very short and easy to follow, which I did. My power supply outputs 5.1V and 2.1A, which may not be enough to power the whole board with a 2.5" HDD at the same time (although it seemed to when booting Android from the NAND flash). I did not have an ethernet cable connected, as this seemed to be one of the initial problems with CubieTruck and Arch.

I plugged in the power supply and connected the CubieTruck to my plasma screen via the HDMI port. I was very pleasantly surprised to see the LED lights come on and TUX appear in the top left of the screen! Then a boot script displayed on the screen after a short period.

I've never used the ARM architecture before, and don't know what the boot output should display, but as it flashed before my eyes it looked a little different to my other Arch installs. But, I eventually got a login prompt! Default login is root/root which worked, and I was able to type a few commands to convince myself that I really had Arch up and running on my CubieTruck. One of the first I tried was
# dmesg | less

which worked initially, but...

It crashed with a whole bunch of hex codes outputing to the terminal. Funny thing was that I couldn't get archlinuxARM to boot a second time. I thought initially the problem was that CubieTruck was not supported by this version of archlinuxARM, but that may not be the case.

Turns out the micro SD card I bought from eBay is junk, and most likely the issue. But it took me a while to come to that realisation.

I first went through the process of trying to install lubuntu following this tutorial. That's when I noticed the trouble with the card. I tried to wipe zeros to it with a simple command:

# dd if=/dev/zero of=${card} bs=4096

where ${card} was defined earlier as card=/dev/sde on my system. This failed pretty quickly with an error "read-only filesystem". So I tried to format it with gparted, and this worked. Mounting one of the new partitions and trying write zeros to it again failed. The card would umount itself after only about 100MB were copied to it.

I eventually managed to get zeros written to it all, and then installed lubuntu and checked the integrity with the provided sha256sums file. All good, but putting the SD card in CubieTruck and booting failed pretty quickly.

Very disappointing. I now have to decide if I will flash lubuntu to the NAND flash, or get myself a valid micro SD and install to that. Delays delays!

Monday, December 9, 2013

CubieTruck - Assembled

I assembled my CubieTruck in its case last night. My first experience was 99% fine, but one of the screws supplied is dodgy and won't work with the board. Lucky for me, I'm a hoarder of such things and have spares lying around.
CubieTruck assembled in its clear case in front of the box it arrived in.
CubieTruck assembled.
I like the way the 2.5" HDD sits inside the case, was not expecting it to be so neat.
CubieTruck with 2.5" HDD under the board
CubieTruck with 2.5" HDD sitting under the board.

Friday, November 8, 2013

Use SSH Keys (public key cryptography) to connect on your LAN

Learning by example: (and borrowing from the Arch wiki)

I want to connect to my desktop from my htpc.
Assuming that openssh is installed and the daemon is running, and that /etc/ssh/sshd_config is configured to your satisfaction.

Issue the following command to create a 521 bit long public/private ECDSA key pair with an extended comment on the HTCP.

htpc $ ssh-keygen -t ecdsa -b 521 -C "$(whoami)@$(hostname)-$(date -I)"

Authorized_keys file

In order to connect from the htpc, the desktop must have a copy of the htpc's public key in its ~/.ssh/authorized_key file. Use whatever method you have available to get a copy of the id_ecdsa.pub to the desktop.

desktop $ scp david@htpc:/home/david/.ssh/id_ecdsa.pub /home/david/tmp

Then import this public key into the ~/.ssh/authorized_keys file.

desktop $ cat ~/tmp/id_ecdsa.pub >> ~/.ssh/authorized_keys
desktop $ rm ~/tmp/id_ecdsa.pub


Known_hosts

Upon your first connection to the desktop, you will see the following

htpc $ ssh david@desktop
The authenticity of host 'desktop (192.168.1.110)' can't be established.
ECDSA key fingerprint is 61:3e:e6:40:29:d7:d8:dc:db:f8:0b:d9:74:c5:d6:6f.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'desktop,192.168.1.110' (ECDSA) to the list of known hosts.
Enter passphrase for key '/home/david/.ssh/id_ecdsa':

This creates an entry in /home/david/.ssh/known_hosts which will allow connections to desktop.

Saturday, October 26, 2013

Shepherd cron job for MythTV

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

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

Monday, September 23, 2013

my .conkyrc file

# Conky, a system monitor, based on torsmo
#
# Any original torsmo code is licensed under the BSD license
#
# All code written since the fork of torsmo is licensed under the GPL
#
# Please see COPYING for details
#
# Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
# Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al. (see AUTHORS)
# All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

background yes
use_xft yes
xftfont HandelGotD:size=8
xftalpha 0.2
update_interval 5
total_run_times 0
own_window yes
own_window_transparent yes
own_window_hints undecorated,below,sticky,skip_taskbar,skip_pager
own_window_argb_visual true
double_buffer yes
#minimum_size 275 5
#maximum_width 275
minimum_size 320 800
maximum_width 320
draw_shades no
draw_outline no
draw_borders no
draw_graph_borders no
default_color white
default_shade_color black
default_outline_color green
alignment top_right
gap_x 10
gap_y 40
no_buffers yes
uppercase no
cpu_avg_samples 2
net_avg_samples 1
override_utf8_locale yes
use_spacer right

TEXT




${alignc}${time %a, %b %d %Y}
${offset 30}${font Dejavu Serif:size=20}${time %I:%M %p}${font}


CPU:1  ${cpu cpu1}% ${cpubar cpu1}
CPU:2  ${cpu cpu2}% ${cpubar cpu2}


${color white}RAM ${color} $alignr $mem / $memmax
$membar
${color white}SWAP ${color} $alignr $swap / $swapmax
$swapbar


${color white}/ ${color} $alignr ${fs_used /} / ${fs_size /}
${fs_bar /}
${color white}Home ${color} $alignr ${fs_used /home/david} / ${fs_size /home/david}
${fs_bar /home/david}
${color white}Movies ${color} $alignr ${fs_used /home/david/Movies} / ${fs_size /home/david/Movies}
${fs_bar /home/david/Movies}
${color white}Music ${color} $alignr ${fs_used /home/david/Music} / ${fs_size /home/david/Music}
${fs_bar /home/david/Music}
${color white}MythTV ${color} $alignr ${fs_used /MythTV} / ${fs_size /MythTV}
${fs_bar /MythTV}
${color white}TV Series ${color} $alignr ${fs_used /home/david/TVseries} / ${fs_size /home/david/TVseries}
${fs_bar /home/david/TVseries}

${color white}Processes: Total | Running
${color white}Processes:$color $processes  | $running_processes

Top Processes

CPU $alignr CPU%
${top name 1}$alignr${top cpu 1}
${top name 2}$alignr${top cpu 2}
${top name 3}$alignr${top cpu 3}
${top name 4}$alignr${top cpu 4}

MEM $alignr MEM%
${top_mem name 1}$alignr${top_mem mem 1}
${top_mem name 2}$alignr${top_mem mem 2}
${top_mem name 3}$alignr${top_mem mem 3}
${top_mem name 4}$alignr${top_mem mem 4}


IP on eth0 $alignr ${addr eth0}

Down $alignr ${downspeed eth0}/s
${color green}${downspeedgraph eth0}${color}
Up $alignr ${upspeed eth0}/s
#${color green}${upspeedgraph eth0 16,200}${color}
${color green}${upspeedgraph eth0}${color}

# Arch RSS feed
#${rss https://planet.archlinux.org/rss20.xml 1 item_titles 10 }

Tuesday, July 16, 2013

Adding bittorrent ports to my iptables

I accessed my router and did the port forwarding necessary to allow my bittorrent client (transmission) to seed properly, but I also needed to add a rule to my iptables to allow the traffic through.

My setup is

Internet     ->     Hardware Router        ->     Linux Desktop
                    with port forwarding          Client
                    enabled


I began by backing up my current iptables file

$ sudo cp /etc/iptables/iptables.rules /etc/iptables/iptables.rules.20130716


I am using Arch Linux Simple Stateful Firewall along with Transmission bittorrent client. The default port is 51413, so I needed to perform the following commands:

$ sudo iptables -A TCP -p tcp --dport 51413 -j ACCEPT
$ sudo iptables -A TCP -p tcp --sport 51413 -j ACCEPT
$ su -c "iptables-save > /etc/iptables/iptables.rules"
Password:
$ sudo systemctl restart iptables.service

If I want to revert back to my old rules (which block port 51413) I can issue the command

$ sudo iptables-restore < /etc/iptables/iptables.rules.20130716
$ su -c "iptables-save > /etc/iptables/iptables.rules"
Password:
$ sudo systemctl restart iptables.service

Friday, May 24, 2013

Rip CDs to flac on Linux with Morituri and verify with accuraterip

I want to rip my cd collection accurately, so I use Morituri from the command line. It's a very simple process, which will use MusicBrainz to populate the artist and track information.

Firstly find your drive offset either from this list of drive offsets or use Morituri with a cd in the drive

$ rip offset find

Once the offset is established (for example +48), rip the cd with the following command

rip cd --device /dev/cdrom rip --offset 48

For a different naming convention, I prefer

$ rip cd rip --offset 48 --track-template="%A/%A - %d/%t - %a - %n" --disc-template="%A/%A - %d/%A - %d"

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;