root/trunk/grabbers/oztivo

Revision 1401, 26.4 kB (checked in by max, 3 months ago)

oztivo channel updates

Line 
1#!/usr/bin/env perl
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#
8# http://www.oztivo.net         OzTiVo TWiki
9# http://tvguide.oztivo.net     TV Guide site, needs twiki login/password
10# http://xml.oztivo.net/xmltv   XML files cache machine, copy from main machine
11# http://www.oztivo.net/xmltv   XML files main machine (will be removed end of June 2008)
12
13use strict;
14use warnings;
15
16use Getopt::Long;
17use Shepherd::Common;
18use XML::LibXML;
19use URI::Escape;
20use POSIX;
21use Time::Local;
22
23# ensure 'cache' directory exists (or is created) before the 'use HTTP::Cache'
24# below or it'll fail
25BEGIN {
26        -d "cache" or mkdir "cache" or die "Cannot create directory cache: $!";
27}
28
29# Setup caching so we are good Netizens
30# This works transparently under the LWP::* modules
31#
32use HTTP::Cache::Transparent (
33        BasePath  => "cache",
34        MaxAge    => 8*24, # hours
35        Verbose   => 0,
36        NoUpdate  => 15*60,
37        ApproveContent => sub { return $_[0]->is_success },
38);
39
40
41################################################################################
42# Global variables
43#
44my $progname = 'oztivo';
45my $nicename = 'OzTivo';
46my $version = '2.50';
47my $config_file = "$progname.pw";
48my $output_file = undef;
49my $channels_file;
50my $ver;
51my $ready_check;
52my $configure;
53my $region = undef;
54my $days = 7;
55
56# Channel to XMLTvID map from channels file
57#
58my $channels, my $opt_channels;
59
60# User name and password from configuration file
61#
62my $user;
63my $pw;
64
65
66################################################################################
67# Setup a mapping file to go from a Shepherd channel name to an OzTivo name
68# Taking into consideration the region.
69#
70# The order of search for a mapping is;
71#       Region specific mapping (under numeric region code keys)
72#       National mapping (under hash key 'National')
73#       Pay TV mapping (under hash key 'PAY')
74# First mapping found wins.
75#
76my %s_to_o_map = (
77        'National' => { # National channel mappings, all timeshifted
78                'ABC2 / ABC4' => 'ABC2',
79                'ABC3' => 'ABC3',
80                'ABC News 24' => 'ABC-News24',
81                #'7HD'=>'SevenHD-Mel',
82                'Prime HD' => 'SevenHD', # no such thing yet
83                'Nine HD' => 'NineHD',
84                #'TEN HD' => 'TenHD',
85                #'TEN HD'=>'TenHD-Vic',
86                'GO!' => 'GO',
87                '7mate' => '7mate',
88                'ELEVEN' => 'ELEVEN',
89        },
90        'VIC' => {
91                'ONE' => 'One-Vic',
92                'One Digital' => 'One-Vic',
93                'SBS TWO' => 'SBSTWO-Vic',
94                '7TWO' => '7TWO-Vic',
95                'GEM' => 'GEM-Vic',
96        },
97        'NSW' => {
98                'ONE' => 'One-NSW',
99                'One Digital' => 'One-NSW',
100                'SBS TWO' => 'SBSTWO-NSW',
101                '7TWO' => '7TWO-NSW',
102                'GEM' => 'GEM-NSW',
103        },
104        'QLD' => {
105                'ONE' => 'One-Qld',
106                'One Digital' => 'One-Qld',
107                'SBS TWO' => 'SBSTWO-Qld',
108                '7TWO' => '7TWO-Qld',
109                'GEM' => 'GEM-Qld',
110        },
111        'WA' => {
112                'ONE' => 'One-WA',
113                'One Digital' => 'One-WA',
114                'SBS TWO' => 'SBSTWO-WA',
115                '7TWO' => '7TWO-WA',
116                'GEM' => 'GEM-WA',
117        },
118        'SA' => {
119                'ONE' => 'One-SA',
120                'One Digital' => 'One-SA',
121                'SBS TWO' => 'SBSTWO-SA',
122                '7TWO' => '7TWO-SA',
123                'GEM' => 'GEM-SA',
124        },
125        'NT' => {
126                'ONE' => 'One-Dar',
127                'One Digital' => 'One-Dar',
128                'SBS TWO' => 'SBSTWO-NT',
129                '7TWO' => '7TWO-NT',
130        },
131        'ACT' => {
132                'ONE' => 'One-NSW',
133                'One Digital' => 'One-NSW',
134                'SBS TWO' => 'SBSTWO-NSW',
135                '7TWO' => '7TWO-ACT',
136                'GEM' => 'GEM-ACT',
137        },
138        'TAS' => {
139                'ONE' => 'One-Tas',
140                'One Digital' => 'One-Tas',
141                'SBS TWO' => 'SBSTWO-Tas',
142                '7TWO' => '7TWO-Tas',
143                'GEM' => 'GEM-Tas',
144        },
145        '67' => {       # NSW: Griffith
146                'ABC1' => 'ABC-NSW',
147                'Prime' => 'PrimeS',
148                'SBS ONE' => 'SBS-NSW',
149                'SC10' => 'SC10-NSW',
150                'WIN' => 'WIN-NSW',
151        },
152        '90' => {       # VIC: Ballarat
153                'ABC1' => 'ABC-Vic',
154                'Prime' => 'Prime-Vic',
155                'SBS ONE' => 'SBS-Vic',
156                'SC10' => 'SC10-Vic',
157                'WIN' => 'WIN-Vic',
158                'Imparja' => 'IMP',
159        },
160        '63' => {       # NSW: Broken Hill
161                'ABC1' => 'ABC-NSW',
162                'Central GTS/BKN' => 'GTS-BKN',
163                'SBS ONE' => 'SBS-NSW',
164                'SC10' => 'SC10-NSW',
165        },
166        '102' => {      # WA: Regional
167                'ABC1' => 'ABC-WA',
168                'Access 31' => '31-Per',
169                'Golden West' => 'GWN-WA',
170                'Nine' => 'Nine-WA',
171                'SBS ONE' => 'SBS-WA',
172                'Seven' => 'Seven-WA',
173                'TEN' => 'Ten-WA',
174                'WIN' => 'WIN-WA',
175        },
176        '78' => {       # QLD: Gold Coast
177                'ABC1' => 'ABC-Qld',
178                'NBN' => 'NWN-GC',
179                'Nine' => 'Nine-Qld',
180                'Prime' => 'Prime-GC',
181                'SBS ONE' => 'SBS-Qld',
182                'Seven' => 'Seven-Bris',
183                'SC10' => 'SC10-GC',
184                'TEN' => 'Ten-Qld',
185        },
186        '255' => {      # QLD: Sunshine Coast
187                'ABC1' => 'ABC-Qld',
188                'SBS ONE' => 'SBS-Qld',
189                'Seven' => 'Seven-Qld',
190                'TEN' => 'Ten-Qld',
191                'SC10' => 'SC10-Qld',
192                'WIN' => 'Win-Qld',
193        },
194        '256' => {      # QLD: Toowoomba
195                'ABC1' => 'ABC-Qld',
196                'SBS ONE' => 'SBS-Qld',
197                'Seven' => 'Seven-Qld',
198                'SC10' => 'SC10-Qld',
199                'WIN' => 'Win-Qld',
200        },
201        '258' => {      # QLD: Wide Bay
202                'ABC1' => 'ABC-Qld',
203                'SBS ONE' => 'SBS-Qld',
204                'Seven' => 'Seven-Qld',
205                'SC10' => 'Ten-Qld',
206                'WIN' => 'Win-Qld',
207        },
208        '254' => {      # QLD: Rockhampton
209                'ABC1' => 'ABC-Qld',
210                'SBS ONE' => 'SBS-Qld',
211                'Seven' => 'Seven-Qld',
212                'SC10' => 'Ten-Qld',
213                'WIN' => 'Win-Qld',
214        },
215        '253' => {      # QLD: Mackay
216                'ABC1' => 'ABC-Qld',
217                'SBS ONE' => 'SBS-Qld',
218                'Seven' => 'Seven-Qld',
219                'SC10' => 'Ten-Qld',
220                'WIN' => 'Win-Qld',
221        },
222        '257' => {      # QLD: Townsville
223                'ABC1' => 'ABC-Qld',
224                'SBS ONE' => 'SBS-Qld',
225                'Seven' => 'Seven-Qld',
226                'SC10' => 'Ten-Qld',
227                'WIN' => 'Win-Qld',
228        },
229        '79' => {       # QLD: Cairns
230                'ABC1' => 'ABC-Qld',
231                'SBS ONE' => 'SBS-Qld',
232                'Seven' => 'Seven-Qld',
233                'SC10' => 'Ten-Qld',
234                'WIN' => 'Win-Qld',
235        },
236        '107' => {      # SA: Remote & Central
237                'ABC1' => 'ABC-SA',
238                'Imparja' => 'IMP',
239                'SBS ONE' => 'SBS-SA',
240                'SCTV Central' => 'Seven-SA',
241        },
242        '88' => {       # Tasmania
243                'ABC1' => 'ABC-Tas',
244                'SBS ONE' => 'SBS-Tas',
245                'Southern Cross' => 'SC-Tas',
246                'TDT' => 'TDT-Tas',
247                'WIN' => 'Win-Tas',
248        },
249        '93' => {       # VIC: Geelong
250                'ABC1' => 'ABC-Vic',
251                'Channel 31' => '31-Mel',
252                'Nine' => 'Nine-Mel',
253                'SBS ONE' => 'SBS-Vic',
254                'Seven' => 'Seven-Mel',
255                'TEN' => 'Ten-Vic',
256        },
257        '106' => {      # NSW: Remote and Central
258                'ABC1' => 'ABC-NSW',
259                'Imparja' => 'IMP',
260                'Prime' => 'PrimeS',
261                'SBS ONE' => 'SBS-NSW',
262                'SCTV Central' => 'Seven-Syd',
263        },
264        '126' => {      # ACT
265                'ABC1' => 'ABC-Can',
266                'Prime' => 'Prime-Can',
267                'SBS ONE' => 'SBS-Can',
268                'SC10' => 'Ten-Can',
269                'WIN' => 'WIN-Can',
270        },
271        '82' => {       # SA: Renmark
272                'ABC1' => 'ABC-SA',
273                'SBS ONE' => 'SBS-SA',
274                'WIN TEN' => 'TEN-SA',
275                'WIN' => 'Win-Vic',
276        },
277        '74' => {       # NT: Darwin
278                'ABC1' => 'ABC-NT',
279                'SBS ONE' => 'SBS-NT',
280                'Southern Cross' => 'Seven-Dar',
281                'Nine' => 'Nine-NT',
282                'DDT' => 'Ten-Dar',
283        },
284        '85' => {       # SA: South East SA
285                'ABC1' => 'ABC-SA',
286                'SBS ONE' => 'SBS-SA',
287                'WIN TEN' => 'Ten-SA',
288                'Seven' => 'WIN7-SA',
289                'WIN Nine' => 'Nine-SA'
290        },
291        '83' => {       # SA: Riverland
292                'ABC1' => 'ABC-SA',
293                'SBS ONE' => 'SBS-SA',
294                'WIN TEN' => 'TEN-SA',
295                'WIN' => 'Win-Vic',
296        },
297        '95' => {       # VIC: Mildura/Sunraysia
298                'ABC1' => 'ABC-Vic',
299                'Prime' => 'Prime-Vic',
300                'SBS ONE' => 'SBS-Vic',
301                'TEN' => 'Ten-Vic',
302                'SC10' => 'SC10-Vic',
303                'WIN' => 'WIN-Vic',
304        },
305        '75' => {       # QLD: Brisbane
306                'ABC1' => 'ABC-Qld',
307                'QCTV' => 'BRIZ',
308                'Nine' => 'Nine-Qld',
309                'SBS ONE' => 'SBS-Qld',
310                'Seven' => 'Seven-Bris',
311                'TEN' => 'Ten-Qld',
312        },
313        '94' => {       # VIC: Melbourne
314                'ABC1' => 'ABC-Vic',
315                'Channel 31' => '31-Mel',
316                'Nine' => 'Nine-Mel',
317                'SBS ONE' => 'SBS-Vic',
318                'Seven' => 'Seven-Mel',
319                'TEN' => 'Ten-Vic',
320        },
321        '108' => {      # NT: Remote & Central
322                'ABC1' => 'ABC-NT',
323                'Imparja' => 'IMP',
324                'SBS ONE' => 'SBS-NT',
325                'SCTV Central' => 'Seven-Dar',
326        },
327        '114' => {      # QLD: Remote & Central
328                'ABC1' => 'ABC-Qld',
329                'Imparja' => 'IMP',
330                'SBS ONE' => 'SBS-Qld',
331                'SCTV Central' => 'Seven-Qld',
332        },
333        '184' => {      # NSW: Newcastle
334                'ABC1' => 'ABC-NSW',
335                'NBN' => 'NBN',
336                'Prime' => 'PrimeN',
337                'SBS ONE' => 'SBS-NSW',
338                'SC10' => 'SC10-NSW',
339                'ONE' => 'One-Newc',
340        },
341        '69' => {       # NSW: Tamworth
342                'ABC1' => 'ABC-NSW',
343                'NBN' => 'NBN',
344                'Prime' => 'PrimeN',
345                'SBS ONE' => 'SBS-NSW',
346                'SC10' => 'SC10-NSW',
347        },
348        '71' => {       # NSW: Wollongong
349                'ABC1' => 'ABC-NSW',
350                'Prime' => 'PrimeS',
351                'SBS ONE' => 'SBS-NSW',
352                'SC10' => 'SC10-NSW',
353                'WIN' => 'WIN-NSW',
354        },
355        '259' => {      # NSW: Far South Coast
356                'ABC1' => 'ABC-NSW',
357                'Prime' => 'Prime-Can',
358                'SBS ONE' => 'SBS-NSW',
359                'SC10' => 'SC10-NSW',
360                'WIN' => 'WIN-NSW',
361        },
362        '261' => {      # NSW: Lismore/Coffs Harbour
363                'ABC1' => 'ABC-NSW',
364                'NBN' => 'NBN',
365                'Prime' => 'PrimeN',
366                'SBS ONE' => 'SBS-NSW',
367                'SC10' => 'SC10-NSW',
368        },
369        '262' => {      # NSW: Orange/Dubbo
370                'ABC1' => 'ABC-NSW',
371                'Prime' => 'PrimeS',
372                'SBS ONE' => 'SBS-NSW',
373                'SC10' => 'SC10-NSW',
374                'WIN' => 'WIN-NSW',
375        },
376        '263' => {      # NSW: Taree/Port Macquarie
377                'ABC1' => 'ABC-NSW',
378                'NBN' => 'NBN',
379                'Prime' => 'PrimeN',
380                'SBS ONE' => 'SBS-NSW',
381                'SC10' => 'SC10-NSW',
382        },
383        '264' => {      # NSW: Wagga Wagga
384                'ABC1' => 'ABC-NSW',
385                'Prime' => 'PrimeS',
386                'SBS ONE' => 'SBS-NSW',
387                'SC10' => 'SC10-NSW',
388                'WIN' => 'WIN-NSW',
389        },
390        '81' => {       # SA: Adelaide
391                'ABC1' => 'ABC-SA',
392                'C31 Adelaide' => '31-Adl',
393                'Nine' => 'Nine-SA',
394                'SBS ONE' => 'SBS-SA',
395                'Seven' => 'Seven-SA',
396                'TEN' => 'Ten-SA',
397        },
398        '98' => {       # VIC: Gippsland
399                'ABC1' => 'ABC-Vic',
400                'Imparja' => 'IMP',
401                'Prime' => 'Prime-Vic',
402                'SBS ONE' => 'SBS-Vic',
403                'SCTV Central' => 'Seven-Mel',
404                'SC10' => 'Ten-Vic',
405                'WIN' => 'WIN-Vic',
406        },
407        '66' => {       # NSW: Central Coast
408                'ABC1' => 'ABC-NSW',
409                'NBN' => 'NBN',
410                'Nine' => 'Nine-Syd',
411                'Prime' => 'PrimeN',
412                'SBS ONE' => 'SBS-NSW',
413                'Seven' => 'Seven-Syd',
414                'Sthn Cross TEN' => 'SC10-NSW',
415                'TEN' => 'SC10-NSW',
416        },
417        '73' => {       # NSW: Sydney
418                'ABC1' => 'ABC-NSW',
419                'Channel NSW' => undef,
420                'Nine' => 'Nine-Syd',
421                'SBS ONE' => 'SBS-NSW',
422                'Seven' => 'Seven-Syd',
423                'TEN' => 'SC10-NSW',
424                'TVS' => '31-Syd',
425        },
426        '101' => {      # WA: Perth
427                'ABC1' => 'ABC-WA',
428                'Access 31' => '31-Per',
429                'Nine' => 'Nine-WA',
430                'SBS ONE' => 'SBS-WA',
431                'Seven' => 'Seven-WA',
432                'TEN' => 'Ten-WA',
433        },
434        '86' => {       # SA: Spencer Gulf
435                'ABC1' => 'ABC-SA',
436                'Central GTS/BKN' => 'GTS-BKN',
437                'Nine' => 'Nine-SA',
438                'SBS ONE' => 'SBS-SA',
439                'Sthn Cross TEN' => 'SC10-NSW',
440        },
441        '266' => {      # VIC: Bendigo
442                'ABC1' => 'ABC-Vic',
443                'Prime' => 'Prime-Vic',
444                'SBS ONE' => 'SBS-Vic',
445                'SC10' => 'SC10-Vic',
446                'WIN' => 'WIN-Vic',
447                'Imparja' => 'IMP',
448        },
449        '267' => {      # VIC: Shepparton
450                'ABC1' => 'ABC-Vic',
451                'Prime' => 'Prime-Vic',
452                'SBS ONE' => 'SBS-Vic',
453                'SC10' => 'SC10-Vic',
454                'WIN' => 'WIN-Vic',
455        },
456        '268' => {      # VIC: Albury/Wodonga
457                'ABC1' => 'ABC-Vic',
458                'Prime' => 'Prime-Vic',
459                'SBS ONE' => 'SBS-Vic',
460                'SC10' => 'SC10-Vic',
461                'WIN' => 'WIN-Vic',
462        },
463        'PAY' => {      # Pay channels
464                 '111HITS2' => 'HITS+2',
465                 '111Hits' => 'HITS',
466                 '13THSTREET' => '13THST',
467                 '13thSTREET2' => '13THST+2',
468                 #'3DChannel' => '',
469                 'ACC' => 'ACC',
470                 #'' => 'ADULT',
471                 #'' => 'ADULT1',
472                 #'' => 'ADULT2',
473                 'AIR' => 'AIRACT',
474                 'AlJazeera' => 'ALJA',
475                 'AnimalPlanet' => 'ANIMAL',
476                 'AntennaPacific' => 'ANT',
477                 'APAC' => 'A-PAC',
478                 #'' => 'ARCADE',
479                 'Arena' => 'ARNA',
480                 'Arena2' => 'ARNA+2',
481                 #'' => 'ART',
482                 'Aurora' => 'AUR',
483                 #'' => 'AV',
484                 'BBCKnowledge' => 'BBCK',
485                 #'BBCKnowledgeHD' => '',
486                 'BBCWorldNews' => 'BBC',
487                 #'' => 'BETHTV',
488                 'Bio' => 'BIOG',
489                 'BloombergTelevision' => 'BLM',
490                 #'' => 'BNTV',
491                 'Boomerang' => 'BOOM',
492                 #'' => 'CANALSUR',
493                 'CartoonNetwork' => 'CART',
494                 'Cbeebies' => 'CBEEB',
495                 #'' => 'CCTV4',
496                 'CCTVNews' => 'CCTV9',
497                 'ChannelV' => 'V',
498                 #'' => 'Cinemax',
499                 #'' => 'CLUBV',
500                 'CMC' => 'CMC',
501                 #'' => 'CMTE',
502                 #'' => 'CMTER1',
503                 #'' => 'CMTER2',
504                 #'' => 'CMTER3',
505                 #'' => 'CMTER4',
506                 'CNBC' => 'CNBC',
507                 'CNN' => 'CNNI',
508                 #'' => 'CNNfn',
509                 'Comedy2' => 'CMDY+2',
510                 'ComedyChannel' => 'CMDY',
511                 'Crime' => 'CRIME',
512                 #'' => 'CVISION',
513                 #'' => 'D4NEWS',
514                 #'' => 'DAAI',
515                 #'' => 'DIG',
516                 #'' => 'Digital4',
517                 #'' => 'DIG-jazz',
518                 'Discovery2' => 'DISC+2',
519                 'DiscoveryChannel' => 'DISC',
520                 'DiscoveryHealth' => 'HEALTH',
521                 'DiscoveryRealTime' => 'DISCRT',
522                 'DiscoveryScience' => 'SCIENCE',
523                 'DiscoveryTurboMAX' => 'DTM',
524                 #'DiscoveryWorldHD' => '',
525                 'DiscTurboMAX2' => 'DTM+2',
526                 'DisneyChannel' => 'DISN',
527                 #'' => 'DWTV',
528                 'E!Entertainment' => 'E!',
529                 #'' => 'ENEWS-GE',
530                 #'' => 'ENEWSI',
531                 #'' => 'ENEWS-IT',
532                 #'' => 'ENEWSS',
533                 #'' => 'ENEWS-SP',
534                 #'' => 'ERITV',
535                 #'' => 'ERTW',
536                 'ESPN' => 'ESPN',
537                 #'ESPNHD' => '',
538                 #'' => 'EURONEWS',
539                 'Eurosport' => 'EUROSPORT',
540                 'Eurosportnews' => 'ESNEWS',
541                 #'' => 'EWTN',
542                 'EXPO' => 'EXPO',
543                 'FashionTV' => 'FASH',
544                 'FMC' => 'FMC',
545                 'FOX82' => 'FOX8+2',
546                 'FOX8' => 'FOX8',
547                 #'FOX8HD' => '',
548                 'FOXClassics2' => 'CLAS+2',
549                 'FOXClassics' => 'CLAS',
550                 'FOXNews' => 'FOXN',
551                 'FOXSPORTS1' => 'FS1',
552                 #'FoxSports1HD' => '',
553                 'FOXSPORTS2' => 'FS2',
554                 #'FoxSports2HD' => '',
555                 'FOXSPORTS3' => 'FS3',
556                 #'FoxSports3HD' => '',
557                 'FOXSPORTSNews' => 'FSN',
558                 'FUELTV' => 'FUEL',
559                 'FX' => 'FX',
560                 #'' => 'GUIDE',
561                 #'' => 'GWORLD',
562                 #'' => 'HIST+2',
563                 #'' => 'HOUSE',
564                 'HOWTOChannel' => 'HOWTO',
565                 #'' => 'IMP',
566                 #'' => 'ISHTAR',
567                 #'' => 'JJJ',
568                 'KidsCo' => 'KIDSCO',
569                 #'' => 'LBC',
570                 #'' => 'LEONARDO',
571                 'LifeStyle2' => 'LIFE+2',
572                 'LifestyleFOOD2' => 'FOOD+2',
573                 'LifeStyleFOOD' => 'FOOD',
574                 'LifeStyleYOU' => 'LYOU',
575                 'LifStyleYOU2' => 'LYOU+2',
576                 #'' => 'LNTV',
577                 #'' => 'MACQD',
578                 #'' => 'MAIN',
579                 'MAX' => 'max',
580                 #'' => 'MCMTop',
581                 #'' => 'MCMTOP',
582                 #'' => 'MINDGAMES',
583                 'MOVIEEXTRA' => 'MOVX',
584                 'MOVIEGREATS' => 'MOVG',
585                 'MOVIEONE' => 'MOV1',
586                 #'MOVIEONEHD' => '',
587                 'MOVIETWO' => 'MOV1+2',
588                 'MTVClassic' => 'MTVC',
589                 'MTV' => 'MTV',
590                 'MTVHits' => 'MTVH',
591                 'MTVNLive' => 'MTVN',
592                 #'MTVNLiveHD' => '',
593                 #'' => 'NASIA',
594                 'NatGeoAdventure' => 'ADV1',
595                 #'NatGeoAdventure2' => '',
596                 'NatGeoWild' => 'NGW',
597                 #'NatGeoWildHD' => '',
598                 'NationalGeographic' => 'NGEO',
599                 'NatGeo2' => 'NGEO+2',
600                 #'NatGeoHD' => '',
601                 #'' => 'NBN',
602                 #'' => 'NBN-GC',
603                 'Nickelodeon' => 'NICK',
604                 'NickJr' => 'NICKJR',
605                 'NITV' => 'NITV',
606                 #'' => 'ODY',
607                 #'' => 'OLY1',
608                 #'' => 'OLY2',
609                 #'' => 'OLY3',
610                 #'' => 'OLY4',
611                 #'' => 'OUT-TV',
612                 'Ovation' => 'OVAT',
613                 'PlayhouseDisney' => 'PHDISN',
614                 #'' => 'PREVIEW',
615                 'RAIITALIA' => 'RAI',
616                 #'' => 'RMTV',
617                 #'' => 'RTPI',
618                 'SCIFI2' => 'Sci-Fi+2',
619                 'SCIFI' => 'Sci-Fi',
620                 #'' => 'SENATE',
621                 #'' => 'ServiceNSW',
622                 'showcase' => 'SHWC',
623                 #'showcaseHD' => '',
624                 'showcasetwo' => 'SHWC+2',
625                 'showtimeaction' => 'SACT',
626                 #'showtimeactionHD' => '',
627                 'showtimecomedy' => 'SCOM',
628                 'showtimedrama' => 'SDRA',
629                 'showtimepremiere' => 'SHOW',
630                 #'ShowPremiereHD' => '',
631                 'showtimetwo' => 'SHW2',
632                 #'' => 'SHWGRTS',
633                 'SKYNewsBusiness' => 'SKYB',
634                 'SKYNewsNational' => 'SKYN',
635                 'SKYRacing' => 'SKYR',
636                 #'SKYRacing2' => '',
637                 #'SKYRacingWorld' => '',
638                 #'' => 'SPORTSEL',
639                 #'' => 'StarMovies',
640                 'SPEED' => 'SPEED',
641                 #'SPEEDHD' => '',
642                 'STARPICS1' => 'SPICS',
643                 #'STARPICS1HD' => '',
644                 'STARPICS2' => 'SPICS+2',
645                 #'STARPICS2HD' => '',
646                 #'' => 'StarWorld',
647                 'SetantaSports' => 'STAS',
648                 #'' => 'STC',
649                 #'' => 'STV1',
650                 'STVDIO' => 'STVDIO',
651                 #'' => 'SYRIATV',
652                 #'' => 'TABNSW',
653                 'TCM' => 'TCM',
654                 #'' => 'TDT-Tas',
655                 'TheHistoryChannel' => 'HIST',
656                 'TheLifeStyleChannel' => 'LIFE',
657                 'TheStyleNetwork' => 'STYLE',
658                 'TheWeatherChannel' => 'TWC',
659                 'TLC' => 'TRAVEL',
660                 #'' => 'TMF',
661                 'TV12' => 'TV1+2',
662                 'TV1' => 'TV1',
663                 #'' => 'TV5',
664                 #'' => 'TVCHILE',
665                 #'' => 'TVE',
666                 'TVN' => 'TVN',
667                 #'' => 'TVS',
668                 'TVSN' => 'TVSN',
669                 'UKTV2' => 'UKTV+2',
670                 'UKTV' => 'UKTV',
671                 #'UKTVHD' => '',
672                 'Universal' => 'UNIV',
673                 'VHits' => 'V2',
674                 #'' => 'VTV',
675                 'W' => 'W',
676                 'W2' => 'W+2',
677                 #'WHD' => '',
678                 #'' => 'WEIN',
679                 #'' => 'WINE',
680                 #'' => 'WLINK',
681                 'WORLDMOVIES' => 'WMOV',
682                 #'' => 'WTV',
683                 #'' => 'WZONE',
684        }
685);
686
687
688################################################################################
689# Routine for forcing the timezone of a listing to the local time zone.
690# This has the effect of mimicing the time-shifting of national programmes
691# such as ABC2 for which there is a single programme in, say, Sydney time, but
692# where the programmes are broadcast at the same wall clock time everywhere in
693# Australia. 
694# Care is taken to use the local time zone at the time of the programme.
695#
696# Also defines a list of OzTivo channels names this kludge should be applied to.
697#
698sub force_local_timezone($) {
699        my $oztivoTime = shift;
700
701        # Split the given time into components ignoring the specified TZ
702        # Our input looks like "20080324183000 +1100"
703        #
704        my ($year, $mon, $mday, $hr, $min, $sec) = $oztivoTime =~ m/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
705
706        # Treat the time as being local and calculate the UNIX time
707        # This will account for DST in our time zone.
708        #
709        my $unixTime = timelocal($sec, $min, $hr, $mday, $mon-1, $year); # expects months 0..11
710
711        # Return a correctly formatted string
712        #
713        return strftime('%Y%m%d%H%M%S %z', localtime($unixTime));
714}
715
716my %force_timezone_set = map { $_ => 1 } ( values %{$s_to_o_map{'National'}} );
717
718
719################################################################################
720# Routines for configuring this grabber
721#
722sub please_configure_me
723{
724        print <<__EOM;
725If you wish to use $nicename, please run
726        tv_grab_au --configure $progname
727Configuration of $progname is required
728__EOM
729}
730
731sub configure
732{
733        print <<__EOM;
734Configuring...
735
736NOTE: This step is currently optional.
737
738You can create an OzTivo account here:
739  http://www.oztivo.net/twiki/bin/view/TWiki/TWikiRegistration
740... and select some channels here:
741  http://www.oztivo.net/tivo-bin/tvguide.pl
742When you're done, enter your username and password here, to
743enable this grabber to retrieve your guide data automatically:
744Username?
745__EOM
746
747        my $username = <>;
748        chomp $username;
749        unless ($username) {
750                print "No username supplied. Exiting configuration.\n";
751                exit 3;
752        }
753        print "Password?\n";
754        my $pw = <>;
755        chomp $pw;
756        unless ($pw) {
757                print "No password supplied. Exiting configuration.\n";
758                exit 3;
759        }
760        print "Creating config file $config_file...\n";
761        open (CONF, ">$config_file")
762                or die "Unable to create $config_file: $!";
763        print CONF "$username:$pw";
764        close CONF;
765
766        print "Done.\n";
767        exit 0;
768}
769
770sub load_config() {
771        unless (-r $config_file) {
772                print "Can't find $config_file!\n";
773                please_configure_me();
774                exit 1;
775        }
776
777        print "Reading configuration file $config_file.\n";
778
779        unless (open(CONF, $config_file)) {
780                print "Unable to read config file $config_file: $!\n";
781                please_configure_me();
782                exit 1;
783        }
784        my $line = <CONF>;
785        close CONF;
786
787        unless ($line =~ /^(.*):(.*)$/) {
788                print "Unable to parse config file!\n" .
789                          "It should be in the format: username:password\n";
790                please_configure_me();
791                exit 1;
792        }
793        ($user, $pw) = ($1, $2);
794
795        unless ($user and $pw) {
796                print "Failed to extract a sensible username and password from config file.\n";
797                please_configure_me();
798                exit 1;
799        }
800}
801
802################################################################################
803# Local extension of the shared get_url() to cater for password authentication
804# failure
805#
806# Returns the data as a string, or undef if data could not be retrieved
807#
808sub get_url($) {
809        my ($data, $success, $status, $bytes);
810
811        my $url = shift;
812
813        # Don't rely on Shepherd::Common::get_url()'s retry, because
814        # if we get a 401 (wrong password) there's no point in retrying.
815        my $max_tries = 3;
816        foreach my $tries (1 .. $max_tries) {
817                ($data, $success, $status, $bytes) = Shepherd::Common::get_url(
818                                url => $url,
819                                referer => "Shepherd ".$nicename." grabber, version ".$version,
820                                delay => "1-3",
821                                retries => 0);
822
823                last if ($success);
824
825                print "Download failed: $status\n";
826                if ($status =~ /401/)
827                {
828                        print <<__EOM;
829Your OzTivo username and/or password may be incorrect
830The username and password you supplied when configuring the oztivo grabber
831must match your registration details on the OzTivo.com site.
832If this error persists, try reconfiguring:
833        tv_grab_au --configure $progname
834__EOM
835                        last;   # bail out of the retry loop
836                }
837
838                my $sleep = 47 + int(rand(120));
839                print "Sleeping for $sleep seconds before retrying...\n";
840                sleep($sleep);
841        }
842
843        # hack: if we don't get back something that looks like xml then try to
844        # ungzip it
845        if (substr($data,0,1) ne '<') {
846                print "Doesn't look like xml, attempting to gunzip.\n";
847                $data = Compress::Zlib::memGunzip($data);
848        }
849
850        return $data;
851}
852
853
854
855################################################################################
856#
857$| = 1;
858
859GetOptions(
860        'config=s'                      => \$config_file,
861        'channels_file=s'       => \$channels_file,
862        'output=s'                      => \$output_file,
863        'version'                       => \$ver,
864        'ready'                         => \$ready_check,
865        'configure'                     => \$configure,
866        'region=s'                      => \$region,
867        'days:i'                        => \$days,                      # Optional
868);
869
870print "$nicename Grabber v$version\n";
871exit 0 if ($ver);       # Just print the version and leave
872
873# Do the configuration if requested
874# Load the configuration otherwise
875#
876configure() if ($configure);
877#load_config();
878
879exit 0 if ($ready_check);       # Just print the version and leave
880
881# Idiot checks on mandatory options
882#
883unless (defined($output_file)) {
884        die "No --output file specified.\n";
885}
886unless (defined($region)) {
887        die "No --region specified.\n";
888}
889
890# Import the Shepherd channels configuration
891#
892unless ($channels_file) {
893        die "No --channels_file specified.\n";
894}
895unless( -r $channels_file) {
896        die "Unable to read channels file $channels_file: $!";
897}
898{       # Block localises the variables
899        local (@ARGV, $/) = ($channels_file);
900        eval <>;
901        die "\nError in channels file!\nDetails:\n$@" if ($@);
902}
903
904
905# Create the in-memory form of the output file
906#
907my $outdom = XML::LibXML::Document->new( '1.0', 'iso-8859-1' );
908my $root = $outdom->createElement('tv');
909$root->setAttribute('generator-info-name', $progname);
910$outdom->setDocumentElement($root);
911
912# Calculate the start and end date for the days requested
913# #
914my ($mday, $mon, $year) = (localtime)[3..5];
915my $startdate = sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday);
916($mday, $mon, $year) = (localtime(time + ($days-1)*86400) )[3..5];
917my $enddate = sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday);
918
919# Get the OzTivo datalist.xml file and interrogate it for;
920# The base URL for each channel
921# The data files available for today onward including their
922#       last modified date.
923#
924
925my $datalist = get_url("http://xml.oztivo.net/xmltv/datalist.xml.gz");
926unless (defined($datalist)) {
927        die "\nError fetching data list xml file!\n";
928}
929
930# Make the user credentials safe
931#my $credentials = uri_escape($user) .':' . uri_escape($pw);
932
933my $dlparser = XML::LibXML->new();              # For the datalist.xml file
934my $progparser = XML::LibXML->new();    # For the programme data files
935
936my $dlxml = $dlparser->parse_string($datalist);
937foreach my $sname ( keys(%$channels), keys(%$opt_channels) ) {
938        # Work out which OzTivo channel this Shepherd channel is
939        #
940        # The order of search for a mapping is;
941        #       Region specific mapping (under numeric region code keys)
942        #       National mapping (under hash key 'National')
943        #       Pay TV mapping (under hash key 'PAY')
944        # First mapping found wins.
945        #
946        my $oname = $s_to_o_map{$region}->{$sname};
947        if (! defined($oname)) {
948                $oname = $s_to_o_map{&Shepherd::Common::which_state($region)}->{$sname};
949        }
950        if (! defined($oname)) {        # Try a National channel
951                $oname = $s_to_o_map{'National'}->{$sname};
952        }
953        if (! defined($oname)) {        # Try a PAY channel
954                $oname = $s_to_o_map{'PAY'}->{$sname};
955        }
956        unless (defined($oname)) {
957                print "\nNo region $region mapping from " .
958                        "Shepherd channel $sname to OzTivo channel\n\n";
959                next;
960        }
961
962        # What XMLTV id is this channel. May be in either channels hash
963        #
964        my $xmltvid = defined($channels->{$sname}) ? 
965                $channels->{$sname}: $opt_channels->{$sname};
966        unless (defined($xmltvid)) {
967                print "\nNo XMLTV id for Shepherd channel $sname\n\n";
968                next;
969        }
970
971        # Construct a clone of select parts of the channel element in the output document.
972        # Ignores non-XMLTV elements present in the datalist.xml file.
973        #
974        my ($channelnode) = $dlxml->findnodes('/tv/channel[@id="' . $oname . '"]');
975        unless (defined($channelnode)) {
976                print "\nCannot find the $oname channel element in the datalist.xml file\n";
977                next;
978        }
979        my $newnode = $channelnode->cloneNode(0);
980        $newnode->setAttribute('id', $xmltvid);
981        foreach my $node ($channelnode->findnodes('display-name|url|icon')) {
982                $newnode->appendChild($node->cloneNode(1));
983        }
984        $root->insertBefore($newnode, $root->firstChild);
985
986       
987        # Get the first base URL for the channel
988        # and mangle our user name and password into it
989        #
990        my $baseurl = $dlxml->findvalue('/tv/channel[@id="' . $oname . '"]/base-url[1]');
991        unless (defined($baseurl)) {
992                print "\nCannot find the $oname baseurl in the datalist.xml file\n";
993                next;
994        }
995        #$baseurl =~ s!^(https?://)!$1$credentials\@!;
996
997        # Process the list of data files for today or later
998        #
999        my @datafornodes = $dlxml->findnodes('/tv/channel[@id="' . $oname . '"]/datafor');
1000        unless (@datafornodes) {
1001                print "\nCannot find the $oname datafor records in the datalist.xml file\n";
1002                next;
1003        }
1004        foreach my $datafornode (@datafornodes) {
1005                my $filedate = $datafornode->findvalue('child::text()');
1006
1007                if ($filedate ge $startdate && $filedate le $enddate ) {        # Today or future data
1008                        # Build the source file name and fetch it
1009                        #
1010                        my $url = $baseurl . uri_escape( $oname . '_' . $filedate . '.xml.gz' );
1011                        my $progdata = get_url($url);
1012                        unless ($progdata) {
1013                                print "\nCannot retrieve the $oname programme data for $filedate\n";
1014                                next;
1015                        }
1016
1017                        # We have the data we need to actually build the output file
1018                        #
1019                        my $progxml = $progparser->parse_string($progdata);
1020                        my @prognodes = $progxml->findnodes('/tv/programme');
1021                        unless (@prognodes) {
1022                                print "\nCannot find the $oname programme records in the $filedate file\n";
1023                                next;
1024                        }
1025                        foreach my $prognode (@prognodes) {
1026                                # Remove the node from the source document and make it part of
1027                                # our output document.
1028                                #
1029                                $outdom->adoptNode($prognode);
1030                                $root->appendChild($prognode);
1031
1032                                # If there are <star-rating> element(s) present and they contain
1033                                # a <value> element with '0/10' then delete the <star-rating>.
1034                                # OzTivo data uses this to mean 'Unrated' rather than 'Terrible programme'.
1035                                # This rating is used by the post-processors and skews their results.
1036                                #
1037                                my @ratings = $prognode->findnodes("star-rating[value='0/10']");
1038                                foreach my $rating (@ratings) {
1039                                        $rating->unbindNode();
1040                                }
1041                               
1042
1043                                # Correct the channel attribute to use our desired XMLTV id
1044                                # as specified in the channels.conf file.
1045                                #
1046                                $prognode->setAttribute('channel', $xmltvid);
1047
1048                                # If necessary force the local time zone onto the start and end times
1049                                #
1050                                if ($force_timezone_set{$oname}) {
1051                                        my $starttime = force_local_timezone($prognode->getAttribute('start'));
1052                                        my $stoptime = force_local_timezone($prognode->getAttribute('stop'));
1053                                        $prognode->setAttribute('start', $starttime);
1054                                        $prognode->setAttribute('stop', $stoptime);
1055                                }
1056                        }
1057                        $progxml = undef;
1058                }
1059        }
1060}
1061$dlxml = undef;
1062
1063# Write the output
1064#
1065$outdom->toFile($output_file, 1)
1066        or die "\nCould not create output file $output_file\n";
Note: See TracBrowser for help on using the browser.