root/trunk/grabbers/oztivo @ 1033

Revision 1033, 22.5 kB (checked in by paul, 5 years ago)

add new regions from region 79 Regional QLD

Line 
1#!/usr/bin/perl -w
2# vi: ts=4 sw=4
3#
4# OzTivo Programme Guide Grabber for Shepherd
5# Chris Williams <shepherd@psychogeeks.com>
6# Copyright Chris Williams, 2008.
7#
8use strict;
9
10use Getopt::Long;
11use Shepherd::Common;
12use XML::LibXML;
13use URI::Escape;
14use POSIX;
15use Time::Local;
16
17# ensure 'cache' directory exists (or is created) before the 'use HTTP::Cache'
18# below or it'll fail
19BEGIN {
20        -d "cache" or mkdir "cache" or die "Cannot create directory cache: $!";
21}
22
23# Setup caching so we are good Netizens
24# This works transparently under the LWP::* modules
25#
26use HTTP::Cache::Transparent (
27        BasePath  => "cache",
28        MaxAge    => 8*24, # hours
29        Verbose   => 0,
30        NoUpdate  => 15*60,
31        ApproveContent => sub { return $_[0]->is_success },
32);
33
34
35################################################################################
36# Global variables
37#
38my $progname = 'oztivo';
39my $nicename = 'OzTivo';
40my $version = '2.12';
41my $config_file = "$progname.pw";
42my $output_file = undef;
43my $channels_file;
44my $ver;
45my $ready_check;
46my $configure;
47my $region = undef;
48my $days = 7;
49
50# Channel to XMLTvID map from channels file
51#
52my $channels, my $opt_channels;
53
54# User name and password from configuration file
55#
56my $user;
57my $pw;
58
59
60################################################################################
61# Setup a mapping file to go from a Shepherd channel name to an OzTivo name
62# Taking into consideration the region.
63#
64# The order of search for a mapping is;
65#       Region specific mapping (under numeric region code keys)
66#       National mapping (under hash key 'National')
67#       Pay TV mapping (under hash key 'PAY')
68# First mapping found wins.
69#
70my %s_to_o_map = (
71        'National' => { # National channel mappings, all timeshifted
72                'ABC2' => 'ABC2',
73                'SBS News' => 'SBS-NEWS',
74                '7HD' => 'SevenHD',
75                'Nine HD' => 'NineHD',
76                'TEN HD' => 'TenHD',
77        },
78        '67' => {       # NSW: Griffith
79                'ABC1' => 'ABC-NSW',
80                'Prime' => 'PrimeS',
81                'SBS' => 'SBS-NSW',
82                'TEN' => 'Ten-NSW',
83                'WIN' => 'WIN-NSW',
84        },
85        '90' => {       # VIC: Eastern Victoria
86                'ABC1' => 'ABC-Vic',
87                'Prime (Albury)' => 'Prime-Vic',
88                'Prime (Regional)' => 'Prime-Vic',
89                'SBS' => 'SBS-Vic',
90                'TEN' => 'SC10-Vic',
91                'WIN' => 'WIN-Vic',
92        },
93        '63' => {       # NSW: Broken Hill
94                'ABC1' => 'ABC-NSW',
95                'Central GTS/BKN' => 'GTS-BKN',
96                'SBS' => 'SBS-NSW',
97                'Sthn Cross TEN' => 'SC10-NSW',
98        },
99        '71' => {       # NSW: Southern NSW
100                'ABC1' => 'ABC-NSW',
101                'Prime (Canberra/South Coast)' => 'Prime-Can',
102                'Prime (Orange)' => 'PrimeS',
103                'Prime (Wagga Wagga)' => 'PrimeS',
104                'Prime (Wollongong)' => 'PrimeS',
105                'SBS' => 'SBS-NSW',
106                'TEN (Mildura Digital)' => 'Ten-NSW',
107                'TEN (NSW: Southern NSW)' => 'Ten-NSW',
108                'WIN' => 'WIN-NSW',
109        },
110        '102' => {      # WA: Regional
111                'ABC1' => 'ABC-WA',
112                'Access 31' => '31-Per',
113                'Golden West' => 'GWN-WA',
114                'Nine' => 'Nine-WA',
115                'SBS' => 'SBS-WA',
116                'Seven' => 'Seven-WA',
117                'TEN' => 'Ten-WA',
118                'WIN' => 'WIN-WA',
119        },
120        '78' => {       # QLD: Gold Coast
121                'ABC1' => 'ABC-Qld',
122                'NBN' => 'NWN-GC',
123                'Nine' => 'Nine-Qld',
124                'Prime' => 'Prime-GC',
125                'SBS' => 'SBS-Qld',
126                'Seven' => 'Seven-Bris',
127                'Sthn Cross TEN' => 'SC10-GC',
128                'TEN' => 'Ten-Qld',
129        },
130        '255' => {      # QLD: Sunshine Coast
131                'ABC1' => 'ABC-Qld',
132                'SBS' => 'SBS-Qld',
133                'Seven' => 'Seven-Qld',
134                'TEN' => 'Ten-Qld',
135                'WIN' => 'Win-Qld',
136        },
137        '256' => {      # QLD: Toowoomba
138                'ABC1' => 'ABC-Qld',
139                'SBS' => 'SBS-Qld',
140                'Seven' => 'Seven-Qld',
141                'TEN' => 'Ten-Qld',
142                'WIN' => 'Win-Qld',
143        },
144        '258' => {      # QLD: Wide Bay
145                'ABC1' => 'ABC-Qld',
146                'SBS' => 'SBS-Qld',
147                'Seven' => 'Seven-Qld',
148                'TEN' => 'Ten-Qld',
149                'WIN' => 'Win-Qld',
150        },
151        '254' => {      # QLD: Rockhampton
152                'ABC1' => 'ABC-Qld',
153                'SBS' => 'SBS-Qld',
154                'Seven' => 'Seven-Qld',
155                'TEN' => 'Ten-Qld',
156                'WIN' => 'Win-Qld',
157        },
158        '253' => {      # QLD: Mackay
159                'ABC1' => 'ABC-Qld',
160                'SBS' => 'SBS-Qld',
161                'Seven' => 'Seven-Qld',
162                'TEN' => 'Ten-Qld',
163                'WIN' => 'Win-Qld',
164        },
165        '257' => {      # QLD: Townsville
166                'ABC1' => 'ABC-Qld',
167                'SBS' => 'SBS-Qld',
168                'Seven' => 'Seven-Qld',
169                'TEN' => 'Ten-Qld',
170                'WIN' => 'Win-Qld',
171        },
172        '79' => {       # QLD: Cairns
173                'ABC1' => 'ABC-Qld',
174                'SBS' => 'SBS-Qld',
175                'Seven' => 'Seven-Qld',
176                'TEN' => 'Ten-Qld',
177                'WIN' => 'Win-Qld',
178        },
179        '107' => {      # SA: Remote & Central
180                'ABC1' => 'ABC-SA',
181                'Imparja' => 'IMP',
182                'SBS' => 'SBS-SA',
183                'Seven' => 'Seven-SA',
184        },
185        '88' => {       # Tasmania
186                'ABC1' => 'ABC-Tas',
187                'SBS' => 'SBS-Tas',
188                'Southern Cross' => 'SC-Tas',
189                'TDT' => 'TDT-Tas',
190                'WIN' => 'Win-Tas',
191        },
192        '93' => {       # VIC: Geelong
193                'ABC1' => 'ABC-Vic',
194                'Channel 31' => '31-Mel',
195                'Nine' => 'Nine-Mel',
196                'SBS' => 'SBS-Vic',
197                'Seven' => 'Seven-Mel',
198                'TEN' => 'Ten-Vic',
199        },
200        '106' => {      # NSW: Remote and Central
201                'ABC1' => 'ABC-NSW',
202                'Imparja' => 'IMP',
203                'Prime' => 'PrimeS',
204                'SBS' => 'SBS-NSW',
205                'Seven' => 'Seven-Syd',
206        },
207        '126' => {      # ACT
208                'ABC1' => 'ABC-Can',
209                'Prime' => 'Prime-Can',
210                'SBS' => 'SBS-Can',
211                'TEN' => 'Ten-Can',
212                'WIN' => 'Win-Can',
213        },
214        '82' => {       # SA: Renmark
215                'ABC1' => 'ABC-SA',
216                'SBS' => 'SBS-SA',
217                'WIN TEN' => 'TEN-SA',
218                'WIN' => 'Win-Vic',
219        },
220        '74' => {       # NT: Darwin
221                'ABC1' => 'ABC-NT',
222                'Nine' => undef,
223                'SBS' => 'SBS-NT',
224                'Southern Cross' => 'SC10-Qld',
225                #? 'Seven' => 'Seven-Dar',
226        },
227        '85' => {       # SA: South East SA
228                'ABC1' => 'ABC-SA',
229                'SBS' => 'SBS-SA',
230                'WIN TEN' => 'TEN-SA',
231                'WIN' => 'Win-Vic',
232        },
233        '83' => {       # SA: Riverland
234                'ABC1' => 'ABC-SA',
235                'SBS' => 'SBS-SA',
236                'WIN TEN' => 'TEN-SA',
237                'WIN' => 'Win-Vic',
238        },
239        '95' => {       # VIC: Mildura/Sunraysia
240                'ABC1' => 'ABC-Vic',
241                'Prime' => 'Prime-Vic',
242                'SBS' => 'SBS-Vic',
243                'TEN' => 'Ten-Vic',
244                'WIN' => 'WIN-Vic',
245        },
246        '75' => {       # QLD: Brisbane
247                'ABC1' => 'ABC-Qld',
248                'Briz 31' => 'BRIZ',
249                'Nine' => 'Nine-Qld',
250                'SBS' => 'SBS-Qld',
251                'Seven' => 'Seven-Bris',
252                'TEN' => 'Ten-Qld',
253        },
254        '94' => {       # VIC: Melbourne
255                'ABC1' => 'ABC-Vic',
256                'Channel 31' => '31-Mel',
257                'Nine' => 'Nine-Mel',
258                'SBS' => 'SBS-Vic',
259                'Seven' => 'Seven-Mel',
260                'TEN' => 'Ten-Vic',
261        },
262        '108' => {      # NT: Remote & Central
263                'ABC1' => 'ABC-NT',
264                'Imparja' => 'IMP',
265                'SBS' => 'SBS-NT',
266                'Seven' => 'Seven-Dar',
267        },
268        '114' => {      # QLD: Remote & Central
269                'ABC1' => 'ABC-Qld',
270                'Imparja' => 'IMP',
271                'SBS' => 'SBS-Qld',
272                'Seven' => 'Seven-Qld',
273        },
274        '184' => {      # NSW: Newcastle
275                'ABC1' => 'ABC-NSW',
276                'NBN' => 'NBN',
277                'Prime' => 'PrimeN',
278                'SBS' => 'SBS-NSW',
279                'Sthn Cross TEN' => 'SC10-NSW',
280        },
281        '69' => {       # NSW: Northern NSW
282                'ABC1' => 'ABC-NSW',
283                'NBN' => 'NBN',
284                'Prime (NSW: Northern NSW)' => 'PrimeN',
285                'Prime NSW: Northern NSW' => 'PrimeN',
286                'SBS' => 'SBS-NSW',
287                'Sthn Cross TEN' => 'SC10-NSW',
288        },
289        '81' => {       # SA: Adelaide
290                'ABC1' => 'ABC-SA',
291                'C31 Adelaide' => '31-Adl',
292                'Nine' => 'Nine-SA',
293                'SBS' => 'SBS-SA',
294                'Seven' => 'Seven-SA',
295                'TEN' => 'Ten-SA',
296        },
297        '98' => {       # VIC: Western Victoria
298                'ABC1' => 'ABC-Vic',
299                'Imparja' => 'IMP',
300                'Prime' => 'Prime-Vic',
301                'SBS' => 'SBS-Vic',
302                'Seven' => 'Seven-Mel',
303                'TEN' => 'Ten-Vic',
304                'WIN' => 'WIN-Vic',
305        },
306        '66' => {       # NSW: Central Coast
307                'ABC1' => 'ABC-NSW',
308                'NBN' => 'NBN',
309                'Nine' => 'Nine-Syd',
310                'Prime' => 'PrimeN',
311                'SBS' => 'SBS-NSW',
312                'Seven' => 'Seven-Syd',
313                'Sthn Cross TEN' => 'SC10-NSW',
314                'TEN' => 'Ten-NSW',
315        },
316        '73' => {       # NSW: Sydney
317                'ABC1' => 'ABC-NSW',
318                'Channel NSW' => undef,
319                'Nine' => 'Nine-Syd',
320                'SBS' => 'SBS-NSW',
321                'Seven' => 'Seven-Syd',
322                'TEN' => 'Ten-NSW',
323                'TVS' => '31-Syd',
324        },
325        '101' => {      # WA: Perth
326                'ABC1' => 'ABC-WA',
327                'Access 31' => '31-Per',
328                'Nine' => 'Nine-WA',
329                'SBS' => 'SBS-WA',
330                'Seven' => 'Seven-WA',
331                'TEN' => 'Ten-WA',
332        },
333        '86' => {       # SA: Spencer Gulf
334                'ABC1' => 'ABC-SA',
335                'Central GTS/BKN' => 'GTS-BKN',
336                'Nine' => 'Nine-SA',
337                'SBS' => 'SBS-SA',
338                'Sthn Cross TEN' => 'SC10-NSW',
339        },
340        'PAY' => {      # Pay channels
341                'ACC' => 'ACC',
342                # '????' => 'ADULT',
343                # '????' => 'ADULT1',
344                # '????' => 'ADULT2',
345                # '????' => 'AdultsOnly960',
346                'AIR' => 'AIRACT',
347                # '????' => 'ALJA',
348                'AnimalPlanet' => 'ANIMAL',
349                # '????' => 'AntennaGreek',
350                'AntennaPacific' => 'ANT',
351                # '????' => 'ARCADE',
352                'ARENATV2' => 'ARNA+2',
353                'ARENATV' => 'ARNA',
354                # '????' => 'ART',
355                'Aurora' => 'AUR',
356                # '????' => 'AV',
357                'BBCWorld' => 'BBC',
358                # '????' => 'BETHTV',
359                'Bio' => 'BIOG',
360                'BloombergTelevision' => 'BLM',
361                # '????' => 'BNTV',
362                'Boomerang' => 'BOOM',
363                # '????' => 'CANALSUR',
364                'CartoonNetwork' => 'CART',
365                'ChannelV2' => 'V2',
366                'ChannelV' => 'V',
367                # '????' => 'Cinemax',
368                # '????' => 'CLUBV',
369                # '????' => 'CMTE',
370                # '????' => 'CMTER1',
371                # '????' => 'CMTER2',
372                # '????' => 'CMTER3',
373                # '????' => 'CMTER4',
374                'CNBC' => 'CNBC',
375                'CNN' => 'CNNI',
376                # '????' => 'CNNfn',
377                'CountryMusicChannel' => 'CMC',
378                'CrimeandInvestigationNetwork' => 'CRIME',
379                # '????' => 'D4NEWS',
380                # '????' => 'DAAI',
381                # '????' => 'DIG',
382                # '????' => 'Digital4',
383                # '????' => 'DIG-jazz',
384                'DiscoveryChannel' => 'DISC',
385                'DiscoveryHealth' => 'HEALTH',
386                'DiscoveryRealTime' => 'DISCRT',
387                'DiscoveryScience' => 'SCIENCE',
388                'DiscoveryTravel' => 'TRAVEL',
389                'DisneyChannel' => 'DISN',
390                # '????' => 'DWTV',
391                'E!Entertainment' => 'E!',
392                # '????' => 'ENEWS-GE',
393                # '????' => 'ENEWS-IT',
394                # '????' => 'ENEWS-SP',
395                # '????' => 'ERITV',
396                # '????' => 'ERTGreek',
397                'ESPN' => 'ESPN',
398                # '????' => 'EURONEWS',
399                'Eurosport' => 'EUROSPORT2',
400                'Eurosportnews' => 'EUROSPORT',
401                # '????' => 'EWTN',
402                'EXPO' => 'EXPO',
403                'FashionTV' => 'FASH',
404                # '????' => 'FB01',
405                # '????' => 'FB02',
406                # '????' => 'FB03',
407                # '????' => 'FB04',
408                # '????' => 'FB05',
409                # '????' => 'FB06',
410                # '????' => 'FB07',
411                # '????' => 'FB08',
412                # '????' => 'FB09',
413                # '????' => 'FB10',
414                # '????' => 'FB11',
415                # '????' => 'FB12',
416                # '????' => 'FB13',
417                # '????' => 'FB14',
418                # '????' => 'FB15',
419                # '????' => 'FB16',
420                # '????' => 'FB17',
421                # '????' => 'FB18',
422                # '????' => 'FB19',
423                # '????' => 'FB20',
424                # '????' => 'FB21',
425                # '????' => 'FB22',
426                # '????' => 'FB23',
427                # '????' => 'FB24',
428                # '????' => 'FB25',
429                # '????' => 'FB26',
430                # '????' => 'FB27',
431                # '????' => 'FFC',
432                # '????' => 'FFC-NSW',
433                # '????' => 'FFC-Qld',
434                # '????' => 'FFC-SA',
435                # '????' => 'FFC-Vic',
436                # '????' => 'FFC-WA',
437                'FOX82' => 'FOX8+2',
438                'FOX8' => 'FOX8',
439                'FOXClassics2' => 'CLAS+2',
440                'FOXClassics' => 'CLAS',
441                'FOXNews' => 'FOXN',
442                'FOXSPORTS1' => 'FS1',
443                'FOXSPORTS2' => 'FS2',
444                'FOXSPORTS3' => 'FS3',
445                'FOXSPORTSNews' => 'FSN',
446                # '????' => 'FOXTELBoxOffice',
447                # '????' => 'FOXTELGamesworld',
448                'FUELTV' => 'FUEL',
449                # '????' => 'GUIDE',
450                # '????' => 'GWORLD',
451                'Hallmark' => 'HALL',
452                # '????' => 'HBOMovie',
453                # '????' => 'HIST+2',
454                # '????' => 'H&L',
455                # '????' => 'HOUSE',
456                'HOWTOChannel' => 'HOWTO',
457                # '????' => 'ISHTAR',
458                # '????' => 'JJJ',
459                # '????' => 'LBC',
460                # '????' => 'LEONARDO',
461                'LifestyleChannel2' => 'LIFE+2',
462                'LifeStyleFOOD' => 'FOOD',
463                # '????' => 'LNTV',
464                # '????' => 'MACQD',
465                # '????' => 'MAIN',
466                # '????' => 'MainEvent',
467                'MAX' => 'max',
468                # '????' => 'MCMTop',
469                # '????' => 'MINDGAMES',
470                'MOVIEEXTRA' => 'MOVX',
471                'MOVIEGREATS' => 'MOVG',
472                'MOVIEONE' => 'MOV1',
473                'MOVIETWO' => 'MOV1+2',
474                'MTV' => 'MTV',
475                'NatGeoAdventure' => 'ADV1',
476                'NationalGeographic' => 'NGEO',
477                'Nickelodeon' => 'NICK',
478                'NickJr' => 'NICKJR',
479                'NITV' => 'NITV',
480                # '????' => 'NoNag',
481                # '????' => 'ODY',
482                # '????' => 'OUT-TV',
483                'Ovation' => 'OVAT',
484                'PlayhouseDisney' => 'PHDISN',
485                # '????' => 'PREVIEW',
486                'RAIInternational' => 'RAI',
487                # '????' => 'RMTV',
488                # '????' => 'Sci-Fi',
489                # '????' => 'SC-Tas',
490                # '????' => 'SENATE',
491                # '????' => 'ServiceNSW',
492                'showcase' => 'SHWC',
493                'SHOWTIME2' => 'SHW2',
494                'SHOWTIMEGreats' => 'SHWGRTS',
495                'SHOWTIME' => 'SHOW',
496                'SkyNewsAustralia' => 'SKYN',
497                'SkyNewsBusiness' => 'SKYB',
498                'SkyRacing' => 'SKYR',
499                # '????' => 'SPORTSEL',
500                # '????' => 'StarMovies',
501                # '????' => 'StarWorld',
502                # '????' => 'STC',
503                # '????' => 'STV1',
504                # '????' => 'SYRIATV',
505                # '????' => 'TABNSW',
506                'TCM' => 'TCM',
507                # '????' => 'Test',
508                'THECOMEDYCHANNEL2' => 'CMDY+2',
509                'THECOMEDYCHANNEL' => 'CMDY',
510                'TheHistoryChannel' => 'HIST',
511                'TheLifeStyleChannel' => 'LIFE',
512                'TheWeatherChannel' => 'TWC',
513                # '????' => 'TMF',
514                'TV12' => 'TV1+2',
515                'TV1' => 'TV1',
516                # '????' => 'TV5',
517                'TVChileSpanish' => 'TVCHILE',
518                'TVE' => 'TVE',
519                'TVN' => 'TVN',
520                'UKTV2' => 'UKTV+2',
521                'UKTV' => 'UKTV',
522                'VH1' => 'VH1',
523                # '????' => 'VTV',
524                'W2' => 'W+2',
525                # '????' => 'WEIN',
526                # '????' => 'WINE',
527                'WORLDMOVIES' => 'WMOV',
528                'W' => 'W',
529                # '????' => 'WZONE',
530        }
531);
532
533
534################################################################################
535# Routine for forcing the timezone of a listing to the local time zone.
536# This has the effect of mimicing the time-shifting of national programmes
537# such as ABC2 for which there is a single programme in, say, Sydney time, but
538# where the programmes are broadcast at the same wall clock time everywhere in
539# Australia. 
540# Care is taken to use the local time zone at the time of the programme.
541#
542# Also defines a list of OzTivo channels names this kludge should be applied to.
543#
544sub force_local_timezone($) {
545        my $oztivoTime = shift;
546
547        # Split the given time into components ignoring the specified TZ
548        # Our input looks like "20080324183000 +1100"
549        #
550        my ($year, $mon, $mday, $hr, $min, $sec) = $oztivoTime =~ m/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
551
552        # Treat the time as being local and calculate the UNIX time
553        # This will account for DST in our time zone.
554        #
555        my $unixTime = timelocal($sec, $min, $hr, $mday, $mon-1, $year); # expects months 0..11
556
557        # Return a correctly formatted string
558        #
559        return strftime('%Y%m%d%H%M%S %z', localtime($unixTime));
560}
561
562my %force_timezone_set = map { $_ => 1 } ( values %{$s_to_o_map{'National'}} );
563
564
565################################################################################
566# Routines for configuring this grabber
567#
568sub please_configure_me
569{
570        print <<__EOM;
571If you wish to use $nicename, please run
572        tv_grab_au --configure $progname
573Configuration of $progname is required
574__EOM
575}
576
577sub configure
578{
579        print <<__EOM;
580Configuring...
581Before you can use the $nicename grabber, you must create a
582OzTivo account here:
583  http://minnie.tuhs.org/twiki/bin/view/TWiki/TWikiRegistration
584... and select some channels here:
585  http://minnie.tuhs.org/tivo-bin/tvguide.pl
586When you're done, enter your username and password here, to
587enable this grabber to retrieve your guide data automatically:
588Username?
589__EOM
590
591        my $username = <>;
592        chomp $username;
593        unless ($username) {
594                print "No username supplied. Exiting configuration.\n";
595                exit 3;
596        }
597        print "Password?\n";
598        my $pw = <>;
599        chomp $pw;
600        unless ($pw) {
601                print "No password supplied. Exiting configuration.\n";
602                exit 3;
603        }
604        print "Creating config file $config_file...\n";
605        open (CONF, ">$config_file")
606                or die "Unable to create $config_file: $!";
607        print CONF "$username:$pw";
608        close CONF;
609
610        print "Done.\n";
611        exit 0;
612}
613
614sub load_config() {
615        unless (-r $config_file) {
616                print "Can't find $config_file!\n";
617                please_configure_me();
618                exit 1;
619        }
620
621        print "Reading configuration file $config_file.\n";
622
623        unless (open(CONF, $config_file)) {
624                print "Unable to read config file $config_file: $!\n";
625                please_configure_me();
626                exit 1;
627        }
628        my $line = <CONF>;
629        close CONF;
630
631        unless ($line =~ /^(.*):(.*)$/) {
632                print "Unable to parse config file!\n" .
633                          "It should be in the format: username:password\n";
634                please_configure_me();
635                exit 1;
636        }
637        ($user, $pw) = ($1, $2);
638
639        unless ($user and $pw) {
640                print "Failed to extract a sensible username and password from config file.\n";
641                please_configure_me();
642                exit 1;
643        }
644}
645
646################################################################################
647# Local extension of the shared get_url() to cater for password authentication
648# failure
649#
650# Returns the data as a string, or undef if data couild not be retrieved
651#
652sub get_url($) {
653        my ($data, $success, $status, $bytes);
654
655        my $url = shift;
656
657        # Don't rely on Shepherd::Common::get_url()'s retry, because
658        # if we get a 401 (wrong password) there's no point in retrying.
659        my $max_tries = 3;
660        foreach my $tries (1 .. $max_tries) {
661                ($data, $success, $status, $bytes) = Shepherd::Common::get_url(
662                                url => $url,
663                                referer => "Shepherd ".$nicename." grabber, version ".$version,
664                                delay => 1,
665                                retries => 0);
666
667                last if ($success);
668
669                print "Download failed: $status\n";
670                if ($status =~ /401/)
671                {
672                        print <<__EOM;
673Your OzTivo username and/or password may be incorrect
674The username and password you supplied when configuring the oztivo grabber
675must match your registration details on the OzTivo.com site.
676If this error persists, try reconfiguring:
677        tv_grab_au --configure $progname
678__EOM
679                        last;   # bail out of the retry loop
680                }
681
682                my $sleep = 47 + int(rand(120));
683                print "Sleeping for $sleep seconds before retrying...\n";
684                sleep($sleep);
685        }
686
687        # hack: if we don't get back something that looks like xml then try to
688        # ungzip it
689        $data = Compress::Zlib::memGunzip($data) if (substr($data,0,1) ne '<');
690
691        return $data;
692}
693
694
695
696################################################################################
697#
698$| = 1;
699
700GetOptions(
701        'config=s'                      => \$config_file,
702        'channels_file=s'       => \$channels_file,
703        'output=s'                      => \$output_file,
704        'version'                       => \$ver,
705        'ready'                         => \$ready_check,
706        'configure'                     => \$configure,
707        'region=s'                      => \$region,
708        'days:i'                        => \$days,                      # Optional
709);
710
711print "$nicename Grabber v$version\n";
712exit 0 if ($ver);       # Just print the version and leave
713
714# Do the configuration if requested
715# Load the configuration otherwise
716#
717configure() if ($configure);
718load_config();
719
720exit 0 if ($ready_check);       # Just print the version and leave
721
722# Idiot checks on mandatory options
723#
724unless (defined($output_file)) {
725        die "No --output file specified.\n";
726}
727unless (defined($region)) {
728        die "No --region specified.\n";
729}
730
731# Import the Shepherd channels configuration
732#
733unless ($channels_file) {
734        die "No --channels_file specified.\n";
735}
736unless( -r $channels_file) {
737        die "Unable to read channels file $channels_file: $!";
738}
739{       # Block localises the variables
740        local (@ARGV, $/) = ($channels_file);
741        eval <>;
742        die "\nError in channels file!\nDetails:\n$@" if ($@);
743}
744
745
746# Create the in-memory form of the output file
747#
748my $outdom = XML::LibXML::Document->new( '1.0', 'iso-8859-1' );
749my $root = $outdom->createElement('tv');
750$root->setAttribute('generator-info-name', $progname);
751$outdom->setDocumentElement($root);
752
753# Calculate the start and end date for the days requested
754# #
755my ($mday, $mon, $year) = (localtime)[3..5];
756my $startdate = sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday);
757($mday, $mon, $year) = (localtime(time + ($days-1)*86400) )[3..5];
758my $enddate = sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday);
759
760# Get the OzTivo datalist.xml file and interrogate it for;
761# The base URL for each channel
762# The data files available for today onward including their
763#       last modified date.
764#
765
766my $datalist = get_url("http://$user:$pw\@minnie.tuhs.org/xmltv/datalist.xml.gz");
767unless (defined($datalist)) {
768        die "\nError fetching data list xml file!\n";
769}
770
771# Make the user credentials safe
772my $credentials = uri_escape($user) .':' . uri_escape($pw);
773
774my $dlparser = XML::LibXML->new();              # For the datalist.xml file
775my $progparser = XML::LibXML->new();    # For the programme data files
776
777my $dlxml = $dlparser->parse_string($datalist);
778foreach my $sname ( keys(%$channels), keys(%$opt_channels) ) {
779        # Work out which OzTivo channel this Shepherd channel is
780        #
781        # The order of search for a mapping is;
782        #       Region specific mapping (under numeric region code keys)
783        #       National mapping (under hash key 'National')
784        #       Pay TV mapping (under hash key 'PAY')
785        # First mapping found wins.
786        #
787        my $oname = $s_to_o_map{$region}->{$sname};
788        if (! defined($oname)) {        # Try a National channel
789                $oname = $s_to_o_map{'National'}->{$sname};
790        }
791        if (! defined($oname)) {        # Try a PAY channel
792                $oname = $s_to_o_map{'PAY'}->{$sname};
793        }
794        unless (defined($oname)) {
795                print "\nNo region $region mapping from " .
796                        "Shepherd channel $sname to OzTivo channel\n\n";
797                next;
798        }
799
800        # What XMLTV id is this channel. May be in either channels hash
801        #
802        my $xmltvid = defined($channels->{$sname}) ? 
803                $channels->{$sname}: $opt_channels->{$sname};
804        unless (defined($xmltvid)) {
805                print "\nNo XMLTV id for Shepherd channel $sname\n\n";
806                next;
807        }
808
809        # Construct a clone of select parts of the channel element in the output document.
810        # Ignores non-XMLTV elements present in the datalist.xml file.
811        #
812        my ($channelnode) = $dlxml->findnodes('/tv/channel[@id="' . $oname . '"]');
813        unless (defined($channelnode)) {
814                print "\nCannot find the $oname channel element in the datalist.xml file\n";
815                next;
816        }
817        my $newnode = $channelnode->cloneNode(0);
818        $newnode->setAttribute('id', $xmltvid);
819        foreach my $node ($channelnode->findnodes('display-name|url|icon')) {
820                $newnode->appendChild($node->cloneNode(1));
821        }
822        $root->insertBefore($newnode, $root->firstChild);
823
824       
825        # Get the first base URL for the channel
826        # and mangle our user name and password into it
827        #
828        my $baseurl = $dlxml->findvalue('/tv/channel[@id="' . $oname . '"]/base-url[1]');
829        unless (defined($baseurl)) {
830                print "\nCannot find the $oname baseurl in the datalist.xml file\n";
831                next;
832        }
833        $baseurl =~ s!^(https?://)!$1$credentials\@!;
834
835        # Process the list of data files for today or later
836        #
837        my @datafornodes = $dlxml->findnodes('/tv/channel[@id="' . $oname . '"]/datafor');
838        unless (@datafornodes) {
839                print "\nCannot find the $oname datafor records in the datalist.xml file\n";
840                next;
841        }
842        foreach my $datafornode (@datafornodes) {
843                my $filedate = $datafornode->findvalue('child::text()');
844
845                if ($filedate ge $startdate && $filedate le $enddate ) {        # Today or future data
846                        # Build the source file name and fetch it
847                        #
848                        my $url = $baseurl . uri_escape( $oname . '_' . $filedate . '.xml.gz' );
849                        my $progdata = get_url($url);
850                        unless ($progdata) {
851                                print "\nCannot retrieve the $oname programme data for $filedate\n";
852                                next;
853                        }
854
855                        # We have the data we need to actually build the output file
856                        #
857                        my $progxml = $progparser->parse_string($progdata);
858                        my @prognodes = $progxml->findnodes('/tv/programme');
859                        unless (@prognodes) {
860                                print "\nCannot find the $oname programme records in the $filedate file\n";
861                                next;
862                        }
863                        foreach my $prognode (@prognodes) {
864                                # Remove the node from the source document and make it part of
865                                # our output document.
866                                #
867                                $outdom->adoptNode($prognode);
868                                $root->appendChild($prognode);
869
870                                # Correct the channel attribute to use our desired XMLTV id
871                                # as specified in the channels.conf file.
872                                #
873                                $prognode->setAttribute('channel', $xmltvid);
874
875                                # If necessary force the local time zone onto the start and end times
876                                #
877                                if ($force_timezone_set{$oname}) {
878                                        my $starttime = force_local_timezone($prognode->getAttribute('start'));
879                                        my $stoptime = force_local_timezone($prognode->getAttribute('stop'));
880                                        $prognode->setAttribute('start', $starttime);
881                                        $prognode->setAttribute('stop', $stoptime);
882                                }
883                        }
884                        $progxml = undef;
885                }
886        }
887}
888$dlxml = undef;
889
890# Write the output
891#
892$outdom->toFile($output_file, 1)
893        or die "\nCould not create output file $output_file\n";
Note: See TracBrowser for help on using the browser.