Tuesday, June 5, 2012

Transcode SD


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

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

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

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

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

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

# do nothing?
my $noexec = 0;

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

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

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_SD.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
my $DEBUG = 1;

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

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

# transcode video options
my $videocodec = "-e x264";
my $videoquality = "-q 22";
my $ftype = "-f mkv";
my $anamorphic = "--loose-anamorphic";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

my $extra = 'Copy';


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

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

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

$command .= ');';

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

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

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

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

$db->disconnect;
1;

Transcode HD


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

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

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

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

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

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

# do nothing?
my $noexec = 0;

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

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

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_HD.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
my $DEBUG = 1;

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

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

# transcode video options
my $videocodec = "-e x264";
my $videoquality = "-q 24";
my $ftype = "-f mkv";
my $anamorphic = "--loose-anamorphic";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

my $extra = 'Copy';


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

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

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

$command .= ');';

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

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

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

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

$db->disconnect;
1;

Transcode No Delete


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

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

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

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

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

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

# do nothing?
my $noexec = 0;

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

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

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

# extra console output?
my $LOG = "/var/log/transcode/transcodemyth_nodelete.log";
my $LOGSIZE = 4096; #Maximum log size in kbytes, self pruning.
my ($LOGDEBUG) = $FALSE; #Debugging default is off.
my ($BASENAME) = $0; #How was the program called?
my $logverbose = "-v 1";
my $DEBUG = 1;

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

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

# transcode video options
my $videocodec = "-e x264";
my $ftype = "-f mkv";
my $deinterlace = "--deinterlace";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $chapters = "-m";

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

my $extra = 'Copy';


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

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

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

$command .= ');';

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

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

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

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

$db->disconnect;
1;

Saturday, May 12, 2012

Slackware lirc with mceusb

For my older installation (Slackware 12.2) I made this post to get lirc working.

Woodsman has posted this post and this post about starting lirc, which may help.

On Arch, where lirc is currently working, I have the following output
$ lsmod | grep lirc
ir_lirc_codec           4027  3 
lirc_dev                9359  1 ir_lirc_codec
rc_core                13280  14 mceusb,ir_nec_decoder,ir_rc5_decoder,ir_rc6_decoder,ir_jvc_decoder,rc_rc6_mce,ir_sony_decoder,ir_sanyo_decoder,ir_mce_kbd_decoder,ir_lirc_codec,cx88xx,rc_winfast

However, I did the following for my Slackware 13.37:
  1. I firstly installed lirc from Slackbuilds.
  2. I tried to start lircd but had to create a folder to prevent the following error:
    • lircd: can't open or create /var/run/lirc/lircd.pid
      lircd: No such file or directory
    • Simple solution, use the following command (as root):
      # mkdir -p /var/run/lirc
  3. Start lircd with this command (as root)
    # lircd -d /dev/lirc/0

Tuesday, May 8, 2012

kill processes with kill-byname.sh

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

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

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

E_BADARGS=66

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


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

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

exit $?

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

Monday, May 7, 2012

XBMC Keymaps

XBMC keymaps

keyboard_david-htpc.xml
********************************************************************************
<!-- This file contains the mapping of keys (gamepad, remote, and keyboard) to actions within XBMC -->
<!-- The <global> section is a fall through - they will only be used if the button is not          -->
<!-- used in the current window's section.  Note that there is only handling                       -->
<!-- for a single action per button at this stage.                                                 -->
<!-- For joystick/gamepad configuration under linux/win32, see below as it differs from xbox       -->
<!-- gamepads.                                                                                     -->

<!-- The format is:                      -->
<!--    <device>                         -->
<!--      <button>action</button>        -->
<!--    </device>                        -->

<!-- To map keys from other remotes using the RCA protocol, you may add <universalremote> blocks -->
<!-- In this case, the tags used are <obc#> where # is the original button code (OBC) of the key -->
<!-- You set it up by adding a <universalremote> block to the window or <global> section:       -->
<!--    <universalremote>             -->
<!--       <obc45>Stop</obc45>         -->
<!--    </universalremote>            -->

<!-- Note that the action can be a built-in function.                 -->
<!--  eg <B>XBMC.ActivateWindow(MyMusic)</B>                         -->
<!-- would automatically go to My Music on the press of the B button. -->

