| 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 | # |
|---|
| 8 | use strict; |
|---|
| 9 | |
|---|
| 10 | use Getopt::Long; |
|---|
| 11 | use Shepherd::Common; |
|---|
| 12 | use XML::LibXML; |
|---|
| 13 | use URI::Escape; |
|---|
| 14 | use POSIX; |
|---|
| 15 | use Time::Local; |
|---|
| 16 | |
|---|
| 17 | # ensure 'cache' directory exists (or is created) before the 'use HTTP::Cache' |
|---|
| 18 | # below or it'll fail |
|---|
| 19 | BEGIN { |
|---|
| 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 | # |
|---|
| 26 | use 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 | # |
|---|
| 38 | my $progname = 'oztivo'; |
|---|
| 39 | my $nicename = 'OzTivo'; |
|---|
| 40 | my $version = '2.12'; |
|---|
| 41 | my $config_file = "$progname.pw"; |
|---|
| 42 | my $output_file = undef; |
|---|
| 43 | my $channels_file; |
|---|
| 44 | my $ver; |
|---|
| 45 | my $ready_check; |
|---|
| 46 | my $configure; |
|---|
| 47 | my $region = undef; |
|---|
| 48 | my $days = 7; |
|---|
| 49 | |
|---|
| 50 | # Channel to XMLTvID map from channels file |
|---|
| 51 | # |
|---|
| 52 | my $channels, my $opt_channels; |
|---|
| 53 | |
|---|
| 54 | # User name and password from configuration file |
|---|
| 55 | # |
|---|
| 56 | my $user; |
|---|
| 57 | my $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 | # |
|---|
| 70 | my %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 | # |
|---|
| 544 | sub 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 | |
|---|
| 562 | my %force_timezone_set = map { $_ => 1 } ( values %{$s_to_o_map{'National'}} ); |
|---|
| 563 | |
|---|
| 564 | |
|---|
| 565 | ################################################################################ |
|---|
| 566 | # Routines for configuring this grabber |
|---|
| 567 | # |
|---|
| 568 | sub please_configure_me |
|---|
| 569 | { |
|---|
| 570 | print <<__EOM; |
|---|
| 571 | If you wish to use $nicename, please run |
|---|
| 572 | tv_grab_au --configure $progname |
|---|
| 573 | Configuration of $progname is required |
|---|
| 574 | __EOM |
|---|
| 575 | } |
|---|
| 576 | |
|---|
| 577 | sub configure |
|---|
| 578 | { |
|---|
| 579 | print <<__EOM; |
|---|
| 580 | Configuring... |
|---|
| 581 | Before you can use the $nicename grabber, you must create a |
|---|
| 582 | OzTivo 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 |
|---|
| 586 | When you're done, enter your username and password here, to |
|---|
| 587 | enable this grabber to retrieve your guide data automatically: |
|---|
| 588 | Username? |
|---|
| 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 | |
|---|
| 614 | sub 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 | # |
|---|
| 652 | sub 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; |
|---|
| 673 | Your OzTivo username and/or password may be incorrect |
|---|
| 674 | The username and password you supplied when configuring the oztivo grabber |
|---|
| 675 | must match your registration details on the OzTivo.com site. |
|---|
| 676 | If 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 | |
|---|
| 700 | GetOptions( |
|---|
| 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 | |
|---|
| 711 | print "$nicename Grabber v$version\n"; |
|---|
| 712 | exit 0 if ($ver); # Just print the version and leave |
|---|
| 713 | |
|---|
| 714 | # Do the configuration if requested |
|---|
| 715 | # Load the configuration otherwise |
|---|
| 716 | # |
|---|
| 717 | configure() if ($configure); |
|---|
| 718 | load_config(); |
|---|
| 719 | |
|---|
| 720 | exit 0 if ($ready_check); # Just print the version and leave |
|---|
| 721 | |
|---|
| 722 | # Idiot checks on mandatory options |
|---|
| 723 | # |
|---|
| 724 | unless (defined($output_file)) { |
|---|
| 725 | die "No --output file specified.\n"; |
|---|
| 726 | } |
|---|
| 727 | unless (defined($region)) { |
|---|
| 728 | die "No --region specified.\n"; |
|---|
| 729 | } |
|---|
| 730 | |
|---|
| 731 | # Import the Shepherd channels configuration |
|---|
| 732 | # |
|---|
| 733 | unless ($channels_file) { |
|---|
| 734 | die "No --channels_file specified.\n"; |
|---|
| 735 | } |
|---|
| 736 | unless( -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 | # |
|---|
| 748 | my $outdom = XML::LibXML::Document->new( '1.0', 'iso-8859-1' ); |
|---|
| 749 | my $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 | # # |
|---|
| 755 | my ($mday, $mon, $year) = (localtime)[3..5]; |
|---|
| 756 | my $startdate = sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday); |
|---|
| 757 | ($mday, $mon, $year) = (localtime(time + ($days-1)*86400) )[3..5]; |
|---|
| 758 | my $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 | |
|---|
| 766 | my $datalist = get_url("http://$user:$pw\@minnie.tuhs.org/xmltv/datalist.xml.gz"); |
|---|
| 767 | unless (defined($datalist)) { |
|---|
| 768 | die "\nError fetching data list xml file!\n"; |
|---|
| 769 | } |
|---|
| 770 | |
|---|
| 771 | # Make the user credentials safe |
|---|
| 772 | my $credentials = uri_escape($user) .':' . uri_escape($pw); |
|---|
| 773 | |
|---|
| 774 | my $dlparser = XML::LibXML->new(); # For the datalist.xml file |
|---|
| 775 | my $progparser = XML::LibXML->new(); # For the programme data files |
|---|
| 776 | |
|---|
| 777 | my $dlxml = $dlparser->parse_string($datalist); |
|---|
| 778 | foreach 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"; |
|---|