#!/usr/bin/perl -w use strict; use Getopt::Long; use XMLTV; use POSIX qw(strftime mktime); $| = 1; my $progname = "setHDTV"; my $version = "0.01"; # originally written by Paul Andreassen 2007-12-08 # various parts from Shepherd 1.0 my $opt; ########################################################################## # strptime type date parsing - BUT - if no timezone is present, treat # time as being in localtime rather than the various other perl # implementation which treat it as being in UTC/GMT my $gmt_offset; sub parse_xmltv_date { my $datestring = shift; my @t; # 0=sec,1=min,2=hour,3=day,4=month,5=year,6=wday,7=yday,8=isdst my $tz_offset = 0; # work out GMT offset - we only do this once if (!$gmt_offset) { my $tzstring = strftime("%z", localtime(time)); $gmt_offset = (60*60) * int(substr($tzstring,1,2)); # hr $gmt_offset += (60 * int(substr($tzstring,3,2))); # min $gmt_offset *= -1 if (substr($tzstring,0,1) eq "-"); # +/- } if ($datestring =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/) { ($t[5],$t[4],$t[3],$t[2],$t[1],$t[0]) = (int($1)-1900,int($2)-1,int($3),int($4),int($5),0); ($t[6],$t[7],$t[8]) = (-1,-1,-1); # if input data has a timezone offset, then offset by that if ($datestring =~ /\+(\d{2})(\d{2})/) { $tz_offset = $gmt_offset - (($1*(60*60)) + ($2*60)); } elsif ($datestring =~ /\-(\d{2})(\d{2})/) { $tz_offset = $gmt_offset + (($1*(60*60)) + ($2*60)); } my $e = mktime(@t); return ($e+$tz_offset) if ($e > 1); } return undef; } ########################################################################## # # Shepherd::MythTV library #my $version = '0.7'; # This module provides some library functions for Shepherd components, # relieving them of the need to duplicate functionality. # # To use this library, components simply need to include the line: # # use Shepherd::MythTV; # # To make a single quick query: # Shepherd::MythTV::query($sql) # ... where $sql is an SQL statement. This will establish a database # connection and close if following the query. # # If instead you'd like to make multiple queries, open and close the # DB connection yourself: # Shepherd::MythTV::open_connection(); # while (...) { # Shepherd::MythTV::query($sql); # } # Shepherd::MythTV::close_connection(); # # Shepherd::MythTV::open_connection() returns the $dbh object if # successful, so you can manipulate connections yourself too. # #package Shepherd::MythTV; use DBI; my $dbh; my $db; my @tried; sub find_database_settings_file { my $cfgfile = shift; $cfgfile = &standard_mysql_locations unless ($cfgfile); foreach my $f (split(/:/,$cfgfile)) { next if (grep ($f eq $_, @tried)); return $f if ((-f $f) && (-r $f)); } print "\nWARNING: Could not find valid MythTV mysql.txt config file!\n". "Looked in: $cfgfile\n"; return undef; } sub standard_mysql_locations { return "/usr/local/share/mythtv/mysql.txt". ":/usr/share/mythtv/mysql.txt". ":$ENV{HOME}/.mythtv/mysql.txt". ":/home/mythtv/.mythtv/mysql.txt". ":/root/.mythtv/mysql.txt". ":/etc/mythtv/mysql.txt"; } # Find MythTV database settings # sub setup { my $cfgfile = shift; $cfgfile = &find_database_settings_file unless ($cfgfile); return unless ($cfgfile); unless (open(F,"<$cfgfile")) { print "ERROR: Couldn't read $cfgfile: $!\n"; return undef; } print "Reading MythTV DB settings from: $cfgfile\n" if !$opt->{quiet}; $db->{cfgfile} = $cfgfile; while () { chomp; $db->{$1} = $2 if ($_ =~ /^(DB.*?)=(.*)/); } close(F); return 1; } # # Send an SQL query to the MythTV DB. Will try standard locations to # find the MythTV mysql.txt file; if you want to specify a non-standard # location, first call Shepherd::MythTV::setup($file_location), where # $file_location is a colon-separated string of (potential) filenames. # # Note that data is returned in array form. # sub query { my $sql = shift; my $leave_open = 1; unless ($dbh) { &open_connection or return undef; $leave_open = 0; } my @ret = $dbh->selectrow_array($sql); &close_connection unless ($leave_open); return @ret; } sub open_connection { &setup unless ($db); unless ($db->{DBName} and $db->{DBHostName} and $db->{DBUserName} and $db->{DBPassword}) { print "ERROR: Missing essential DB connection info.\n"; return undef; } my $counter = 0; while (!($dbh = DBI->connect( "dbi:mysql:database=".$db->{DBName}.":host=".$db->{DBHostName}, $db->{DBUserName}, $db->{DBPassword}))) { # Sanity check; should never be required but we don't want infinite loops here! last if ($counter++ > 10); push @tried, $db->{cfgfile}; last unless (&setup); } unless ($dbh) { print "Couldn't connect to database $db->{DBName}.\n"; return undef; } return $dbh; } sub close_connection { $dbh->disconnect() if ($dbh); undef $dbh; } ########################################################################## my $xmlid_to_chanid; my $sth; my $count = 0; sub programme_cb { my $prog=shift; return if !(defined $prog->{title} && defined $prog->{title}->[0] && defined $prog->{title}->[0]->[0]); if (!defined $xmlid_to_chanid->{$prog->{channel}} || $xmlid_to_chanid->{$prog->{channel}} eq "NOT FOUND") { print "Don't know the chanid for xmlid $prog->{channel}\n" if !defined $xmlid_to_chanid->{$prog->{channel}}; $xmlid_to_chanid->{$prog->{channel}} = "NOT FOUND"; return; } my $hdtv = 0; # my $closecaptioned = 0; # (lots of grabbers) my $subtitled = 0; # (only citysearch) # stereo (don't set because almost always stereo and shepherd doesn't output except for foxtel_swf) $hdtv = 1 if defined $prog->{video} && defined $prog->{video}->{quality} && $prog->{video}->{quality} =~ /HDTV/i; if (defined $prog->{subtitles}) { foreach my $type (@{$prog->{subtitles}}) { $closecaptioned = 1 if $type->{type} =~ /teletext/i; $subtitled = 1 if $type->{type} =~ /onscreen/i; } } my $start = POSIX::strftime("%Y-%m-%d %H:%M:%S",localtime(parse_xmltv_date($prog->{start}))); my $stop = POSIX::strftime("%Y-%m-%d %H:%M:%S",localtime(parse_xmltv_date($prog->{stop}))); my $ret = $sth->execute($hdtv, $closecaptioned, $subtitled, $xmlid_to_chanid->{$prog->{channel}}->{chanid}, $start, $stop, $prog->{title}->[0]->[0]); $count += 1 if defined $ret; if (!defined $ret || defined $opt->{debug}) { print "$prog->{channel} $xmlid_to_chanid->{$prog->{channel}}->{chanid} " . "$prog->{start} $start $prog->{stop} $stop \'$prog->{title}->[0]->[0]\' " . "hdtv = $hdtv, cc = $closecaptioned, subtitled = $subtitled."; if (defined $ret) { print " Updated $ret.\n"; } else { print " Error updating @ $count.\n"; exit 4; } } } sub main { GetOptions( 'quiet' => \$opt->{quiet}, 'debug+' => \$opt->{debug}, 'help' => \$opt->{help}, 'h' => \$opt->{help}, 'version' => \$opt->{version}, 'v' => \$opt->{version}); print "$progname v$version\n" if !$opt->{quiet}; if ($opt->{help}) { print<{version}) || ($opt->{help})); eval { my $dbh = &open_connection(); return unless ($dbh); my $DBSchemaVer = $dbh->selectrow_array("SELECT data FROM settings WHERE value='DBSchemaVer';"); if (!defined $DBSchemaVer || $DBSchemaVer ne "1160") { print "Can't run this program on any MythTV version except 0.20\n"; exit 1; } $xmlid_to_chanid = $dbh->selectall_hashref("SELECT xmltvid,chanid FROM channel;", 'xmltvid'); if (defined $xmlid_to_chanid) { print "Read channel table xmltvid's.\n" if !$opt->{quiet}; } else { print "Can't read the channel table xmltvid's.\n"; exit 2; } $sth = $dbh->prepare("UPDATE program SET hdtv = ?, closecaptioned = ?, subtitled = ? " . "WHERE chanid = ? AND starttime = ? AND endtime = ? AND title = ? ;"); foreach my $file (@ARGV) { printf "Parsing: %s\n",($file eq "-" ? "(from-stdin, hit control-D to finish)" : $file) if !$opt->{quiet}; XMLTV::parsefiles_callback(undef, undef, undef, \&programme_cb, $file); } &close_connection; print "Successfully updated $count MythTV programs. Please now execute \"mythbackend --resched\".\n" if !$opt->{quiet}; #system("mythbackend --resched"); }; if ($@) { print "Error trying to access MythTV database: $@\n"; exit 3; } } &main;