<!-- Joysticks / Gamepads:                                                                    -->
<!--   See the sample PS3 controller configuration below for the format.                      -->
<!--                                                                                          -->
<!--  Joystick Name:                                                                          -->
<!--   Do 'cat /proc/bus/input/devices' or see your xbmc log file  to find the names of       -->
<!--   detected joysticks. The name used in the configuration should match the detected name. -->
<!--                                                                                          -->
<!--  Button Ids:                                                                             -->
<!--   'id' is the button ID used by SDL. Joystick button ids of connected joysticks appear   -->
<!--   in xbmc.log when they are pressed. Use your log to map custom buttons to actions.      -->
<!--                                                                                          -->
<!--  Axis Ids / Analog Controls                                                              -->
<!--   Coming soon.                                                                           -->
<keymap>
  <global>
    <keyboard>
      <p>Play</p>
      <q>Queue</q>
      <f>FastForward</f>
      <r>Rewind</r>
      <left>Left</left>
      <right>Right</right>
      <up>Up</up>
      <down>Down</down>
      <pageup>PageUp</pageup>
      <pagedown>PageDown</pagedown>
      <return>Select</return>
      <enter>Select</enter>
      <backspace>ParentDir</backspace>
      <m>ActivateWindow(PlayerControls)</m>
      <s>ActivateWindow(shutdownmenu)</s>
      <escape>PreviousMenu</escape>
      <i>Info</i>
      <menu>ContextMenu</menu>
      <c>ContextMenu</c>
      <space>Pause</space>
      <x>Stop</x>
      <period>SkipNext</period>
      <comma>SkipPrevious</comma>
      <tab>FullScreen</tab>
      <printscreen>Screenshot</printscreen>
      <s mod="ctrl">Screenshot</s>
      <minus>VolumeDown</minus>
      <plus>VolumeUp</plus>
      <zero>Number0</zero>
      <one>Number1</one>
      <two>Number2</two>
      <three>Number3</three>
      <four>Number4</four>
      <five>Number5</five>
      <six>Number6</six>
      <seven>Number7</seven>
      <eight>Number8</eight>
      <nine>Number9</nine>
      <backslash>ToggleFullScreen</backslash>
      <browser_home>XBMC.ActivateWindow(Home)</browser_home>
      <browser_favorites>ActivateWindow(Favourites)</browser_favorites>
      <browser_refresh/>
      <browser_search/>
      <launch_app1_pc_icon>ActivateWindow(MyPrograms)</launch_app1_pc_icon>
      <launch_media_select>XBMC.ActivateWindow(MyMusic)</launch_media_select>
      <play_pause>Pause</play_pause>
      <stop>Stop</stop>
      <volume_up>VolumeUp</volume_up>
      <volume_mute>Mute</volume_mute>
      <volume_down>VolumeDown</volume_down>
      <next_track>SkipNext</next_track>
      <prev_track>SkipPrevious</prev_track>
      <launch_mail></launch_mail>
      <key id='61620'></key>      <!-- same as above, launch_mail, but using button code (based on vkey id) -->
      <home>FirstPage</home>
      <end>LastPage</end>
      <key id='65446'>ParentDir</key>
      <key id='65459'>Play</key>
      <!-- ****************************************************** -->
      <!-- MS Media Center keyboard shortcuts sent by MCE remotes -->
      <!-- See http://msdn.microsoft.com/en-us/library/bb189249.aspx -->
      <p mod="ctrl,shift">Play</p>        <!-- Play -->
      <s mod="ctrl,shift">Stop</s>        <!-- Stop -->
      <p mod="ctrl">Pause</p>             <!-- Pause -->
      <f mod="ctrl,shift">FastForward</f> <!-- Fwd -->
      <b mod="ctrl,shift">Rewind</b>      <!-- Rew -->
      <f mod="ctrl">SkipNext</f>          <!-- Skip -->
      <b mod="ctrl">SkipPrevious</b>      <!-- Replay -->
      <d mod="ctrl">Info</d>              <!-- MCE Details -->
      <f10>VolumeUp</f10>                 <!-- MCE Vol up -->
      <f9>VolumeDown</f9>                 <!-- MCE Vol down -->
      <f8>Mute</f8>                       <!-- MCE mute -->
      <g mod="ctrl">OSD</g>               <!-- MCE Guide -->
      <m mod="ctrl">ActivateWindow(music)</m>    <!-- MCE My music -->
      <i mod="ctrl">ActivateWindow(pictures)</i> <!-- MCE My pictures -->
      <e mod="ctrl">ActivateWindow(video)</e>    <!-- MCE videos -->
      <!-- MCE keypresses without an obvious use in XBMC -->
      <o mod="ctrl">Notification(MCEKeypress, Recorded TV, 3)</o>
      <t mod="ctrl">Notification(MCEKeypress, Live TV, 3)</t>
      <t mod="ctrl,shift">Notification(MCEKeypress, My TV, 3)</t>
      <a mod="ctrl">Notification(MCEKeypress, Radio, 3)</a>
      <m mod="ctrl,shift">Notification(MCEKeypress, DVD menu, 3)</m>
      <u mod="ctrl">Notification(MCEKeypress, DVD subtitle, 3)</u>
      <a mod="ctrl,shift">Notification(MCEKeypress, DVD audio, 3)</a>
    </keyboard>
  </global>
  <LoginScreen>
    <keyboard>
      <end>XBMC.ShutDown()</end>
    </keyboard>
  </LoginScreen>
  <Home>
    <keyboard>
      <i>info</i>
      <end>XBMC.ShutDown()</end>
      <backspace>XBMC.ActivateWindow(shutdownmenu)</backspace>
    </keyboard>
  </Home>
  <VirtualKeyboard>
    <keyboard>
      <backspace>Backspace</backspace>
    </keyboard>
  </VirtualKeyboard>
  <MyFiles>
    <keyboard>
      <space>Highlight</space>
      <delete>Delete</delete>
      <m>Move</m>
      <r>Rename</r>
    </keyboard>
  </MyFiles>
  <MyMusicPlaylist>
    <keyboard>
      <space>Playlist</space>      <!-- Close playlist -->
      <delete>Delete</delete>
      <c>Playlist.Clear</c>
      <u>MoveItemUp</u>
      <d>MoveItemDown</d>
      <backspace>Playlist</backspace>      <!-- Close playlist -->
    </keyboard>
  </MyMusicPlaylist>
  <MyMusicPlaylistEditor>
    <keyboard>
      <u>MoveItemUp</u>
      <d>MoveItemDown</d>
      <delete>Delete</delete>
    </keyboard>
  </MyMusicPlaylistEditor>
  <MyMusicFiles>
    <keyboard>
      <space>Playlist</space>
      <q>Queue</q>
    </keyboard>
  </MyMusicFiles>
  <MyMusicLibrary>
    <keyboard>
      <space>Playlist</space>
      <q>Queue</q>
    </keyboard>
  </MyMusicLibrary>
  <FullscreenVideo>
    <keyboard>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>StepForward</period>
      <comma>StepBack</comma>
      <backspace>Fullscreen</backspace>
      <quote>SmallStepBack</quote>
      <opensquarebracket>BigStepForward</opensquarebracket>
      <closesquarebracket>BigStepBack</closesquarebracket>
      <return>OSD</return>
      <enter>OSD</enter>
      <m>OSD</m>
      <i>Info</i>
      <o>CodecInfo</o>
      <z>AspectRatio</z>
      <t>ShowSubtitles</t>
      <l>NextSubtitle</l>
      <left>StepBack</left>
      <right>StepForward</right>
      <up>BigStepForward</up>
      <down>BigStepBack</down>
      <a>AudioDelay</a>
      <!--<escape>Fullscreen</escape>-->
