source: trunk/grabbers/ebroadcast

Last change on this file was 775, checked in by max, 11 years ago

Tinkering with ebroadcast

  • Property svn:executable set to *
File size: 7.2 KB
Line 
1#!/usr/bin/perl
2#
3# ebroadcast
4#
5# Proof-of-concept grabber. Not ready for prime time.
6#
7# The main thing that makes this interesting is that it can supply PayTV.
8
9my $version = '0.1';
10
11# Issues:
12# * Doesn't grab details. Could be made to, but details are really sparse
13#   so it hardly seems worth it.
14# * Server seems incredibly flaky. Often craps out, and even when it's
15#   up it's very slow.
16# * Show titles are often ALL IN CAPS
17# * Idiosyncratic channel names
18# * Rural regions are combined into a single guide with notes like
19#   "(excl. Albury)" beside individual shows
20
21use strict;
22use Getopt::Long;
23use POSIX;
24use Date::Manip;
25use HTML::TreeBuilder;
26use XMLTV;
27use Data::Dumper;
28use Shepherd::Common;
29use Shepherd::JS;
30
31$| = 1;
32
33my $progname =      'ebroadcast';
34my $DATASOURCE =    'http://www.ebroadcast.com.au';
35my $GUIDE =         "$DATASOURCE/cgi-bin/TV/grid?";
36my $JS =            "$DATASOURCE/tv/crypt.js";
37my $JS_LOCAL =      "crypt.js";
38my $lang =          "en";
39
40my $opt = { };
41my $channels = { };
42my $opt_channels = { };
43my $debug = 0;
44my $tv_guide = { };
45my %stats;
46
47print "$progname $version\n";
48&set_defaults;
49&get_command_line_options;
50
51exit 0 if ($opt->{version});
52
53&read_channels_file;
54&read_gaps_file;
55
56Shepherd:Common::set_defaults(debug => $debug * 4, stats => \%stats, delay => "1-5", retry_delay => "8-12");
57
58&setup_js_file;
59
60&get_guide_data;
61
62&adjust_times;
63
64print Dumper($tv_guide);
65&write_xml;
66
67print "Done.\n";
68
69exit 0;
70
71
72###############
73# Subs
74#
75
76sub get_guide_data
77{
78    print "Grabbing data for days $opt->{offset}-" .
79          ($opt->{days} - 1) . " into $opt->{output}.\n";
80
81    my @lt = localtime;
82
83    # Establish epoch time for midnight on day 0
84    my $epoch = time - ($lt[0] + ($lt[1] * 60) + $lt[2] * 3600); 
85
86    for my $day ($opt->{offset} .. $opt->{days} - 1)
87    {
88        my $date = POSIX::strftime("%A_%d_%B", localtime($epoch + ($day * 86400)));
89
90        print "Day $day: $date\n" if ($debug);
91
92        my @sessions = ( 'LateNight', 'Morning', 'Afternoon', 'Night');
93        my $i = 0;
94        while ($i++ < @sessions)
95        {
96            # Don't get any times earlier than now
97            next unless ($day or time < $epoch + (($i+1) * 6 * 3600));
98
99            my $url = sprintf("%sdate=%s&TVperiod=%s&state=%s&fta=%d&fox=%d&opt=%d",
100                        $GUIDE,
101                        $date,
102                        $sessions[$i],
103                        'Melbourne',    # FIXME: allow other regions
104                        1,
105                        0,              # FIXME: allow paytv details
106                        0);             # FIXME? Need Optus as well?
107            my $data = Shepherd::Common::get_url($url);
108            next unless ($data);
109
110            $data = Shepherd::JS::read($data, $debug);
111
112            parse_guide($data, $epoch+($day*86400));
113        }
114    }
115}
116
117sub parse_guide
118{
119    my ($data, $midnight) = @_;
120
121    my $tree = HTML::TreeBuilder->new_from_content($data);
122    foreach my $tag ($tree->look_down('_tag' => 'th', 'background' => '/pic/2005/2005_grid_blur.gif'))
123    {
124        my $channame = find_channel($tag->as_text());
125        unless ($channels->{$channame})
126        {
127            print "Unsubscribed channel: \"$channame\". Skipping.\n" if ($debug);
128            next;
129        }
130        print "Channel \"$channame\"\n" if ($debug);
131
132        while ($tag = $tag->right)
133        {
134            my @parts = split /<br>/, $tag->as_HTML;
135
136            foreach my $part (@parts)
137            {
138                $part =~ s/<.*?>//g;
139                if ($part =~ /(\d+).(\d+) ([ap]m): ([^|]*)/g)
140                {
141                    my $show;
142
143                    my $title = $4;
144                    my $time = $1;
145                    $time = 0 if ($time == 12);
146                    $time += 12 if ($3 eq 'pm');
147                    $time = ($time * 3600) + ($2 * 60); # seconds past midnight
148                    $time += $midnight;
149                    $show->{start} = POSIX::strftime("%Y%m%d%H%M", localtime($time));
150
151                    if ($title =~ s/( \(.*?\))//g)
152                    {
153                        # Ignore shows with no easily available start time
154                        next if ($1 eq ' (Cont.)');
155
156                        # FIXME: regional stuff "(excl. Albury)"
157                        print "Removed $1 from $title\n";
158                    }
159                    $show->{title} = [ [ $title, $lang ] ];
160                    $show->{channel} = $channame;
161
162                    print "- $title\n";
163                    $tv_guide->{$channame}->{$time} = $show;
164                }
165            }
166        }
167    }
168}
169
170sub adjust_times
171{
172    my $stoptimes = { };
173    foreach my $channel (keys %$tv_guide)
174    {
175        foreach my $starttime (reverse sort keys %{$tv_guide->{$channel}})
176        {
177            my $prog = $tv_guide->{$channel}->{$starttime};
178
179            if (defined $stoptimes->{$channel})
180            {
181                $tv_guide->{$channel}->{$starttime}->{stop} = POSIX::strftime("%Y%m%d%H%M", localtime($stoptimes->{$channel}));
182            }
183            else
184            {
185                # Don't know stop time
186                delete $tv_guide->{$channel}->{$starttime};
187            }
188            $stoptimes->{$channel} = $starttime;
189        }
190    }
191}
192
193sub write_xml
194{
195    my %writer_args = ( encoding => 'ISO-8859-1' );
196
197    print "Writing XML.\n";
198
199    my $fh = new IO::File(">" . $opt->{output})
200        or die "Can't open " . $opt->{output} . ": $!";
201    $writer_args{OUTPUT} = $fh;
202
203    my $writer = new XMLTV::Writer(%writer_args);
204
205    $writer->start
206        ( { 'source-info-url'    => $DATASOURCE,
207            'source-info-name'   => "ebroadcast",
208            'generator-info-name' => "$progname $version"} );
209
210    foreach my $channel (sort keys %$tv_guide)
211    {
212        my $chanid = $channels->{$channel};
213        $writer->write_channel( { 'display-name' => [[$channel, $lang]],
214                'id' => $chanid } );
215    }
216
217    foreach my $channel (sort keys %$tv_guide)
218    {
219        foreach my $starttime (keys %{$tv_guide->{$channel}})
220        {
221            print "- " . $tv_guide->{$channel}->{$starttime}->{'title'}[0][0] . "\n" if ($debug);
222            $writer->write_programme($tv_guide->{$channel}->{$starttime});
223        }
224    }
225
226    $writer->end();
227}
228
229sub find_channel
230{
231    my $str = shift;
232
233    my $channames = {
234        'SBSNEWS' => 'SBS News',
235        '31' => 'Channel 31',
236        'Ten' => 'TEN'
237    };
238
239    $str =~ s/ \(.*?\)//g; # Remove anything in brackets
240    $str =~ s/[\W _-]//g;  # Remove weird chars
241    return $channames->{$str} if ($channames->{$str});
242    return $str;
243}       
244
245###############
246# Utilities
247#
248
249sub setup_js_file
250{
251    my $data;
252    if (-r $JS_LOCAL)
253    {
254        if (open (F, $JS_LOCAL))
255        {
256            print "Reading local file $JS_LOCAL.\n" if ($debug);
257            $data = '';
258            while (<F>)
259            {
260                $data .= $_;
261            }
262            close F;
263        }
264    }
265    unless ($data)
266    {
267        $data = Shepherd::Common::get_url(url => $JS, retries => 9);
268        if ($data and open (F, ">$JS_LOCAL"))
269        {
270            print "Storing $JS_LOCAL locally.\n";
271            print F $data;
272            close F;
273        }
274    }
275    unless ($data)
276    {
277        print "*** WARNING: Failure to fetch $JS will probably be fatal. ***\n";
278    }
279    Shepherd::JS::eval_js($data, $debug);
280}
281
282sub set_defaults
283{
284    $opt->{days} = 7;
285    $opt->{offset} = 0;
286    $opt->{region} = 94;
287    $opt->{output} = getcwd() . "/output.xmltv";
288}
289
290sub get_command_line_options
291{
292    Getopt::Long::Configure(qw/pass_through/);
293    GetOptions($opt, qw(
294                        help
295                        debug
296                        output=s
297                        days=i
298                        offset=i
299                        region=i
300                        channels_file=s
301                        version
302                    )); 
303    $debug = $opt->{debug};
304}
305
306sub read_channels_file 
307{
308    read_config_file('channels', 'channels_file');
309}
310
311sub read_gaps_file
312{
313    read_config_file('gaps', 'gaps_file');
314}
315
316sub read_config_file
317{
318    my ($name, $arg) = @_;
319
320    return unless ($opt->{$arg});
321    print "Reading $name file: $opt->{$arg}\n";
322    if (-r $opt->{$arg})
323    {
324        local (@ARGV, $/) = ($opt->{$arg});
325        no warnings 'all';
326        eval <>;
327        die "Can't parse $name file: $@" if ($@);
328    }
329    else
330    {
331        print "Unable to read $name file.\n";
332    }
333}
334
Note: See TracBrowser for help on using the repository browser.