root/postprocessors/flag_aus_hdtv @ 227

Revision 227, 16.7 kB (checked in by lincoln, 7 years ago)

force flag_aus_hdtv to be disabled (for now)

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl
2
3# flag_aus_hdtv
4#  checks the digital broadcasting australia (www.dba.org.au) site for
5#  their condensed program guide information for information such as
6#  widescreen, "high definition" (notwithstanding channel seven's
7#  definition as 576p), DD5.1 and updates supplied xmltv
8#  to map SD to SD+HD channels
9#  - Richard Dale <richard@interlink.com.au>, September 2006
10
11my $progname = "flag_aus_hdtv";
12my $version = "0.10.3";
13$| = 1;
14printf "%s v%s\n",$progname,$version;
15
16exit 1; # mark as broken for now
17
18use strict;
19use Getopt::Long;
20use LWP::UserAgent;
21use HTML::TreeBuilder;
22use IO::File;
23use XMLTV;
24use Data::Dumper;
25
26#
27# customizations
28#
29
30# Add overrides for HD - sometimes DBA doesn't always have the latest HD information about TV shows so set them here in the override
31my $hdoverride;
32$hdoverride->{TEN} = [ "The Handler", "N.Y.P.D. Blue", "The Office", "Battlestar Galactica" ];
33
34# Add translations for show names that differ between DBA and our normal tv_grab_au names
35my $translation;
36$translation->{'ACA'} = 'A Current Affair';
37$translation->{'CSI'} = 'CSI: Crime Scene Investigation';
38$translation->{'CSI-Miami'} = 'CSI: Miami';
39
40my $chan_name_translations;
41$chan_name_translations->{"Ten"} = "TEN";
42$chan_name_translations->{"ABC Main"} = "ABC";
43
44#
45# options
46#
47
48my $ua;
49$ua = LWP::UserAgent->new('timeout' => 30, 'keep_alive' => 1, 'agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-us)' );
50$ua->env_proxy;
51my %stats;
52my $channels, my $opt_channels;
53my $hdprog;
54my %amp = ( nbsp => ' ', qw{ amp & lt < gt > apos ' quot " } );
55
56my $opt;
57$opt->{dbaurl} = "http://www.dba.org.au/dtvprograms"; # This URL seems to work fine Sep 2006
58$opt->{output_file} = "output.xmltv";
59$opt->{region} = 94;
60$opt->{debug} = 1;
61
62&parse_command_line;
63exit 0 if ($opt->{version});
64&show_help if ($opt->{help});
65die "'--channels_file {file}' must be specified. see --help for details.\n" if (!$opt->{channels_file});
66die "'--dontflagalldays' not yet implemented.  sorry!\n" if ($opt->{dontflagalldays});
67
68&read_config_file($opt->{channels_file},1);
69
70$opt->{locality} = decode_region($opt->{region});
71die "no HDTV data available for region $opt->{region}" if (!$opt->{locality});
72
73my $data = &get_url($opt->{dbaurl},"Obtaining DBA data",0);
74&parse_data($data) if ($data);
75
76#
77# now eat up the XMLTV inputs we were given!
78#
79
80@ARGV = ('-') if not @ARGV;
81
82my %writer_args = ( encoding => 'ISO-8859-1' );
83my $fh = new IO::File(">".$opt->{output_file}) ||
84  die "can't open $opt->{output_file} for writing: $!";
85$writer_args{OUTPUT} = $fh;
86
87my $writer = new XMLTV::Writer(%writer_args);
88$writer->start( {
89        'source-info-name' => "$progname $version",
90        'generator-info-name' => "$progname $version"} );
91
92foreach my $file (@ARGV) {
93        printf "Parsing: %s\n",($file eq "-" ? "(from-stdin, hit control-D to finiah)" : $file);
94        XMLTV::parsefiles_callback(undef, undef, \&channel_cb,\&programme_cb, $file);
95}
96
97$writer->end();
98printf "Finished parsing, output in $opt->{output_file}\n";
99
100&print_stats;
101exit(0);   
102
103######################################################################################################
104# read settings
105
106sub read_config_file
107{
108        my($file,$die_on_failure) = @_;
109        if (!(-r $file)) {
110                die "file $file could not be read.  aborting.\n" if $die_on_failure;
111                return;
112        }
113        local (@ARGV, $/) = ($file);
114        no warnings 'all'; eval <>; die "$@" if $@;
115}
116
117######################################################################################################
118
119sub parse_command_line
120{
121        GetOptions(
122                'channels_file=s' => \$opt->{channels_file},
123                'config=s'      => \$opt->{config_file},
124                'region=i'      => \$opt->{region},
125                'dbaurl=s'      => \$opt->{dbaurl},
126                'output=s'      => \$opt->{output_file},
127                'dontflagalldays' => \$opt->{dontflagalldays},
128                'debug+'        => \$opt->{debug},
129                'help'          => \$opt->{help},
130                'h'             => \$opt->{help},
131                'v'             => \$opt->{version},
132                'version'       => \$opt->{version});
133}
134
135######################################################################################################
136
137sub show_help
138{
139        print<<EOF
140$progname $version
141
142options:
143  --channels_file=(f)  shepherd channels file (mandatory)
144  --region=(region)    region where we are (mandatory) (default: $opt->{region})
145                         (VIC)     90,93,94,95,98
146                         (NSW/ACT) 63,66,67,69,71,73,106,184
147                         (QLD)     75,78,79,114
148                         (SA)      81,82,83,85,86,107
149                         (WA)      101,102
150                       (no HDTV in TAS or NT apparently)
151  --dbaurl=(url)       URL where dba lists HDTV programming (default: $opt->{dbaurl})
152
153  --dontflagalldays    don't flag a programme as being in HD across all days
154                       if we see it in HD on one day.  (dba website is often
155                       incorrect with regard to what day a HD programme is on)
156                       (default: do)
157
158  --output=(f)         output XMLTV filename (default: $opt->{output})
159
160  --debug              increase debug level
161
162EOF
163;
164        exit(0);
165}
166
167######################################################################################################
168
169sub decode_region
170{
171        my $r = shift;
172
173        return "M" if (($r==90)||($r==93)||($r==94)||($r==95)||($r==98));
174        return "W" if (($r==101)||($r==102));
175        return "S" if (($r==63)||($r==66)||($r==67)||($r==69)||($r==71)||($r==73)||($r==76)||($r==106)||($r==184));
176        return "B" if (($r==75)||($r==78)||($r==79)||($r==114));
177        return "A" if (($r==81)||($r==82)||($r==83)||($r==85)||($r==86)||($r==107));
178        return undef;
179}
180
181######################################################################################################
182
183sub parse_data
184{
185        my $tree = HTML::TreeBuilder->new_from_content($data); 
186
187        # each daily section starts with:
188        # <TABLE class=tv...
189        # so we'll search for that and populate an array
190        my @chan_map;
191        for ($tree->look_down('_tag' => 'table', 'class' => 'tv')) {
192                my $firstrowdone=0;
193                my $dayname = "";
194
195                foreach my $row ($_->look_down('_tag' => 'tr')) {
196                        if ($firstrowdone == 0) { # Channel list is the first row
197                                $firstrowdone = 1;
198                                my $colnum = 0;
199                                for my $channel ($row->look_down('_tag' => 'td')) {
200                                        my $this_chan = $channel->as_text;
201                                        $colnum++;
202                                        $this_chan =~ s/[^\x20-\x7f]/ /g;
203                                        $this_chan =~ s/(^\s+|\s+$)//g;
204                                        next if ($this_chan eq "");
205
206                                        $this_chan = $chan_name_translations->{$this_chan}
207                                          if (defined $chan_name_translations->{$this_chan});
208
209                                        if (!defined $channels->{$this_chan}) {
210                                                printf "DBA listed unkown channel '$this_chan'! Ignored.\n";
211                                                $stats{unknown_channels}++;
212                                                next;
213                                        }
214
215                                        $chan_map[$colnum] = $this_chan;
216                                        #printf "got channel '$this_chan' in column $colnum\n";
217                                }
218                                next;
219                        }
220
221                        # These must be the program details
222                        my $colnum = 0;
223                        foreach my $session ($row->look_down('_tag' => 'td')) {
224                                $colnum++;
225                                if ($colnum == 1) {
226                                        # This session has the day name in it
227                                        my $newdayname = $session->as_text();
228                                        $newdayname =~ s/Night//;
229                                        $newdayname =~ s/Day//;
230                                        $newdayname =~ s/Sat/Saturday/;
231                                        $newdayname = "" if ($dayname =~ m/Subject to avail/i);
232
233                                        $dayname = $newdayname if ($newdayname =~ m/day$/);
234                                        next;
235                                }
236                 
237                                my $this_chan = $chan_map[$colnum]; 
238
239                                my $sessionshows = $session->as_HTML();
240                                $sessionshows =~ s/<p>/<br \/>/g; # Convert new paragraphs to newlines because they're not always consistent
241                                $sessionshows =~ s/<\/p>//g;
242                                $sessionshows =~ s/<u>//g;
243                                $sessionshows =~ s/<\/u>//g;
244                                $sessionshows =~ s/<\/td>//g;
245                                $sessionshows =~ s/<\/font>//g;
246                                $sessionshows =~ s/<font.*?>//g;
247                                $sessionshows =~ s/<td.*?>//g;
248                                #print "\nFound " . $session->as_HTML() . "\n\n";
249                                my @shows = split(/<br[\s\/]*>/,$sessionshows);
250                                foreach my $show (@shows) {
251                                        # Bodgy stuff to fix html markup
252                                        $show =~ s/\&amp;/\&/g; $show =~ s/\&apos;/\'/g; $show =~ s/\&quot;/\"/g; 
253                                        $show =~ s/\&#184;/\,/g; 
254                                        $show =~ s/\&#39;/\'/g; 
255                                        $show =~ s/\&nbsp;/\ /g; 
256
257                                        # Remove trailing asterisk - just means could be widescreen if content is in widescreen - duh!
258                                        $show =~ s/\*$//;
259
260                                        # High Definition and locality
261                                        my $hd = 0;
262                                        if ($show =~ m/\(HD\)\s*? \(([SMAPB])\)/) {
263                                                $hd = 1 if ($1 eq $opt->{locality});
264                                                $show =~ s/\(HD\)\s*? \([SMAPB]\)//;
265                                        }
266
267
268                                        # High Definition
269                                        if ($show =~ m/\(HD\)/ || $show =~ m/\(JHD\)/ ) {
270                                                $hd = 1;
271                                                $show =~ s/\(HD\)//;
272                                                $show =~ s/\(JHD\)//; # JHD was probably a typo but we've included it here because they'll probably have fat fingers again
273                                        }
274
275                                        # Dolby Digital 5.1
276                                        my $dd51 = 0;
277                                        if ($show =~ m/DD 5.1/ || $show =~ m/DD5.1/) {
278                                                $dd51 = 1;
279                                                $show =~ s/\[\+ DD 5.1 sound\]//;
280                                                $show =~ s/\[\+ DD 5.1 Audio\]//;
281                                                $show =~ s/\[\+ DD 5.1 audio\]//;
282                                                $show =~ s/\[\+DD 5.1 audio\]//;
283                                                $show =~ s/\[\+DD 5.1 Audio\]//;
284                                                $show =~ s/\[\+DD 5.1 sound\]//;
285                                                $show =~ s/\[\+DD 5.1 sound\]//;
286                                                $show =~ s/\[\+DD5.1 sound\]//;
287                                                $show =~ s/\[\+DD5.1\]//;
288                                                $show =~ s/\[\+ DD 5.1\]//;
289                                                $show =~ s/\[\DD5.1\]//;
290                                                $show =~ s/\[\DD 5.1\]//;
291                                        }
292                                        $show =~ s/\s+$//;
293                                        $show =~ s/^\s+//;
294
295                                        if (defined($translation->{$show})) {
296                                                $show = $translation->{$show};
297                                                $stats{manual_translations}++;
298                                        }
299   
300                                        next if (length($show) == 0);
301
302                                        # Widescreen handling
303                                        # Now we're here, this show must be in widescreen - we don't (yet) do anything with widescreen in mythtv
304
305                                        # DD5.1
306                                        if ($dd51 == 1) {
307                                                # This show is is in DD5.1 - we don't (yet) do anything wiht DD5.1 in mythtv
308                                                ;
309                                        }
310 
311                                        # High Definition Handling
312                                        if ($hd == 1) {
313                                                #print "HD Channel $this_chan $dayname \"$show\"\n" if $opt->{debug};
314                                                my $xmlid = $channels->{$this_chan};
315                                                $hdprog->{$xmlid}->{$show}->{chan} = $this_chan;
316                                                $hdprog->{$xmlid}->{$show}->{days}->{$dayname} = 1;
317                                                $stats{parsed_hd_progs}++;
318                                        } else {
319                                                #print "SD Widescreen Channel $channels[$channelnumber]: $dayname $show \n" if $opt->{debug};
320                                                $stats{parsed_sd_widescreen_progs}++;
321                                        }
322                                }
323                        }
324                }
325        }
326}
327
328######################################################################################################
329# logic to fetch a page via http
330#  retries up to 3 times to get a page with 5 second pauses inbetween
331
332sub get_url
333{
334        my($url,$status,$dontretry) = (@_);
335        my $response;
336        my $attempts = 0;
337        my ($raw, $page, $base);
338
339        my $request = HTTP::Request->new(GET => $url);
340        $request->header('Accept-Encoding' => 'gzip');
341
342        print "$status $url\n";
343
344        my $max_retries = 1 if ($dontretry);
345
346        for (1..3) {
347                $response = $ua->request($request);
348                last if ($response->is_success || $dontretry);
349
350                $stats{http_failed_requests}++;
351                $attempts++;
352                &sleepy("attempt $attempts failed (url $url), sleeping for 10 seconds",10);
353        }
354        if (!($response->is_success)) {
355                if ($dontretry == 0) {
356                        printf "ERROR: could not open url %s in %d attempts\n",$url,$attempts;
357                }
358                return undef;
359        }
360
361        $stats{bytes_fetched} += do {use bytes; length($response->content)};
362        $stats{http_successful_requests}++;
363
364        if ($response->header('Content-Encoding') &&
365            $response->header('Content-Encoding') eq 'gzip') {
366                $stats{compressed_pages} += do {use bytes; length($response->content)};
367                $response->content(Compress::Zlib::memGunzip($response->content));
368        }
369
370        if ($response->header('Content-type') eq 'xapplication/ywe-octet-stream') {
371                $stats{transformed_pages}++;
372                $base = &transform_output(length($response->content), $response->content);
373        } else {
374                $base = $response->content;
375        }
376        return $base;
377}
378
379######################################################################################################
380
381sub print_stats
382{
383        printf "%s v%s completed", $progname, $version;
384        foreach my $key (sort keys %stats) {
385                printf ", %d %s",$stats{$key},$key;
386        }
387        printf "\n";
388}
389
390######################################################################################################
391
392sub channel_cb( $ )
393{
394        my $c = shift;
395        # printf "got channel ".Dumper($c);
396        $writer->write_channel($c);
397}
398
399######################################################################################################
400
401sub programme_cb( $ )
402{
403        my $prog=shift;
404        my $this_chan = $prog->{channel};
405        my $this_title;
406        my $hd_chan_base;
407
408        $this_title = $prog->{title}->[0]->[0]
409          if (($prog->{title}) && ($prog->{title}->[0]) && ($prog->{title}->[0]->[0]));
410        # print "got programme ".Dumper($prog);
411
412        goto NOMATCH if (!defined $this_chan);
413        goto NOMATCH if (!defined $this_title);
414        goto NOMATCH if (!defined $hdprog->{$this_chan});
415
416        # see if title matches everything we have seen on this channel
417        foreach my $t (keys %{($hdprog->{$this_chan})}) {
418                if (canonicalizeTitles_match($t,$this_title)) {
419                        # match!
420                        printf "Matched '$t' to '$this_title'\n";
421                        $stats{matched_prog}++;
422                        $hd_chan_base = $hdprog->{$this_chan}->{$t}->{chan}."HD";
423                        goto MATCH;
424                }
425        }
426
427        # see if title matches something in our override table
428        foreach my $ch (keys %{($hdoverride)}) {
429                next if ($channels->{$ch} ne $this_chan);
430                foreach my $t (@{($hdoverride->{$ch})}) {
431                        if (canonicalizeTitles_match($t,$this_title)) {
432                                # match!
433                                printf "Matched '$t' to '$this_title' (hd override)\n";
434                                $stats{override_matched_prog}++;
435                                $hd_chan_base = $ch."HD";
436                                goto MATCH;
437                        }
438                }
439        }
440
441NOMATCH:
442        $writer->write_programme($prog);
443        return;
444
445MATCH:
446        $prog->{video}->{aspect} = "16:9"; # widescreen
447        $prog->{video}->{quality} = "HDTV"; # HD
448        $writer->write_programme($prog);
449        $stats{rewrote_prog_as_hdtv_inplace}++;
450
451        # do we have a HD version of this known in opt_channels table?
452        if (defined $opt_channels->{$hd_chan_base}) {
453                # populate this programme in HD channel also
454                $prog->{channel} = $opt_channels->{$hd_chan_base};
455                $writer->write_programme($prog);
456                $stats{wrote_prog_into_hdtv_channel}++
457        }
458        return;
459}
460
461######################################################################################################
462
463sub canonicalizeTitle
464{
465        my $title=shift;
466        $title =~ s/^\s+//;
467        $title =~ s/\s+$//;
468        $title =~ s/&(#(\d+)|(.*?));/ $2 ? chr($2) : $amp{$3}||' ' /eg;
469        $title =~ s/ *\& */ and /g;
470        $title =~ s/[^ a-zA-Z0-9]//g;
471        $title =~ s/\s+/ /;
472        return(lc($title));
473}
474
475my %alternatives = ( one => "1",
476                     two => "2",
477                     to  => "2",
478                     too => "2",
479                     three => "3",
480                     four => "4",
481                     for => "4",
482                     five => "5",
483                     six => "6",
484                     seven => "7",
485                     eight => "8",
486                     nine => "9"
487                   );
488
489sub forgivingMatch
490{
491        my $word1 = shift;
492        my $word2 = shift;
493
494        # exact match
495        return 1 if $word1 eq $word2;
496        # they match according to our alternatives lookup table
497        return 1 if $alternatives{$word1} && $alternatives{$word1} eq $word2 ||
498                    $alternatives{$word2} && $alternatives{$word2} eq $word1;
499        # irreconcilable differences
500        return 0 if abs(length($word1) - length($word2)) > 1 ||
501                    length($word1) < 3;
502
503        my @list1 = split(//,$word1);
504        my @list2 = split(//,$word2);
505        my $i = 0;
506        my $j = 0;
507
508        # find first difference
509        while ($i < @list1 && $j < @list2 && $list1[$i] eq $list2[$j]) {
510                ++$i; ++$j;
511        }
512        if ($i+1 < @list1 && $j+1 < @list2) {
513                # at least 2 chars to go in both words
514                if ($list1[$i+1] eq $list2[$j] && $list1[$i] eq $list2[$j+1]) {
515                        # characters transposed
516                        $i += 2;
517                        $j += 2;
518                } elsif ($list1[$i+1] eq $list2[$j]) {
519                        # extra character inserted into @list1
520                        $i += 2;
521                        ++$j;
522                } elsif ($list1[$i] eq $list2[$j+1]) {
523                        # extra character inserted into @list2
524                        ++$i;
525                        $j += 2;
526                } else {
527                        # single character difference
528                        ++$i;
529                        ++$j;
530                }
531                # we forgave one difference; now do rest of strings match exactly?
532                while ($i < @list1 && $j < @list2 && $list1[$i] eq $list2[$j]) {
533                        ++$i; ++$j;
534                }
535                return($i == @list1 && $j == @list2);
536        } elsif ($i == @list1 || $j == @list2) {
537                # only difference is one word has one extra letter, or last char
538                # of each word differ.  That's still only one one-char difference
539                return(1);
540        }
541}
542
543sub canonicalizeTitles_match
544{
545        my $word1=canonicalizeTitle(shift);
546        my $word2 =canonicalizeTitle(shift);
547        my @longer;
548        my @shorter;
549
550        if (length($word1) > length($word2)) {
551                @longer  = split(/\s+/, $word1);
552                @shorter = split(/\s+/, $word2);
553        } else {
554                @shorter = split(/\s+/, $word1);
555                @longer  = split(/\s+/, $word2);
556        }
557
558        WORD: for my $word (@shorter) {
559                for(my $i=0; $i < @longer; ++$i) {
560                        if (forgivingMatch($longer[$i], $word)) {
561                                splice(@longer,$i,1);
562                                next WORD;
563                        } elsif ($i+1 < @longer &&
564                                 $word eq "$longer[$i]$longer[$i+1]") {
565                                splice(@longer,$i,2);
566                                next WORD;
567                        }
568                }
569                return(0);
570        }
571        return(1);
572}
573
574##############################################################################
Note: See TracBrowser for help on using the browser.