<escape>Stop</escape>
      <v>XBMC.ActivateWindow(Teletext)</v>
    </keyboard>
  </FullscreenVideo>
  <VideoTimeSeek>
    <keyboard>
      <return>Select</return>
      <enter>Select</enter>
    </keyboard>
  </VideoTimeSeek>
  <FullscreenInfo>
    <keyboard>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>StepForward</period>
      <backspace>Close</backspace>
      <o>CodecInfo</o>
      <i>Close</i>
      <d mod="ctrl">Close</d>
      <m>OSD</m>
    </keyboard>
  </FullscreenInfo>
  <PlayerControls>
    <keyboard>
      <escape>Close</escape>
      <m>close</m>
    </keyboard>
  </PlayerControls>
  <Visualisation>
    <keyboard>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>SkipNext</period>
      <comma>SkipPrevious</comma>
      <return>ActivateWindow(MusicOSD)</return>
      <enter>ActivateWindow(MusicOSD)</enter>
      <m>ActivateWindow(MusicOSD)</m>
      <i>Info</i>
      <p>ActivateWindow(VisualisationPresetList)</p>
      <v>ActivateWindow(VisualisationSettings)</v>
      <n>ActivateWindow(MusicPlaylist)</n>
      <left>SkipPrevious</left>
      <right>SkipNext</right>
      <up>IncreaseRating</up>
      <down>DecreaseRating</down>      <!--<back>NextPreset</back>!-->
      <o>CodecInfo</o>
      <l>LockPreset</l>
      <escape>FullScreen</escape>
    </keyboard>
  </Visualisation>
  <MusicOSD>
    <keyboard>
      <escape>Close</escape>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>SkipNext</period>
      <comma>SkipPrevious</comma>
      <right mod="ctrl">SkipNext</right>          <!-- Skip -->
      <left mod="ctrl">SkipPrevious</left>      <!-- Replay -->
      <right mod="alt">SkipNext</right>          <!-- Skip -->
      <left mod="alt">SkipPrevious</left>      <!-- Replay -->
      <m>Close</m>
      <i>Info</i>
      <o>CodecInfo</o>
      <p>ActivateWindow(VisualisationPresetList)</p>
      <v>ActivateWindow(VisualisationSettings)</v>
      <n>ActivateWindow(MusicPlaylist)</n>
    </keyboard>
  </MusicOSD>
  <VisualisationSettings>
    <keyboard>
      <escape>Close</escape>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>SkipNext</period>
      <comma>SkipPrevious</comma>
      <m>Close</m>
      <i>Info</i>
      <o>CodecInfo</o>
      <p>ActivateWindow(VisualisationPresetList)</p>
      <v>Close</v>
      <n>ActivateWindow(MusicPlaylist)</n>
    </keyboard>
  </VisualisationSettings>
  <VisualisationPresetList>
    <keyboard>
      <escape>Close</escape>
      <f>FastForward</f>
      <r>Rewind</r>
      <period>SkipNext</period>
      <comma>SkipPrevious</comma>
      <m>Close</m>
      <i>Info</i>
      <o>CodecInfo</o>
      <p>Close</p>
      <v>Close</v>
      <n>ActivateWindow(MusicPlaylist)</n>
    </keyboard>
  </VisualisationPresetList>
  <SlideShow>
    <keyboard>
      <zero>ZoomNormal</zero>
      <one>ZoomLevel1</one>
      <two>ZoomLevel2</two>
      <three>ZoomLevel3</three>
      <four>ZoomLevel4</four>
      <five>ZoomLevel5</five>
      <six>ZoomLevel6</six>
      <seven>ZoomLevel7</seven>
      <eight>ZoomLevel8</eight>
      <nine>ZoomLevel9</nine>
      <i>Info</i>
      <o>CodecInfo</o>
      <period>NextPicture</period>
      <comma>PreviousPicture</comma>
      <plus>ZoomIn</plus>
      <minus>ZoomOut</minus>
      <r>Rotate</r>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </SlideShow>
  <ScreenCalibration>
    <keyboard>
      <return>NextCalibration</return>
      <enter>NextCalibration</enter>
      <d>ResetCalibration</d>
      <r>NextResolution</r>
    </keyboard>
  </ScreenCalibration>
  <GUICalibration>
    <keyboard>
      <return>NextCalibration</return>
      <enter>NextCalibration</enter>
      <d>ResetCalibration</d>
    </keyboard>
  </GUICalibration>
  <SelectDialog>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
    </keyboard>
  </SelectDialog>
  <VideoOSD>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
      <m>Close</m>
      <g mod="ctrl">close</g> <!-- MCE Guide button -->
      <i>Info</i>
      <o>CodecInfo</o>
    </keyboard>
  </VideoOSD>
  <VideoMenu>
    <keyboard>
      <opensquarebracket>BigStepForward</opensquarebracket>
      <closesquarebracket>BigStepBack</closesquarebracket>
      <m>OSD</m>
      <i>Info</i>
      <o>CodecInfo</o>
      <z>AspectRatio</z>
      <t>ShowSubtitles</t>
      <l>NextSubtitle</l>
      <a>AudioDelay</a>
      <escape>Fullscreen</escape>
      <return>Select</return>
      <enter>Select</enter>      <!-- backspace>Fullscreen</backspace -->
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </VideoMenu>
  <OSDVideoSettings>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
      <i>Info</i>
      <o>CodecInfo</o>
    </keyboard>
  </OSDVideoSettings>
  <OSDAudioSettings>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
      <i>Info</i>
      <o>CodecInfo</o>
    </keyboard>
  </OSDAudioSettings>
  <VideoBookmarks>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
    </keyboard>
  </VideoBookmarks>
  <MyVideoLibrary>
    <keyboard>
      <delete>Delete</delete>
      <space>Playlist</space>
      <w>ToggleWatched</w>
    </keyboard>
  </MyVideoLibrary>
  <MyVideoFiles>
    <keyboard>
      <space>Playlist</space>
      <q>Queue</q>
      <w>ToggleWatched</w>
    </keyboard>
  </MyVideoFiles>
  <MyVideoPlaylist>
    <keyboard>
      <backspace>Playlist</backspace>      <!-- Close playlist -->
      <space>Playlist</space>      <!-- Close playlist -->
      <delete>Delete</delete>
      <u>MoveItemUp</u>
      <d>MoveItemDown</d>
    </keyboard>
  </MyVideoPlaylist>
  <ContextMenu>
    <keyboard>
      <c>Close</c>
      <menu>Close</menu>
      <backspace>Close</backspace>
    </keyboard>
  </ContextMenu>
  <FileStackingDialog>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </FileStackingDialog>
  <Scripts>
    <keyboard>
      <i>info</i>
    </keyboard>
  </Scripts>
  <Weather>
    <keyboard>
      <backspace>PreviousMenu</backspace>
      <key id='65446'>PreviousMenu</key>
    </keyboard>
  </Weather>
  <Settings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </Settings>
  <MyPicturesSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </MyPicturesSettings>
  <MyProgramsSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </MyProgramsSettings>
  <MyWeatherSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </MyWeatherSettings>
  <MyMusicSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </MyMusicSettings>
  <SystemSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </SystemSettings>
  <MyVideosSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </MyVideosSettings>
  <NetworkSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </NetworkSettings>
  <AppearanceSettings>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </AppearanceSettings>
  <Profiles>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </Profiles>
  <systeminfo>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </systeminfo>
  <shutdownmenu>
    <keyboard>
      <backspace>PreviousMenu</backspace>
      <s>Close</s>  
    </keyboard>
  </shutdownmenu>
  <submenu>
    <keyboard>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </submenu>
  <MusicInformation>
    <keyboard>
      <backspace>Close</backspace>
      <key id='65446'>Close</key>
    </keyboard>
  </MusicInformation>
  <MovieInformation>
    <keyboard>
      <backspace>Close</backspace>
      <i>Close</i>
      <d mod="ctrl">Close</d>
      <key id='65446'>Close</key>
    </keyboard>
  </MovieInformation>
  <AddonInformation>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </AddonInformation>
  <AddonSettings>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </AddonSettings>
  <TextViewer>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </TextViewer>
  <LockSettings>
    <keyboard>
      <escape>Close</escape>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </LockSettings>
  <ProfileSettings>
    <keyboard>
      <escape>Close</escape>
      <backspace>PreviousMenu</backspace>
    </keyboard>
  </ProfileSettings>
  <PictureInfo>
    <keyboard>
      <period>NextPicture</period>
      <comma>PreviousPicture</comma>
      <i>Close</i>
      <d mod="ctrl">Close</d>
      <o>Close</o>
      <backspace>Close</backspace>
      <space>Pause</space>
    </keyboard>
  </PictureInfo>
  <Teletext>
    <keyboard>
      <backspace>Close</backspace>
      <escape>Close</escape>
      <v>Close</v>
    </keyboard>
  </Teletext>
  <Favourites>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </Favourites>
  <NumericInput>
    <keyboard>
      <backspace>Close</backspace>
    </keyboard>
  </NumericInput>
