1#!/usr/local/bin/perl -w 2=pod 3 4=head1 NAME 5 6tv_grab_re - Grab TV listings for Reunion Island (France). 7 8=head1 SYNOPSIS 9 10To configure: tv_grab_re --configure [--config-file FILE] 11To grab channels listing: tv_grab_re --list-channels [--output FILE] 12To grab programmes listings: tv_grab_re [--output FILE] [--offset N] [--days N] [--quiet] 13Slower, detailed grab: tv_grab_re --slow [--output FILE] [--offset N] [--days N] [--quiet] 14Help: tv_grab_re --help 15 16=head1 DESCRIPTION 17 18Output TV listings for Canal Satellite Reunion and Parabole Reunion channels 19available in Reunion Island. The data comes from www.canalsatellite-reunion.com 20for Canal Satellite Reunion and from www.parabolereunion.com for Parabole 21Reunion. The default is to grab listing only for the current day. 22By default program descriptions are not downloaded, so if you want description 23and credits, you should activate the --slow option. To grab listing for 24hertzian channels, select them in Canal Satellite Reunion grid. 25 26B<--configure> Grab channels informations and ask for channel type and names. 27 28B<--output FILE> Write to FILE rather than standard output. 29 30B<--days N> Grab N days, rather than only for the current day. 31 32B<--offset N> Start grabbing for N days in the future, eg offset 1 33means start with tomorrow. 34 35B<--slow> Get additional information from the website, like program 36description and credits. 37 38B<--quiet> Suppress the progress messages normally written to standard 39error. 40 41B<--version> Show the version of the grabber. 42 43B<--help> Print a help message and exit. 44 45=head1 SEE ALSO 46 47L<xmltv(5)> 48 49=head1 AUTHOR 50 51Eric Castelnau, eric.castelnau@free.fr 52Inspired by tv_grab_fr written by Sylvain Fabre, centraladmin@lahiette.com 53 54=cut 55 56use XMLTV::Usage <<END 57$0: get Reunion Island television listings in XMLTV format 58To configure: tv_grab_re --configure [--config-file FILE] 59To grab channels listing: tv_grab_re --list-channels [--output FILE] 60To grab programmes listings: tv_grab_re [--output FILE] [--days N] [-offset N] [--quiet] 61Slower, detailed grab: tv_grab_re --slow [--output FILE] [--days N] [--offset N] [--quiet] 62END 63 ; 64 65use warnings; 66use strict; 67use utf8; 68use XMLTV::Version '$Id: tv_grab_re,v 1.25 2010/12/10 19:02:04 dekarl Exp $ '; 69use XMLTV::Capabilities qw/baseline manualconfig/; 70use XMLTV::Description 'Reunion Island'; 71use Getopt::Long; 72use HTML::TreeBuilder; 73use HTML::Entities; # parse entities 74use HTTP::Cookies; 75use IO::File; 76use URI; 77use Date::Manip; 78use XMLTV; 79use XMLTV::Memoize; 80use XMLTV::Ask; 81use XMLTV::ProgressBar; 82use XMLTV::Mode; 83use XMLTV::Config_file; 84use XMLTV::DST; 85use XMLTV::Get_nice; 86use XMLTV::Memoize; XMLTV::Memoize::check_argv 'get_nice'; 87 88### 89### Main declarations 90### 91my %BROADCASTERS = ( 92 'CANALSAT' => "Canal Satellite Reunion", 93 'PARABOLE' => "Parabole Reunion" 94); 95my $CANALSAT_BASE_URL = "http://www.canalsat-reunion.com/no_cache/programmes/guide-des-programmes/programmes-jour-par-jour/"; 96my $CANALSAT_ICON_URL = "http://www.canalsat-reunion.com/uploads/tx_hhmod4bdd/"; 97my $PARABOLE_BASE_URL = "http://www.parabolereunion.com/"; 98my $PARABOLE_ICON_URL = "http://www.parabolereunion.com/images/tmp"; 99 100### 101### Options processing 102### 103my ($opt_offset, $opt_days); 104my $opt_help; 105my $opt_output; 106my $opt_quiet; 107my $opt_config_file; 108my $opt_configure; 109my $opt_list_channels; 110my $opt_slow; 111 112GetOptions( 'days=i' => \$opt_days, 113 'offset=i' => \$opt_offset, 114 'help' => \$opt_help, 115 'output=s' => \$opt_output, 116 'quiet' => \$opt_quiet, 117 'configure' => \$opt_configure, 118 'config-file=s' => \$opt_config_file, 119 'list-channels' => \$opt_list_channels, 120 'slow' => \$opt_slow, 121) or usage(0); 122 123# need help 124usage(1) if $opt_help; 125 126# verbose by default 127$opt_quiet = 0; 128 129# number of day to process 130die 'Number of days must not be negative' if (defined $opt_days && $opt_days < 0); 131die 'Number of days must not be more than 5' if (defined $opt_days && $opt_days > 5); 132$opt_days = 1 if not defined $opt_days; 133 134# offset - zero (default) means start from today 135die 'Offset must not be negative' if (defined $opt_offset && $opt_offset < 0); 136$opt_offset = 0 if not defined $opt_offset; 137 138# output file 139$opt_output = '-' if not defined $opt_output; 140 141# slow mode off by default 142$opt_slow = 0 if not defined $opt_slow; 143 144# Now detects if we are in configure mode 145my $mode = XMLTV::Mode::mode('grab', $opt_configure => 'configure', 146 $opt_list_channels => 'list-channels'); 147 148# File that stores which channels to download. 149my $config_file = XMLTV::Config_file::filename($opt_config_file, 'tv_grab_re', 150 $opt_quiet); 151 152# Content of $config_file 153my @config_lines; 154 155### 156### Global variables 157### 158 159# channels list 160my @channels; 161 162### 163### Sub sections 164### 165sub dprint($) { 166 my $msg = shift; 167 print STDERR "debug: " . $msg; 168} 169 170sub dump_channel($) { 171 my $c = shift; 172 print "type: $c->{'type'}\n"; 173 print "id : $c->{'id'}\n"; 174 print "name: $c->{'name'}\n"; 175 print "icon: $c->{'icon'}\n"; 176} 177 178sub dump_programme($) { 179 my $c = shift; 180 print "channel : $c->{'channel'}\n"; 181 print "title : $c->{'title'}[0][0]\n"; 182 print "start : $c->{'start'}\n"; 183 print "stop : $c->{'stop'}\n"; 184 #print "length : $c->{'length'}sec.\n"; 185 print "category : $c->{'category'}[0][0]\n" if defined $c->{'category'}; 186} 187 188sub new_xmltv_writer() { 189 my %writer_args; 190 my $file = new IO::File(">$opt_output"); 191 die "Cannot write to $opt_output: $!" if not defined $file; 192 $writer_args{OUTPUT} = $file; 193 $writer_args{'encoding'} = 'ISO-8859-1'; 194 return new XMLTV::Writer(%writer_args); 195} 196 197sub post_nice_tree ($;%) { 198 my $url = shift; 199 my $form = shift; 200 201 202 require HTML::TreeBuilder; 203 my $html = $XMLTV::Get_nice::ua->post($url, $form); 204 205 my $t = new HTML::TreeBuilder; 206 $t->parse($html->content) or die "cannot parse content of $url\n"; 207 $t->eof; 208 return $t; 209} 210 211sub get_channels_list($) { 212 my $arg = shift; 213 my @channels; 214 215 if ($arg eq 'CANALSAT') { 216 my $url = "http://www.canalsat-reunion.com/no_cache/programmes/guide-des-programmes/programmes-jour-par-jour/"; 217 my %form; 218 219 $form{'programme[confirmation_choixfuseau]'} = "Envoyer"; 220 $form{'programme[pdf]'} = ""; 221 $form{'Submit'} = "OK"; 222 $form{'programme[genre]'} = ""; 223 $form{'programme[chaine]'} = 13; 224 $form{'programme[horaires]'} = 6.; # whole day 225 #$form{'programme[jour]'} = ; 226 $form{'programme[fuseauChoix]'} = 2; 227 $form{'no_cache'} = 1; 228 229 # get request and parse 230 my $html = post_nice_tree($url, \%form); 231 232 my $chaines = $html->look_down('_tag', 'select', 'name', 'programme[chaine]'); 233 foreach my $chaine ($chaines->look_down('_tag', 'option')) { 234 my %channel; 235 236 my $id = $chaine->attr_get_i('value'); 237 next if ($id eq ""); 238 my $title = $chaine->as_text(); 239 240 $channel{'type'} = "CANALSAT"; 241 $channel{'id'} = $id; 242 $channel{'name'} = $title; 243 $channel{'icon'} = "$CANALSAT_ICON_URL/${id}_grand.gif"; 244 245 push @channels,\%channel; 246 } 247 248 $html->delete(); 249 undef $html; 250 } 251 252 if ($arg eq 'PARABOLE') { 253 my $url = "http://www.parabolereunion.com/index.jsp?childUrl=epg/epg0.jsp"; 254 my $html = get_nice_tree $url; 255 256 my $chaines = $html->look_down('_tag', 'select', 'name', 'sel_channel'); 257 foreach my $chaine ($chaines->look_down('_tag', 'option')) { 258 my %channel; 259 260 my $id = $chaine->attr_get_i('value'); 261 next if ($id == -1); 262 my $title = $chaine->as_text(); 263 $title =~ s/\d+\.//; 264 $title =~ s/(\s)*$//; 265 266 $channel{'type'} = "PARABOLE"; 267 $channel{'id'} = $id; 268 $channel{'name'} = $title; 269 $channel{'icon'} = "$PARABOLE_ICON_URL/channel_logo_small$id.gif"; 270 271 push @channels,\%channel; 272 } 273 274 $html->delete(); 275 undef $html; 276 } 277 278 return @channels; 279} 280 281sub get_canalsat_programmes_list_slow($%) { 282 my $url = shift(@_); 283 my $p = shift(@_); 284 my @directors; 285 my @actors; 286 287 # get request and parse 288 my $html = get_nice_tree $url; 289 $html->objectify_text(); 290 291 # look for director 292 my $div = $html->look_down('_tag' => 'div', 'id' => 'fiche_zoom_fiche_technique'); 293 #""$div->dump(); 294 295 # look for actors 296 my $table = $div->look_down('_tag' => 'table', 297 'width' => '733', 298 #'cellspacing' => '0', 299 'cellpadding' => '0', 300 'border' => '0' 301 ); 302 #$table->dump(); 303 304 my $ptag = $table->look_down('_tag', 'p'); 305 my @texts = $ptag->look_down('_tag', '~text'); 306 foreach my $text (@texts) { 307 my $tt = $text->attr_get_i('text'); 308 309 # année 310 if ($tt =~ /(\d\d\d\d) - /) { 311 $p->{'date'} = $1; 312 } 313 314 # acteurs 315 if ($tt =~ / de (.*) avec (.*) /) { 316 push @directors, $1; 317 318 my @a = split(',', $2); 319 foreach (@a) { 320 if ($_ =~ /(.*) \(.*\)/) { 321 push @actors, $1; 322 } 323 } 324 } 325 326 # présentateur 327 if ($tt =~ / pr.sent. par (.*)/) { 328 my $str = $1; 329 330 if ($str =~ /,/) { 331 my @a = split(',', $str); 332 foreach (@a) { 333 push @actors, $_; 334 } 335 } 336 else { 337 push @actors, $str; 338 } 339 } 340 } 341 342 $p->{credits}{director} = \@directors if @directors; 343 $p->{credits}{actor} = \@actors if @actors; 344 345 $html->delete(); 346 undef $html; 347} 348 349sub get_canalsat_programmes_list($$$) { 350 my ($idchaine, $offset, $days) = @_; 351 die if $offset < 0; 352 die if $days < 1; 353 354 # the progs list to return 355 my @progs = (); 356 357 my $today = ParseDate 'today'; 358 359 for ($offset + 1 .. $offset + $days) { 360 my $n = $_ - 1; 361 362 # the start tag of programs for this day 363 my $start = DateCalc($today, "+ $n days"); 364 my $start_of_day = Date_SetTime($start, 6, 0, 0);; 365 my $stop; 366 my $url_day = UnixDate($start, "%d/%m/%Y"); 367 368 # build the url 369 my $url = "http://www.canalsat-reunion.com/no_cache/programmes/guide-des-programmes/programmes-jour-par-jour/"; 370 my %form; 371 372 $form{'programme[confirmation_choixfuseau]'} = "Envoyer"; 373 $form{'programme[pdf]'} = ""; 374 $form{'Submit'} = "OK"; 375 $form{'programme[genre]'} = ""; 376 $form{'programme[chaine]'} = $idchaine; 377 $form{'programme[horaires]'} = 6.; # whole day 378 $form{'programme[jour]'} = $url_day; 379 $form{'programme[fuseauChoix]'} = 2; 380 $form{'no_cache'} = 1; 381 382 383 # get request and parse 384 my $html = post_nice_tree($url, \%form); 385 $html->objectify_text(); 386 #$html->dump(); 387 388 # look for every DIV elements that contain programm 389 my @divs = $html->look_down('_tag' => 'div', 'class' => qr/contenu_prog_listing /); 390 391 # scan each row 392 foreach my $div (@divs) { 393 # the current prog being processed 394 my %prog; 395 my ($tag, $tt, $stop); 396 #$div->dump(); 397 398 $prog{'channel'} = $idchaine.".canalsat-reunion.com"; 399 400 $tt = $div->attr_get_i('style'); 401 if ($tt =~ /margin-top:(\d+)px/) { 402 my $break = int($1 / 2); 403 404 # if the time between 2 programs is greater than 30min 405 # so we are in the day after (this is needed when grabbing 406 # several days in one time : same program seen twice) 407 next if ($break > 30) 408 } 409 410 # here is the start time 411 $tag = $div->look_down('_tag', '~text'); 412 $tt = $tag->attr_get_i('text'); 413 if ($tt =~ /(\d+):(\d\d)/ ) { 414 $start = Date_SetTime($start, $1, $2, 0); 415 # programs spanning 6am are duplicated on the next day 416 next if ($start lt $start_of_day); 417 418 my $str = UnixDate($start, "%Y%m%d%H%M%S"); 419 $prog{'start'} = $str." +0400"; 420 } 421 422 # compute the duration from height attribute of the div 423 $tt = $div->attr_get_i('style'); 424 if ($tt =~ /height:(\d+)px/) { 425 my $duration = int($1 / 2); 426 $prog{'length'} = $duration * 60; 427 428 $start = DateCalc($start, "+$duration min"); 429 $stop = $start; 430 } 431 432 # here is the title 433 $tag = $div->look_down('_tag' => 'a'); 434 $tt = $tag->attr_get_i('onmouseover'); 435 if ($tt =~ /<span class=blanc bold majuscule>(.*)<\/span>/ ) { 436 # "Fin des programmes" is not a real tv show 437 next if ($1 eq "Fin des programmes"); 438 439 my $title = $1; 440 $title =~ s/\\'/'/g; 441 $prog{'title'} = [ [ $title ] ]; 442 } 443 444 # here is the description 445 if ($tt =~ /contenu_de_tooltip_listing_programme>([^<]*)</) { 446 my $str = $1; 447 $str =~ s/\\'/'/g; 448 $prog{'desc'} = [ [ $str, "fr" ] ] if not $str eq ""; 449 } 450 451 # here is the category 452 $tt = $div->attr_get_i('class'); 453 $prog{'category'} = [ [ "Musique", "fr" ] ] if ($tt =~ /musique$/); 454 $prog{'category'} = [ [ "Cinéma", "fr" ] ] if ($tt =~ /cinema$/); 455 $prog{'category'} = [ [ "Sport", "fr" ] ] if ($tt =~ /sport$/); 456 $prog{'category'} = [ [ "Divertissement", "fr" ] ] if ($tt =~ /divertissement$/); 457 $prog{'category'} = [ [ "Jeunesse", "fr" ] ] if ($tt =~ /jeunesse$/); 458 $prog{'category'} = [ [ "Découverte", "fr" ] ] if ($tt =~ /decouverte$/); 459 $prog{'category'} = [ [ "Infos / Magazine", "fr" ] ] if ($tt =~ /infos_magazine_emission$/); 460 $prog{'category'} = [ [ "Série", "fr" ] ] if ($tt =~ /serie_feuilleton$/); 461 462 # get director/actors if --slow was asked 463 if ($opt_slow) { 464 $tag = $div->look_down('_tag', 'a'); 465 my $href = $CANALSAT_BASE_URL.$tag->attr_get_i('href'); 466 get_canalsat_programmes_list_slow($href, \%prog); 467 } 468 469 # add the current prog to the list if it is valid 470 # and keep a hand on it 471 if (defined $prog{'title'}) { 472 push @progs,\%prog; 473 } 474 } 475 476 $html->delete(); 477 undef $html; 478 } 479 480 return @progs; 481} 482 483sub get_parabole_programmes_list_slow($%) { 484 my $url = shift(@_); 485 my $p = shift(@_); 486 487 # get request and parse 488 my $html = get_nice_tree $url; 489 490 my $t; 491 492 # get the résumé 493 my $resume_tag = $html->look_down('_tag', 'font', 'class', 'commonText'); 494 if (defined $resume_tag) { 495 $t = $resume_tag->as_text(); 496 $p->{'desc'} = [ [ $t, "fr" ] ]; 497 } 498 499 # get actors list 500 my @actors; 501 my $actors_tag = $html->look_down('_tag', 'font', 'class', 'highlightCasting'); 502 if (defined $actors_tag) { 503 $t = $actors_tag->as_text(); 504 505 my @a = split(',', $t); 506 foreach (@a) { 507 if ($_ =~ /(.*) \(.*\)/) { 508 push @actors, $1; 509 next; 510 } 511 512 push @actors, $_; 513 } 514 } 515 516 $p->{credits}{actor} = \@actors if @actors; 517 518} 519 520sub get_parabole_programmes_list( $$$ ) { 521 my ($channel, $offset, $days) = @_; 522 die if $offset < 0; 523 die if $days < 1; 524 525 my $url_base = "http://www.parabolereunion.com/index.jsp?childUrl=epg/epg0.jsp&mode=1&search_title=&sel_time=-1&sel_type=-1&"; 526 my $url_channel = "sel_channel=".$channel."&"; 527 528 # the progs list to return 529 my @progs = (); 530 531 for ($offset + 1 .. $offset + $days) { 532 my $url_day; 533 my $start; 534 535 if ($_ == 1) { $url_day = "sel_day=-1"; } 536 else { $url_day = "sel_day=".($_ - 1); } 537 538 my $n = $_ - 1; 539 $start = DateCalc("today", "+ $n days"); 540 541 my $url = $url_base.$url_channel.$url_day; 542 543 # get request and parse 544 my $html = get_nice_tree $url; 545 $html->objectify_text(); 546 # look for the table of programmes 547 my @tables = $html->look_down('_tag', 'table', 'border' , '0', 'width', '100%'); 548 549 # the two first tables is not necessary 550 shift @tables; 551 shift @tables; 552 553 # sometime the previous day appear in today's listing 554 # there could be more than 1 table left 555 shift @tables if (@tables > 1); 556 557 # sometimes there is no programme for a channel 558 # Reality show 24h/24 for example 559 next if (@tables == 0); 560 561 # Here is the good table 562 my $table = shift @tables; 563 #$table->dump(); 564 565 # look for the list of rows of the table 566 my @rows = $table->look_down('_tag', 'tr'); 567 568 # scan each row 569 foreach my $r (@rows) { 570 # the current prog being processed 571 my %prog; 572 573 # look for every column 574 my @td = $r->look_down('_tag', 'td'); 575 576 $prog{'channel'} = $channel.".parabolereunion.com"; 577 578 # scan each cellule of the row 579 foreach my $cell (@td) { 580 my @b = $cell->look_down('_tag', '~text'); 581 foreach my $tag (@b) { 582 #$tag->dump(); 583 my $tt = $tag->attr_get_i('text'); 584 585 if ($tt =~ /(\d+):(\d+)/) { 586 $start = Date_SetTime($start, $1, $2, 0); 587 $start = UnixDate($start, "%Y%m%d%H%M%S"); 588 #$start = utc_offset($start, "+0400"); 589 $prog{'start'} = $start." +0400"; 590 next; 591 } 592 593 if ($tt =~ /^\s*(\d+)h(\d+)/) { 594 my $length = $1 * 60 + $2; 595 # length tag is not necessary 596 # if start and stop tags are presents 597 #$prog{'length'} = $length * 60; 598 599 my $stop = DateCalc($start, "+ $length min"); 600 $stop = UnixDate($stop, "%Y%m%d%H%M%S"); 601 #$stop = utc_offset($stop, "+0400"); 602 $prog{'stop'} = $stop." +0400"; 603 604 # Change the start date because 605 # last programme begins this day (at 606 # 23:00 PM) and ends the day after 607 # (at 01:00 AM) 608 my $y = UnixDate($stop, "%Y"); 609 my $m = UnixDate($stop, "%m"); 610 my $d = UnixDate($stop, "%d"); 611 612 $start = Date_SetDateField($stop, "y", $y); 613 $start = Date_SetDateField($start, "m", $m); 614 $start = Date_SetDateField($start, "d", $d); 615 next; 616 } 617 618 next if ($tt =~ /^\s+/); 619 next if ($tt =~ /\s+$/); 620 $prog{'title'} = [ [ $tt ] ]; 621 } 622 623 # get director/actors if --slow was asked 624 if ($opt_slow) { 625 my $a = $cell->look_down('_tag', 'a'); 626 if (defined $a) { 627 my $href = "http://www.parabolereunion.com/".$a->attr_get_i('href'); 628 get_parabole_programmes_list_slow($href, \%prog); 629 } 630 } 631 632 } 633 634 # add the current prog to the list if it is valid 635 if (defined $prog{'title'}) { 636 #dump_programme(\%prog); 637 push @progs,\%prog; 638 } 639 } 640 } 641 642 return @progs; 643} 644 645### 646### Configure mode 647### 648if ($mode eq 'configure') { 649 XMLTV::Config_file::check_no_overwrite($config_file); 650 651 # ask user to select his broadcasters 652 my @id = sort keys %BROADCASTERS; 653 my @questions = map { "Would you like to download data for '$BROADCASTERS{$_}' ?" } @id; 654 my @responses = ask_many_boolean(1, @questions); 655 656 # retrieve the channels list for each broadcasters 657 foreach (0..$#id) { 658 if ($responses[$_]) { 659 my @ch = get_channels_list($id[$_]); 660 @channels = (@channels, @ch) if @ch; 661 } 662 } 663 664 # ask user to add or not each channel 665 @questions = map { "Add channel $_->{'name'} ?" } @channels; 666 @responses = ask_many_boolean(1, @questions); 667 668 # create configuration file 669 open(CONF, ">$config_file") or die "Cannot write to $config_file: $!"; 670 671 foreach (@channels) { 672 my $r = shift @responses; 673 674 if ($r) { 675 print CONF "channel:"; 676 } 677 else { 678 print CONF "#channel:"; 679 } 680 681 if ( $_->{'type'} eq "CANALSAT" ) 682 { 683 print CONF "$_->{'id'}.canalsat-reunion.com;$_->{'name'}\n"; 684 } 685 else 686 { 687 print CONF "$_->{'id'}.parabolereunion.com;$_->{'name'}\n"; 688 } 689 } 690 691 close CONF or warn "Cannot close $config_file: $!"; 692 say("Finished configuration."); 693 exit(); 694} 695 696### 697### List channels 698### 699if ($mode eq 'list-channels') { 700 # init the XMLTV writer 701 my $writer = new_xmltv_writer(); 702 703 # ask user to select his broadcasters 704 my @id = sort keys %BROADCASTERS; 705 my @questions = map { "Select '$BROADCASTERS{$_}' ?" } @id; 706 my @responses = ask_many_boolean(1, @questions); 707 708 # retrieve the channels list for each broadcasters 709 foreach (0..$#id) { 710 if ($responses[$_]) { 711 my @ch = get_channels_list($id[$_]); 712 @channels = (@channels, @ch) if @ch; 713 } 714 } 715 716 # write the XML header 717 $writer->start({ 718 'generator-info-name' => 'XMLTV', 719 'generator-info-url' => 'http://xmltv.org/', 720 }); 721 722 foreach (@channels) { 723 my $id = "id"; 724 $id = $_->{'id'}.".canalsat-reunion.com" if ($_->{'type'} eq "CANALSAT"); 725 $id = $_->{'id'}.".parabolereunion.com" if ($_->{'type'} eq "PARABOLE"); 726 727 $writer->write_channel({ 728 'id' => $id, 729 'display-name' => [[ $_->{'name'} ]], 730 'icon' => [{ 'src' => $_->{'icon'} }] 731 }); 732 } 733 734 $writer->end(); 735 exit(); 736} 737 738### 739### Grab programmes listing 740### 741die if $mode ne 'grab'; 742 743# Now let's do it 744Date_Init("TZ=UTC"); 745 746# read tv_grab_re conf file... 747@config_lines = XMLTV::Config_file::read_lines($config_file); 748 749# ...and parse its content 750my $n = 0; 751foreach (@config_lines) { 752 ++$n; 753 next if not defined; 754 755 if ( /^channel:(\d+)\.(.*);(.*)/ ) { 756 my %channel; 757 758 $channel{'id'} = $1; 759 $channel{'name'} = $3; 760 761 if ($2 eq 'canalsat-reunion.com') { 762 $channel{'type'} = "CANALSAT"; 763 $channel{'icon'} = "$CANALSAT_ICON_URL/".$channel{'id'}."_grand.gif"; 764 } 765 766 if ($2 eq 'parabolereunion.com') { 767 $channel{'type'} = "PARABOLE"; 768 $channel{'icon'} = "$PARABOLE_ICON_URL/channel_logo_small".$channel{'id'}.".gif"; 769 } 770 771 push @channels,\%channel; 772 } 773 else { 774 die "$config_file:$n - Bad line channel"; 775 } 776} 777 778die "No working channels configured, so no grabing" if not @channels; 779 780# init the XMLTV writer 781my $writer = new_xmltv_writer(); 782 783# write the XML header 784$writer->start({ 785 'generator-info-name' => 'XMLTV', 786 'generator-info-url' => 'http://xmltv.org/', 787}); 788 789# first, write channels 790foreach (@channels) { 791 my $id = "id"; 792 $id = $_->{'id'}.".canalsat-reunion.com" if ($_->{'type'} eq "CANALSAT"); 793 $id = $_->{'id'}.".parabolereunion.com" if ($_->{'type'} eq "PARABOLE"); 794 795 $writer->write_channel({ 796 'id' => $id, 797 'display-name' => [ [ $_->{'name'} ] ], 798 'icon' => [ { 'src' => $_->{'icon'} } ] 799 }); 800} 801 802# then, programmes 803foreach (@channels) { 804 my @progs; 805 806 if ($_->{'type'} eq 'CANALSAT') { 807 @progs = get_canalsat_programmes_list($_->{'id'}, $opt_offset, $opt_days); 808 } 809 810 if ($_->{'type'} eq 'PARABOLE') { 811 @progs = get_parabole_programmes_list($_->{'id'}, $opt_offset, $opt_days); 812 } 813 814 foreach my $prog (@progs) { 815 $writer->write_programme(\%$prog); 816 } 817} 818 819$writer->end(); 820