root/postprocessors/augment_timezone @ 641

Revision 641, 8.8 kB (checked in by max, 6 years ago)

More explicit warning from augment_timezone when unable to lookup MythTV TZ

  • Property svn:executable set to *
Line 
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
32use strict;
33my $progname = "augment_timezone";
34my $version = "0.12";
35
36use XMLTV;
37use POSIX qw(strftime mktime);
38use Getopt::Long;
39use DBI;
40use IO::File;
41
42$| = 1;
43my $dbh;
44my %stats;
45
46my $opt = { };
47$opt->{output_file} =   "output.xmltv";
48$opt->{mysql_file} =    "/usr/local/share/mythtv/mysql.txt".
49                        ":/usr/share/mythtv/mysql.txt".
50                        ":$ENV{HOME}/.mythtv/mysql.txt".
51                        ":/home/mythtv/.mythtv/mysql.txt".
52                        ":/root/.mythtv/mysql.txt".
53                        ":/etc/mythtv/mysql.txt";
54
55$opt->{debug} =                 0;
56
57# parse command line
58GetOptions(
59        'output=s'              => \$opt->{output_file},
60        'mysql_file=s'          => \$opt->{mysql_file},
61        'timeoffset=s'          => \$opt->{timeoffset},
62
63        'region=i'              => \$opt->{region},             # ignored
64        'days=i'                => \$opt->{days},               # ignored
65        'offset=i'              => \$opt->{offset},             # ignored
66        'timezone=s'            => \$opt->{timezone},           # ignored
67        'channels_file=s'       => \$opt->{channels_file},      # ignored
68        'config-file=s'         => \$opt->{configfile},         # ignored
69
70        'help'                  => \$opt->{help},
71        'verbose'               => \$opt->{help},
72        'version'               => \$opt->{version},
73        'ready'                 => \$opt->{ready},
74        'desc'                  => \$opt->{desc},
75        'v'                     => \$opt->{version});
76
77printf "%s v%s\n",$progname,$version;
78
79if ($opt->{version} || $opt->{desc} || $opt->{help} || $opt->{ready} ||
80    $opt->{output_file} eq "") {
81        printf "Automatically adjust the XMLTV start/stop timezone based on MythTV's\n".
82            "TImeOffset setting.\n" if $opt->{desc};
83
84        printf "$progname is ready for operation.\n" if ($opt->{ready});
85
86        printf "No --output file specified.\n" if ($opt->{output_file} eq "");
87
88        if ($opt->{help} || $opt->{output_file} eq "") {
89                print<<EOF
90
91usage: $0 [options] {FILE(s)}
92
93Supported options include:
94  --output={file}      Send final XMLTV output to {file} (default: $opt->{output_file})
95  --mysql_file={file}  File where we look for mythtv database user/pass/dbi (default: $opt->{mysql_file})
96  --timeoffset={s}     Specify MythTV's setting, rather than try to look it up
97                       in MythTV's database. (E.g. "Auto", "None", "+1000")
98
99EOF
100;
101        }
102        exit(0);
103}
104
105&get_database_settings unless ($opt->{timeoffset});
106
107$opt->{timeoffset} = "None" if (!defined $opt->{timeoffset});
108if ($opt->{timeoffset} eq "None") {
109        print " - MythTV's TimeOffset setting is set to \"None\". No need to do anything.\n";
110} else {
111        printf " - MythTV's TimeOffset setting is set to \"%s\". Adding timezones.\n",$opt->{timeoffset};
112}
113
114my %writer_args = ( encoding => 'ISO-8859-1' );
115my $fh = new IO::File(">".$opt->{output_file}) || die "can't open $opt->{output_file} for writing: $!";
116$writer_args{OUTPUT} = $fh;
117
118my $writer = new XMLTV::Writer(%writer_args);
119$writer->start( {
120        'source-info-name' => "$progname $version",
121        'generator-info-name' => "$progname $version"} );
122
123foreach my $file (@ARGV) {
124        printf " - parsing: %s\n", ($file eq "-" ? "(from-stdin, hit control-D to finish)" : $file);
125        XMLTV::parsefiles_callback(undef, undef, \&channel_cb,\&programme_cb, $file);
126}
127
128$writer->end();
129
130printf "Finished parsing, output in $opt->{output_file}\n";
131printf "STATS: TimeOffset=".$opt->{timeoffset};
132foreach my $k (keys %stats) {
133        printf ", %d %s", $stats{$k}, $k;
134}
135printf "\n";
136
137exit(0);
138
139##############################################################################
140
141sub channel_cb( $ )
142{
143        my $c = shift;
144        # printf "got channel ".Dumper($c);
145        $writer->write_channel($c);
146}
147
148##############################################################################
149
150sub programme_cb( $ )
151{
152        my $prog=shift;
153
154        if ($opt->{timeoffset} ne "None") {
155                # if there is no timezone present in start time, put one there
156                if (($prog->{start} !~ /\+/) && ($prog->{start} !~ /\-/)) {
157                        $prog->{start} = POSIX::strftime("%Y%m%d%H%M00 %z",localtime(parse_xmltv_date($prog->{start})));
158                        $stats{start_tz_added}++;
159                }
160
161                # if there is no timezone present in stop time, put one there
162                if (($prog->{stop} !~ /\+/) && ($prog->{stop} !~ /\-/)) {
163                        $prog->{stop} = POSIX::strftime("%Y%m%d%H%M00 %z",localtime(parse_xmltv_date($prog->{stop})));
164                        $stats{stop_tz_added}++;
165                }
166        }
167
168        $writer->write_programme($prog);
169}
170
171##############################################################################
172
173# strptime type date parsing - BUT - if no timezone is present, treat time
174# as being in localtime rather than the various other perl implementation
175# which treat it as being in UTC/GMT
176
177sub parse_xmltv_date
178{
179        my $datestring = shift;
180        my @t; # 0=sec,1=min,2=hour,3=day,4=month,5=year,6=wday,7=yday,8=isdst
181        my $tz_offset = 0;
182
183        if ($datestring =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/) {
184                ($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);
185                ($t[6],$t[7],$t[8]) = (-1,-1,-1);
186
187                my $e = mktime(@t);
188
189                # if input data has a timezone offset, then offset by that
190                if ($datestring =~ /\+(\d{2})(\d{2})/) {
191                        $tz_offset = calc_gmt_offset($e) - (($1*(60*60)) + ($2*60));
192                } elsif ($datestring =~ /\-(\d{2})(\d{2})/) {
193                        $tz_offset = calc_gmt_offset($e) + (($1*(60*60)) + ($2*60));
194                }
195
196                return ($e+$tz_offset) if ($e > 1);
197        }
198        return undef;
199}
200
201##############################################################################
202
203# given a particular date (in epoch time), return the local timezone offset
204# on that date in -/+ seconds from GMT
205
206sub calc_gmt_offset
207{
208        my $e = shift;
209        my $gmt_offset;
210
211        my $tzstring = strftime("%z", localtime($e));
212        $gmt_offset = (60*60) * int(substr($tzstring,1,2));     # hr
213        $gmt_offset += (60 * int(substr($tzstring,3,2)));       # min
214        $gmt_offset *= -1 if (substr($tzstring,0,1) eq "-");    # +/-
215
216        return $gmt_offset;
217}
218
219##############################################################################
220
221sub find_database_settings_file
222{
223        foreach my $cfgfile (split(/:/,$opt->{mysql_file})) {
224                return $cfgfile if ((-f $cfgfile) && (-r $cfgfile));
225        }
226
227        printf "\nWARNING: Could not find MythTV mysql.txt config file\n".
228               "         (looked in ".$opt->{mysql_file}.")\n".
229               "         Assuming MythTV's timezone is \"None\".\n".
230               "         *** If this is wrong, guide data may be in wrong timezone! ***\n\n";
231        $stats{missing_mysql_database_file}++;
232        return undef;
233}
234
235##############################################################################
236
237sub get_database_settings
238{
239        my $cfgfile = &find_database_settings_file;
240        return if (!defined $cfgfile);
241
242        #
243        # find database settings
244        #
245
246        if (!(open(F,"<$cfgfile"))) {
247                print "WARNING: could not read $cfgfile: $!\n".
248                      "Won't augment any timezones!\n";
249                $stats{mysql_database_file_unreadable}++;
250                return;
251        }
252
253        while (<F>) {
254                chop;
255
256                if ($_ =~ /^DBHostName=(.*)$/) {
257                        $opt->{dbhost} = $1;
258                } elsif ($_ =~ /^DBUserName=(.*)$/) {
259                        $opt->{dbuser} = $1;
260                } elsif ($_ =~ /^DBPassword=(.*)$/) {
261                         $opt->{dbpass} = $1;
262                } elsif ($_ =~ /^DBName=(.*)$/) {
263                        $opt->{dbname} = $1;
264                }
265        }
266        close(F);
267
268        #
269        # look up the timeoffset setting
270        #
271
272        if (!($dbh = DBI->connect("dbi:mysql:database=".$opt->{dbname}.":host=".$opt->{dbhost},$opt->{dbuser}, $opt->{dbpass}))) {
273                print "WARNING: Couldn't connect to database ".$opt->{dbname}.": $!\n".
274                      "         Won't augment any timezones!\n";
275                $stats{mysql_database_connect_failed}++;
276                return;
277        }
278
279        my $q = "SELECT data FROM settings WHERE value LIKE 'TimeOffset'";
280        if (!($opt->{timeoffset} = $dbh->selectrow_array($q))) {
281                print "WARNING: Couldn't query database: $q: $!\n".
282                      "         Won't augment any timezones!\n";
283                $stats{mysql_database_query_failed}++;
284                return;
285        }
286}
Note: See TracBrowser for help on using the browser.