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