| 1 | #!/usr/bin/perl -w |
|---|
| 2 | |
|---|
| 3 | # Augment XMLTV start/stop times with the local timezone if MythTV's |
|---|
| 4 | # TimeOffset setting is anything other than "None" |
|---|
| 5 | # * to be used as a postprocessor for XMLTV data |
|---|
| 6 | # * can be used in conjunction with 'shepherd' XMLTV reconciler or standalone |
|---|
| 7 | # (pipe-through) |
|---|
| 8 | # * no configuration necessary |
|---|
| 9 | # |
|---|
| 10 | # input XMLTV files will either have programme start/stop with or without |
|---|
| 11 | # timezones. If no timezone is present, Shepherd assumes the input |
|---|
| 12 | # start/stop times are in 'localtime'. |
|---|
| 13 | # |
|---|
| 14 | # If MythTV's "TimeOffset" setting is set to anything other than 'None', |
|---|
| 15 | # this can cause programming information to be out: |
|---|
| 16 | # - if set to 'All', all programs will be out by the difference between |
|---|
| 17 | # GMT and locatime ('All' means MythTV is expecting all start/stop times |
|---|
| 18 | # in GMT) |
|---|
| 19 | # - if explicitly set to GMT +/- XX then this will cause programming to |
|---|
| 20 | # be out whenever there is a switchover to/from daylight savings |
|---|
| 21 | # |
|---|
| 22 | # this postprocessor addresses this by explicitly putting a timezone |
|---|
| 23 | # on every programme that doesn't already have one, BUT ONLY IF |
|---|
| 24 | # MythTV is configured to anything other than 'None'. |
|---|
| 25 | # |
|---|
| 26 | # provided your unix system is configured into the correct timezone, |
|---|
| 27 | # this will work just fine including boundaries crossing daylight savings. |
|---|
| 28 | # |
|---|
| 29 | # it means that it doesn't matter if MythTV's "TimeOffset" is set to |
|---|
| 30 | # 'All' or 'None', or something inbetween, the data will be right regardless. |
|---|
| 31 | |
|---|
| 32 | use strict; |
|---|
| 33 | my $progname = "augment_timezone"; |
|---|
| 34 | my $version = "0.13"; |
|---|
| 35 | |
|---|
| 36 | use XMLTV; |
|---|
| 37 | use POSIX qw(strftime mktime); |
|---|
| 38 | use Getopt::Long; |
|---|
| 39 | use IO::File; |
|---|
| 40 | use Shepherd::MythTV; |
|---|
| 41 | |
|---|
| 42 | $| = 1; |
|---|
| 43 | my %stats; |
|---|
| 44 | |
|---|
| 45 | my $opt = { }; |
|---|
| 46 | $opt->{output_file} = "output.xmltv"; |
|---|
| 47 | $opt->{debug} = 0; |
|---|
| 48 | |
|---|
| 49 | # parse command line |
|---|
| 50 | GetOptions( |
|---|
| 51 | 'output=s' => \$opt->{output_file}, |
|---|
| 52 | 'mysql_file=s' => \$opt->{mysql_file}, |
|---|
| 53 | 'timeoffset=s' => \$opt->{timeoffset}, |
|---|
| 54 | |
|---|
| 55 | 'region=i' => \$opt->{region}, # ignored |
|---|
| 56 | 'days=i' => \$opt->{days}, # ignored |
|---|
| 57 | 'offset=i' => \$opt->{offset}, # ignored |
|---|
| 58 | 'timezone=s' => \$opt->{timezone}, # ignored |
|---|
| 59 | 'channels_file=s' => \$opt->{channels_file}, # ignored |
|---|
| 60 | 'config-file=s' => \$opt->{configfile}, # ignored |
|---|
| 61 | |
|---|
| 62 | 'help' => \$opt->{help}, |
|---|
| 63 | 'verbose' => \$opt->{help}, |
|---|
| 64 | 'version' => \$opt->{version}, |
|---|
| 65 | 'ready' => \$opt->{ready}, |
|---|
| 66 | 'desc' => \$opt->{desc}, |
|---|
| 67 | 'v' => \$opt->{version}); |
|---|
| 68 | |
|---|
| 69 | printf "%s v%s\n",$progname,$version; |
|---|
| 70 | |
|---|
| 71 | if ($opt->{version} || $opt->{desc} || $opt->{help} || $opt->{ready} || |
|---|
| 72 | $opt->{output_file} eq "") { |
|---|
| 73 | printf "Automatically adjust the XMLTV start/stop timezone based on MythTV's\n". |
|---|
| 74 | "TImeOffset setting.\n" if $opt->{desc}; |
|---|
| 75 | |
|---|
| 76 | printf "$progname is ready for operation.\n" if ($opt->{ready}); |
|---|
| 77 | |
|---|
| 78 | printf "No --output file specified.\n" if ($opt->{output_file} eq ""); |
|---|
| 79 | |
|---|
| 80 | if ($opt->{help} || $opt->{output_file} eq "") { |
|---|
| 81 | my $default_loc = &Shepherd::MythTV::standard_mysql_locations; |
|---|
| 82 | print<<EOF |
|---|
| 83 | |
|---|
| 84 | usage: $0 [options] {FILE(s)} |
|---|
| 85 | |
|---|
| 86 | Supported options include: |
|---|
| 87 | --output={file} Send final XMLTV output to {file} (default: $opt->{output_file}) |
|---|
| 88 | --mysql_file={file} File where we look for mythtv database user/pass/dbi (default: $default_loc) |
|---|
| 89 | --timeoffset={s} Specify MythTV's setting, rather than try to look it up |
|---|
| 90 | in MythTV's database. (E.g. "Auto", "None", "+1000") |
|---|
| 91 | |
|---|
| 92 | EOF |
|---|
| 93 | ; |
|---|
| 94 | } |
|---|
| 95 | exit(0); |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | unless ($opt->{timeoffset}) { |
|---|
| 99 | # Specify a non-standard location for mysql.txt |
|---|
| 100 | Shepherd::MythTV::setup($opt->{mysql_file}) if ($opt->{mysql_file}); |
|---|
| 101 | |
|---|
| 102 | my $sql = "SELECT data FROM settings WHERE value LIKE 'TimeOffset'"; |
|---|
| 103 | ($opt->{timeoffset}) = Shepherd::MythTV::query($sql); |
|---|
| 104 | unless ($opt->{timeoffset}) { |
|---|
| 105 | print " Won't augment any timezones!\n" . |
|---|
| 106 | " Assuming MythTV's timezone is \"None\".\n". |
|---|
| 107 | " *** If this is wrong, guide data may be in wrong timezone! ***\n\n"; |
|---|
| 108 | } |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | $opt->{timeoffset} = "None" if (!defined $opt->{timeoffset}); |
|---|
| 112 | if ($opt->{timeoffset} eq "None") { |
|---|
| 113 | print " - MythTV's TimeOffset setting is set to \"None\". No need to do anything.\n"; |
|---|
| 114 | } else { |
|---|
| 115 | printf " - MythTV's TimeOffset setting is set to \"%s\". Adding timezones.\n",$opt->{timeoffset}; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | my %writer_args = ( encoding => 'ISO-8859-1' ); |
|---|
| 119 | my $fh = new IO::File(">".$opt->{output_file}) || die "can't open $opt->{output_file} for writing: $!"; |
|---|
| 120 | $writer_args{OUTPUT} = $fh; |
|---|
| 121 | |
|---|
| 122 | my $writer = new XMLTV::Writer(%writer_args); |
|---|
| 123 | $writer->start( { |
|---|
| 124 | 'source-info-name' => "$progname $version", |
|---|
| 125 | 'generator-info-name' => "$progname $version"} ); |
|---|
| 126 | |
|---|
| 127 | foreach my $file (@ARGV) { |
|---|
| 128 | printf " - parsing: %s\n", ($file eq "-" ? "(from-stdin, hit control-D to finish)" : $file); |
|---|
| 129 | XMLTV::parsefiles_callback(undef, undef, \&channel_cb,\&programme_cb, $file); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | $writer->end(); |
|---|
| 133 | |
|---|
| 134 | printf "Finished parsing, output in $opt->{output_file}\n"; |
|---|
| 135 | printf "STATS: TimeOffset=".$opt->{timeoffset}; |
|---|
| 136 | foreach my $k (keys %stats) { |
|---|
| 137 | printf ", %d %s", $stats{$k}, $k; |
|---|
| 138 | } |
|---|
| 139 | printf "\n"; |
|---|
| 140 | |
|---|
| 141 | exit(0); |
|---|
| 142 | |
|---|
| 143 | ############################################################################## |
|---|
| 144 | |
|---|
| 145 | sub channel_cb( $ ) |
|---|
| 146 | { |
|---|
| 147 | my $c = shift; |
|---|
| 148 | # printf "got channel ".Dumper($c); |
|---|
| 149 | $writer->write_channel($c); |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | ############################################################################## |
|---|
| 153 | |
|---|
| 154 | sub programme_cb( $ ) |
|---|
| 155 | { |
|---|
| 156 | my $prog=shift; |
|---|
| 157 | |
|---|
| 158 | if ($opt->{timeoffset} ne "None") { |
|---|
| 159 | # if there is no timezone present in start time, put one there |
|---|
| 160 | if (($prog->{start} !~ /\+/) && ($prog->{start} !~ /\-/)) { |
|---|
| 161 | $prog->{start} = POSIX::strftime("%Y%m%d%H%M00 %z",localtime(parse_xmltv_date($prog->{start}))); |
|---|
| 162 | $stats{start_tz_added}++; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | # if there is no timezone present in stop time, put one there |
|---|
| 166 | if (($prog->{stop} !~ /\+/) && ($prog->{stop} !~ /\-/)) { |
|---|
| 167 | $prog->{stop} = POSIX::strftime("%Y%m%d%H%M00 %z",localtime(parse_xmltv_date($prog->{stop}))); |
|---|
| 168 | $stats{stop_tz_added}++; |
|---|
| 169 | } |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | $writer->write_programme($prog); |
|---|
| 173 | } |
|---|
| 174 | |
|---|
| 175 | ############################################################################## |
|---|
| 176 | |
|---|
| 177 | # strptime type date parsing - BUT - if no timezone is present, treat time |
|---|
| 178 | # as being in localtime rather than the various other perl implementation |
|---|
| 179 | # which treat it as being in UTC/GMT |
|---|
| 180 | |
|---|
| 181 | sub parse_xmltv_date |
|---|
| 182 | { |
|---|
| 183 | my $datestring = shift; |
|---|
| 184 | my @t; # 0=sec,1=min,2=hour,3=day,4=month,5=year,6=wday,7=yday,8=isdst |
|---|
| 185 | my $tz_offset = 0; |
|---|
| 186 | |
|---|
| 187 | if ($datestring =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/) { |
|---|
| 188 | ($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); |
|---|
| 189 | ($t[6],$t[7],$t[8]) = (-1,-1,-1); |
|---|
| 190 | |
|---|
| 191 | my $e = mktime(@t); |
|---|
| 192 | |
|---|
| 193 | # if input data has a timezone offset, then offset by that |
|---|
| 194 | if ($datestring =~ /\+(\d{2})(\d{2})/) { |
|---|
| 195 | $tz_offset = calc_gmt_offset($e) - (($1*(60*60)) + ($2*60)); |
|---|
| 196 | } elsif ($datestring =~ /\-(\d{2})(\d{2})/) { |
|---|
| 197 | $tz_offset = calc_gmt_offset($e) + (($1*(60*60)) + ($2*60)); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | return ($e+$tz_offset) if ($e > 1); |
|---|
| 201 | } |
|---|
| 202 | return undef; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | ############################################################################## |
|---|
| 206 | |
|---|
| 207 | # given a particular date (in epoch time), return the local timezone offset |
|---|
| 208 | # on that date in -/+ seconds from GMT |
|---|
| 209 | |
|---|
| 210 | sub calc_gmt_offset |
|---|
| 211 | { |
|---|
| 212 | my $e = shift; |
|---|
| 213 | my $gmt_offset; |
|---|
| 214 | |
|---|
| 215 | my $tzstring = strftime("%z", localtime($e)); |
|---|
| 216 | $gmt_offset = (60*60) * int(substr($tzstring,1,2)); # hr |
|---|
| 217 | $gmt_offset += (60 * int(substr($tzstring,3,2))); # min |
|---|
| 218 | $gmt_offset *= -1 if (substr($tzstring,0,1) eq "-"); # +/- |
|---|
| 219 | |
|---|
| 220 | return $gmt_offset; |
|---|
| 221 | } |
|---|