</keymap>

********************************************************************************

remote_logitech525.xml

<!-- This file contains the mapping of keys (gamepad, remote, and keyboard) to actions within XBMC -->
<!-- The <global> section is a fall through - they will only be used if the button is not          -->
<!-- used in the current window's section.  Note that there is only handling                       -->
<!-- for a single action per button at this stage.                                                 -->
<!-- For joystick/gamepad configuration under linux/win32, see below as it differs from xbox       -->
<!-- gamepads.                                                                                     -->

<!-- The format is:                      -->
<!--    <device>                         -->
<!--      <button>action</button>        -->
<!--    </device>                        -->

<!-- To map keys from other remotes using the RCA protocol, you may add <universalremote> blocks -->
<!-- In this case, the tags used are <obc#> where # is the original button code (OBC) of the key -->
<!-- You set it up by adding a <universalremote> block to the window or <global> section:       -->
<!--    <universalremote>             -->
<!--       <obc45>Stop</obc45>         -->
<!--    </universalremote>            -->

<!-- Note that the action can be a built-in function.                 -->
<!--  eg <B>XBMC.ActivateWindow(MyMusic)</B>                         -->
<!-- would automatically go to My Music on the press of the B button. -->

<!-- Joysticks / Gamepads:                                                                    -->
<!--   See the sample PS3 controller configuration below for the format.                      -->
<!--                                                                                          -->
<!--  Joystick Name:                                                                          -->
<!--   Do 'cat /proc/bus/input/devices' or see your xbmc log file  to find the names of       -->
<!--   detected joysticks. The name used in the configuration should match the detected name. -->
<!--                                                                                          -->
<!--  Button Ids:                                                                             -->
<!--   'id' is the button ID used by SDL. Joystick button ids of connected joysticks appear   -->
<!--   in xbmc.log when they are pressed. Use your log to map custom buttons to actions.      -->
<!--                                                                                          -->
<!--  Axis Ids / Analog Controls                                                              -->
<!--   Coming soon.                                                                           -->
<!--  This File is a modified version of the default remote.xml within XBMC.                  -->
<!--   It is set for a Logitech Harmony 525 which is mimicking a MCE USB remote.              -->
<keymap>
  <global>
    <remote>
      <play>Play</play>
      <pause>Pause</pause>
      <stop>Stop</stop>
      <forward>FastForward</forward>
      <reverse>Rewind</reverse>
      <left>Left</left>
      <right>Right</right>
      <up>Up</up>
      <down>Down</down>
      <select>Select</select>
      <enter>PreviousMenu</enter> 
      <pageplus>PageUp</pageplus>
      <pageminus>PageDown</pageminus>
      <back>ParentDir</back>
      <menu>ContextMenu</menu>
      <title>ContextMenu</title>
      <info>Info</info>
      <skipplus>SkipNext</skipplus>
      <skipminus>SkipPrevious</skipminus>
      <display>FullScreen</display>
      <start>PreviousMenu</start>
      <record>Queue</record>
      <volumeplus>VolumeUp</volumeplus>
      <volumeminus>VolumeDown</volumeminus>
      <channelminus>PageDown</channelminus>
      <channelplus>PageUp</channelplus>
      <mute>Mute</mute>
      <power>XBMC.ShutDown()</power>
      <myvideo>XBMC.ActivateWindow(MyVideos)</myvideo>
      <mymusic>XBMC.ActivateWindow(MyMusic)</mymusic>
      <mypictures>XBMC.ActivateWindow(MyPictures)</mypictures>
      <mytv>XBMC.ActivateWindow(VideoLibrary,TvShows)</mytv>
      <green>Queue</green>
      <yellow>XBMC.ActivateWindow(Music,Playlists)</yellow>
      <blue>Playlist</blue>
      <clear>Playlist.Clear</clear>
      <hash>XBMC.ActivateWindow(Music,Playlists)</hash>
      <zero>XBMC.Quit</zero>
      <one>Number1</one>
      <two>JumpSMS2</two>
      <three>JumpSMS3</three>
      <four>JumpSMS4</four>
      <five>JumpSMS5</five>
      <six>JumpSMS6</six>
      <seven>JumpSMS7</seven>
      <eight>JumpSMS8</eight>
      <nine>JumpSMS9</nine>
    </remote>
  </global>
  <Home>
    <remote>
      <green>XBMC.ActivateWindow(Music,Playlists)</green>
      <play>XBMC.ActivateWindow(Music,Playlists)</play>
      <info>XBMC.ActivateWindow(SystemInfo)</info>
      <back>XBMC.ActivateWindow(shutdownmenu)</back>
    </remote>
  </Home>
  <MyFiles>
    <remote>
      <channelminus>PageDown</channelminus>
      <channelplus>PageUp</channelplus>
      <star>Highlight</star>
    </remote>
  </MyFiles>
  <MyMusicPlaylist>
    <remote>
      <clear>Playlist.Clear</clear>
      <red>Delete</red>
      <yellow>XBMC.PlayerControl(Random)</yellow>
      <channelminus>MoveItemDown</channelminus>
      <channelplus>MoveItemUp</channelplus>      
    </remote>
  </MyMusicPlaylist>
  <MyMusicPlaylistEditor>
    <remote>
      <red>Delete</red>
    </remote>
  </MyMusicPlaylistEditor>
  <MyMusicFiles>
    <remote>
    </remote>
  </MyMusicFiles>
  <MyMusicLibrary>
    <remote>
    </remote>
  </MyMusicLibrary>
  <FullscreenVideo>
    <remote>
      <one>Number1</one>
      <two>Number2</two>
      <three>Number3</three>
      <four>Number4</four>
      <five>Number5</five>
      <six>Number6</six>
      <seven>Number7</seven>
      <eight>Number8</eight>
      <nine>Number9</nine>
      <left>StepBack</left>
      <right>StepForward</right>
      <up>BigStepForward</up>
      <down>BigStepBack</down>
      <back>Stop</back>
      <menu>OSD</menu>
      <start>OSD</start>
      <select>AspectRatio</select>
      <title>CodecInfo</title>
      <info>Info</info>
      <teletext>XBMC.ActivateWindow(Teletext)</teletext>
      <subtitle>NextSubtitle</subtitle>
      <yellow>ShowSubtitles</yellow>
      <language>AudioNextLanguage</language>
      <channelminus>AudioDelayMinus</channelminus>
      <channelplus>AudioDelayPlus</channelplus>
    </remote>
  </FullscreenVideo>
  <VideoTimeSeek>
    <remote>
      <select>Select</select>
      <enter>Select</enter>
    </remote>
  </VideoTimeSeek>
  <FullscreenInfo>
    <remote>
      <title>CodecInfo</title>
      <info>Back</info>
      <menu>OSD</menu>
    </remote>
  </FullscreenInfo>
  <PlayerControls>
    <remote>
      <menu>Back</menu>
      <back>Close</back>
    </remote>
  </PlayerControls>
  <Visualisation>
    <remote>
      <left>PreviousPreset</left>
      <right>NextPreset</right>
      <up>IncreaseRating</up>
      <down>DecreaseRating</down>
      <back>LockPreset</back>
      <title>CodecInfo</title>
      <select>XBMC.ActivateWindow(VisualisationPresetList)</select>
      <menu>XBMC.ActivateWindow(MusicOSD)</menu>
      <start>XBMC.ActivateWindow(MusicOSD)</start>
      <info>Info</info>
    </remote>
  </Visualisation>
  <MusicOSD>
    <remote>
      <menu>Back</menu>
      <back>Close</back>
      <title>Info</title>
      <info>CodecInfo</info>
    </remote>
  </MusicOSD>
  <VisualisationSettings>
    <remote>
      <menu>Back</menu>
      <back>Close</back>
    </remote>
  </VisualisationSettings>
  <VisualisationPresetList>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
    </remote>
  </VisualisationPresetList>
  <SlideShow>
    <remote>
      <one>ZoomLevel1</one>
      <two>ZoomLevel2</two>
      <three>ZoomLevel3</three>
      <four>ZoomLevel4</four>
      <five>ZoomLevel5</five>
      <six>ZoomLevel6</six>
      <seven>ZoomLevel7</seven>
      <eight>ZoomLevel8</eight>
      <nine>ZoomLevel9</nine>
      <info>CodecInfo</info>
      <skipplus>NextPicture</skipplus>
      <skipminus>PreviousPicture</skipminus>
      <title>Info</title>
      <select>Rotate</select>
    </remote>
  </SlideShow>
  <ScreenCalibration>
    <remote>
      <select>NextCalibration</select>
      <display>NextResolution</display>
      <xbox>NextResolution</xbox>
    </remote>
  </ScreenCalibration>
  <GUICalibration>
    <remote>
      <select>NextCalibration</select>
    </remote>
  </GUICalibration>
  <VideoOSD>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
      <start>Back</start>
    </remote>
  </VideoOSD>
  <VideoMenu>
    <remote>
      <menu>OSD</menu>
      <info>Info</info>
      <title>CodecInfo</title>
      <one>Number1</one>
      <two>Number2</two>
      <three>Number3</three>
      <four>Number4</four>
      <five>Number5</five>
      <six>Number6</six>
      <seven>Number7</seven>
      <eight>Number8</eight>
      <nine>Number9</nine>
      <play>Select</play>
    </remote>
  </VideoMenu>
  <OSDVideoSettings>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
      <start>Back</start>
    </remote>
  </OSDVideoSettings>
  <OSDAudioSettings>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
      <start>Back</start>
    </remote>
  </OSDAudioSettings>
  <VideoBookmarks>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
      <start>Back</start>
    </remote>
  </VideoBookmarks>
  <MyVideoLibrary>
    <remote>
    </remote>
  </MyVideoLibrary>
  <MyVideoFiles>
    <remote>
    </remote>
  </MyVideoFiles>
  <MyVideoPlaylist>
    <remote>
      <channelminus>MoveItemDown</channelminus>
      <channelplus>MoveItemUp</channelplus>
    </remote>
  </MyVideoPlaylist>
  <VirtualKeyboard>
    <remote>
      <back>BackSpace</back>
      <star>Shift</star>
      <one>Number1</one>
      <two>Number2</two>
      <three>Number3</three>
      <four>Number4</four>
      <five>Number5</five>
      <six>Number6</six>
      <seven>Number7</seven>
      <eight>Number8</eight>
      <nine>Number9</nine>
      <enter>Enter</enter>
      <pageminus>CursorLeft</pageminus>
      <pageplus>CursorRight</pageplus>
    </remote>
  </VirtualKeyboard>
  <ContextMenu>
    <remote>
      <title>Back</title>
    </remote>
  </ContextMenu>
  <Scripts>
    <remote>
      <info>info</info>
    </remote>
  </Scripts>
  <NumericInput>
    <remote>
      <one>Number1</one>
      <two>Number2</two>
      <three>Number3</three>
      <four>Number4</four>
      <five>Number5</five>
      <six>Number6</six>
      <seven>Number7</seven>
      <eight>Number8</eight>
      <nine>Number9</nine>
      <enter>Enter</enter>
      <back>BackSpace</back>
    </remote>
  </NumericInput>
  <MusicInformation>
    <remote>
      <info>Back</info>
    </remote>
  </MusicInformation>
  <MovieInformation>
    <remote>
      <info>Back</info>
    </remote>
  </MovieInformation>
  <LockSettings>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
    </remote>
  </LockSettings>
  <ProfileSettings>
    <remote>
      <back>Close</back>
      <menu>Back</menu>
    </remote>
  </ProfileSettings>
  <PictureInfo>
    <remote>
      <skipplus>NextPicture</skipplus>
      <skipminus>PreviousPicture</skipminus>
      <info>Back</info>
    </remote>
  </PictureInfo>
  <Teletext>
    <remote>
      <back>Close</back>
      <one>number1</one>
      <two>number2</two>
      <three>number3</three>
      <four>number4</four>
      <five>number5</five>
      <six>number6</six>
      <seven>number7</seven>
      <eight>number8</eight>
      <nine>number9</nine>
      <red>Red</red>
      <green>Green</green>
      <yellow>Yellow</yellow>
      <blue>Blue</blue>
      <info>Info</info>
      <menu>Back</menu>
      <start>Back</start>
      <teletext>Back</teletext>
    </remote>
  </Teletext>
  <AddonSettings>
    <remote>
    </remote>
  </AddonSettings>
</keymap>