root/trunk/grabbers/oztivo @ 1056

Revision 1056, 23.5 kB (checked in by paul, 5 years ago)

channel_list,foxtel_swf.conf,oztivo: FOXTELBoxOffice->FoxtelBoxOffice?,LifestyleChannel?2->TheLifestyleChannel?2,RAIInternational->RAIITALIA

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