1# OnTV by Stefan'tommie' Tomanek
2
3use strict;
4use vars qw($VERSION %IRSSI);
5$VERSION = "20050226";
6%IRSSI = (
7    authors     => "Stefan 'tommie' Tomanek",
8    contact     => "stefan\@pico.ruhr.de",
9    name        => "OnTV",
10    description => "turns irssi into a tv program guide",
11    license     => "GPLv2",
12    modules     => "Data::Dumper POSIX LWP::Simple HTML::Entities Text::Wrap",
13    changed     => "$VERSION",
14    commands	=> "ontv"
15);
16
17use Irssi 20020324;
18use Data::Dumper;
19use POSIX;
20use LWP::Simple;
21use HTML::Entities;
22use Text::Wrap;
23
24use vars qw($forked @comp);
25
26sub show_help() {
27    my $help=$IRSSI{name}." ".$VERSION."
28/ontv (current)
29    List the current tv program
30/ontv search <query>
31    Query the program guide for a show
32/ontv next
33    Show what'S next on TV
34/ontv tonight
35    List tonight's program
36/ontv watching <station>
37    Display what's on <station>
38";
39    my $text = '';
40    foreach (split(/\n/, $help)) {
41        $_ =~ s/^\/(.*)$/%9\/$1%9/;
42        $text .= $_."\n";
43    }
44    print CLIENTCRAP &draw_box($IRSSI{name}." help", $text, "help", 1) ;
45}
46
47sub draw_box ($$$$) {
48    my ($title, $text, $footer, $colour) = @_;
49    my $box = '';
50    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
51    foreach (split(/\n/, $text)) {
52        $box .= '%R|%n '.$_."\n";
53    }
54    $box .= '%R`--<%n'.$footer.'%R>->%n';
55    $box =~ s/%.//g unless $colour;
56    return $box;
57}
58
59sub get_prog ($) {
60    my ($what) = @_;
61    my $url = 'http://www.tvmovie.de/tv-programm/jetzt.html?nocache=true';
62    $url = 'http://www.tvmovie.de/tv-programm/gleich.html?nocache=true' if ($what == 0);
63    $url = 'http://www.tvmovie.de/tv-programm/2015.html' if ($what == 2);
64    my $data = get($url);
65    my $programs = [];
66    my %program;
67    foreach (split /\n/, $data) {
68	#print $_;
69	if (/class="linkgrau">(.*?)<\/a><\/font><\/td>/) {
70	    $program{station} = $1;
71	    decode_entities($program{station});
72	}
73	#if (/<a href="http:\/\/www.tvmovie.de\/tv-programm\/sendung.html\?SendungID=(\d+)" class="linkblack"><b>(.*?)<\/b><\/a>/) {
74	if (/<a href="http:\/\/www.tvmovie.de\/tv-programm\/sendung.html\?SendungID=(\d+)" class="linkblack"><b>(.*?)<\/b>/) {
75	    $program{id} = $1;
76	    $program{title} = $2;
77	    decode_entities($program{title});
78	}
79	if (/<FONT face="Verdana, Arial, Helvetica, sans-serif" size="1" color="#757575"><br>(.*?)<\/font><\/font><\/td>/) {
80	    $program{comment} = decode_entities($1);
81	}
82	if (/color='#ee0000'>(.*?)<\/font><\/td>/) {
83	    $program{type} = decode_entities($1);
84	}
85	if (/color="white"><b>([A-Z]{2})<\/b><\/font><\/td>/) {
86	    $program{day} = $1;
87	}
88	if (/size="1">(\d{2}\.\d{2})&nbsp;<\/font><\/td>/) {
89	    $program{begin} = $1;
90	    decode_entities($program{begin});
91	}
92	if (/size="1">bis&nbsp;(\d{2}\.\d{2})<\/font><\/td>/) {
93	    $program{end} = $1;
94	    decode_entities($program{end});
95	    my %data = %program;
96	    push @$programs, \%data;
97	    %program = ();
98	}
99    }
100    return $programs;
101}
102
103sub search_prog ($) {
104    my ($query) = @_;
105    encode_entities($query);
106    my $url = 'http://fernsehen.tvmovie.de/finder?finder=swsendung&tag=alle&sw_sendung='.$query;
107    my $data = get($url);
108    return( parse_search($data) );
109}
110
111sub parse_search ($) {
112    my ($data) = @_;
113    my $programs = [];
114    my %program;
115    foreach (split /\n/, $data) {
116	if (/color="white"><b>([A-Z]{2})<\/b> <\/font><\/td>$/) {
117	    $program{day} = $1;
118	    decode_entities($program{day});
119	}
120	if (/size="1">(\d{2}:\d{2})<\/font><\/td>$/) {
121	    $program{begin} = $1;
122	    decode_entities($program{begin});
123	} elsif (/class="linkgrau">(.*?)<\/a><\/font><\/td>$/) {
124	    $program{station} = $1;
125	    decode_entities($program{station});
126	} elsif (/<a href="http:\/\/www.tvmovie.de\/tv-programm\/sendung\.html\?SendungID=(\d+)" class="linkblack"><b>(.*?)<\/b><\/a><\/font>(?:<FONT face="Verdana, Arial, Helvetica, sans-serif" size="1" color="#757575"><br>(.*?)<\/font>)?/) {
127	    $program{id} = $1;
128	    $program{title} = $2;
129	    $program{comment} = $3;
130	    decode_entities($program{title});
131	    decode_entities($program{comment});
132	#} elsif (/{ \t]*<td valign="top" align="left">$/) {
133	    my %data = %program;
134	    push @$programs, \%data;
135	}
136    }
137    return $programs;
138}
139
140sub get_info ($) {
141    my ($id) = @_;
142    my $data = get('http://www.tvmovie.de/tv-programm/sendung.html?SendungID='.$id);
143    my %info;
144    foreach (split(/\n/, $data)) {
145	#print;
146	if (/size="3"><b>(.*?)<\/b><br><\/font>$/) {
147	    $info{title} = decode_entities($1);
148	} elsif (/color="#FFFFFF"><b>&nbsp;(\d+\.\d+\.\d+) \|/) {
149	    $info{date} = decode_entities($1);
150	} elsif (/size="1"><b>(.*?)<\/b><br><br><\/font>$/) {
151	    $info{comment} = decode_entities($1);
152	} elsif (/class="uppercase"><b>(.*?)<\/b> <\/font>/) {
153	    $info{type} = decode_entities($1);
154	} elsif (/<FONT face="Verdana, Arial, Helvetica, sans-serif" size="1">(.*?)<br><br><\/font>/) {
155	    $info{desc} = decode_entities($1);
156	} elsif (/\[Sender:&nbsp;(.*?)\] \[Beginn:&nbsp;(.*?)\] \[Dauer:&nbsp;(.*?) Min\.\] \[Ende:&nbsp;(.*?)\] \[SV:&nbsp;(.*?)\]/) {
157	    $info{station} = decode_entities($1);
158	    $info{begin} = decode_entities($2);
159	    $info{end} = decode_entities($4);
160	    $info{showview} = decode_entities($5);
161	}
162    }
163    my $stat = $info{station};
164    $info{desc} =~ s/$stat$//;
165    #$info{desc} =~ s/<br><br>$//;
166    $info{desc} =~ s/<br>/\n/g;
167    return \%info;
168}
169
170sub bg_fetch ($$) {
171    my ($op, $query) = @_;
172    my ($rh, $wh);
173    pipe($rh, $wh);
174    if ($forked) {
175        print CLIENTCRAP "%R>>%n Please wait until your earlier request has been finished.";
176        return;
177    }
178    my $pid = fork();
179    $forked = 1;
180    if ($pid > 0) {
181	print CLIENTCRAP "%R>>%n Please wait...";
182	close $wh;
183	Irssi::pidwait_add($pid);
184	my $pipetag;
185	my @args = ($rh, \$pipetag, $op, $query);
186	$pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args);
187    } else {
188	my $result = {};
189	my @program;
190	my $stations = Irssi::settings_get_str('ontv_stations');
191	eval {
192	    if ($op eq 'current') {
193		@program = @{ get_prog(1) };
194		foreach (@program) {
195		    push @{ $result->{program} }, $_ if ($_->{station} =~ /^($stations)$/);
196		}
197	    } elsif ($op eq 'next') {
198		@program = @{ get_prog(0) };
199		foreach (@program) {
200		    push @{ $result->{program} }, $_ if ($_->{station} =~ /^($stations)$/);
201		}
202	    } elsif ($op eq 'tonight') {
203		@program = @{ get_prog(2) };
204		foreach (@program) {
205                    push @{ $result->{program} }, $_ if ($_->{station} =~ /^($stations)$/);
206                }
207	    } elsif ($op eq 'search') {
208		@program = @{ search_prog($query) };
209		foreach (@program) {
210		    push @{ $result->{program} }, $_ if ($_->{station} =~ /^($stations)$/);
211		}
212	    } elsif ($op eq 'watching') {
213		@program = @{ get_prog(1) };
214		foreach (@program) {
215		    next unless ($_->{station} =~ /^($query)$/);
216		    push @{ $result->{program} }, $_;
217		    print $_->{id};
218		    $result->{info} = get_info($_->{id});
219		}
220	    } elsif ($op eq 'info') {
221		$result->{info} = get_info($query);
222	    }
223	    my $dumper = Data::Dumper->new([$result]);
224	    $dumper->Purity(1)->Deepcopy(1)->Indent(0);
225	    print($wh $dumper->Dump);
226	};
227	close $wh;
228	POSIX::_exit(1);
229    }
230}
231
232sub pipe_input ($) {
233    my ($rh, $pipetag, $op, $args) = @{$_[0]};
234    $forked = 0;
235    Irssi::input_remove($$pipetag);
236    my $text;
237    $text .= $_ foreach <$rh>;
238    no strict 'vars';
239    my $incoming = eval("$text");
240    return unless ($incoming->{program} || $incoming->{info});
241    print_prog($incoming->{program}, 'current') if ($op eq 'current');
242    print_prog($incoming->{program}, 'next') if ($op eq 'next');
243    print_prog($incoming->{program}, 'tonight') if ($op eq 'tonight');
244    print_prog($incoming->{program}, 'query: "'.$args.'"') if ($op eq 'search');
245    print_prog($incoming->{program}, 'current: "'.$args.'"') if ($op eq 'watching');
246    print_info($incoming->{info}) if $incoming->{info};
247}
248
249sub print_info ($) {
250    my ($info) = @_;
251    my $text;
252    $text .= '%9'.$info->{title}.'%9'."\n";
253    $text .= $info->{date}.': '.$info->{begin}."-".$info->{end}."\n";
254    $text .= 'Showview: '.$info->{showview}."\n\n";
255    $text .= $info->{comment}."\n\n";
256    $text .= $info->{desc};
257    my $col = int( Irssi::active_win()->{width}*(2/3) );
258    $Text::Wrap::columns = $col;
259    my $article = wrap("", "", $text);
260    print CLIENTCRAP &draw_box('OnTV', $article, $info->{title}, 1);
261}
262
263sub print_prog ($$) {
264    my ($program, $query) = @_;
265    @comp = @$program;
266    my $text;
267    foreach (@$program) {
268	$text .= "%9".$_->{station}."%9:";
269	$text .= " %U".$_->{title}."%U";
270	$text .= " [".$_->{type}."]"if $_->{type};
271	$text .= " (".$_->{id}.")\n";
272	$text .= " >".$_->{comment}."<\n" if $_->{comment};
273	$text .= "  time: ";
274	$text .= $_->{day}.", ";
275	$text .= $_->{begin};
276	$text .= "-".$_->{end} if $_->{end};
277	$text .= "\n";
278	#$text .= "\n";
279    }
280    print CLIENTCRAP &draw_box('OnTV', $text, $query, 1);
281}
282
283sub sig_complete_word ($$$$$) {
284    my ($list, $window, $word, $linestart, $want_space) = @_;
285    return unless $linestart =~ /^.ontv (info)/;
286    foreach (@comp) {
287        push @$list, $_->{id} if ($_->{id} =~ /^(\Q$word\E.*)?$/);
288        push @$list, $_->{station} if ($_->{station} =~ /^(\Q$word\E.*)?$/);
289        push @$list, $_->{title} if ($_->{title} =~ /^(\Q$word\E.*)?$/);
290    }
291    Irssi::signal_stop();
292}
293
294
295sub cmd_ontv ($$$) {
296    my ($args, $server, $witem) = @_;
297    my @arg = split(/ /, $args);
298    if (scalar(@arg) == 0 || $arg[0] eq 'current') {
299	bg_fetch('current', '');
300    } elsif ($arg[0] eq 'next') {
301	bg_fetch('next', '');
302    } elsif ($arg[0] eq 'tonight') {
303	bg_fetch('tonight', '');
304    } elsif ($arg[0] eq 'search') {
305	shift @arg;
306	bg_fetch('search', join(' ', @arg))
307    } elsif ($arg[0] eq 'watching' && defined $arg[1]) {
308	shift @arg;
309	bg_fetch('watching', join(' ', @arg));
310    } elsif ($arg[0] eq 'info' && defined $arg[1]) {
311	shift @arg;
312	my $query = join(' ', @arg);
313	unless ($query =~ /^\d+$/) {
314	    foreach (@comp) {
315		if ($_->{title} eq $query || $_->{station} eq $query) {
316		    $query = $_->{id};
317		    last;
318		}
319	    }
320	}
321	bg_fetch('info', $query);
322    } elsif ($arg[0] eq 'help') {
323	show_help();
324    }
325}
326
327Irssi::settings_add_str($IRSSI{name}, 'ontv_stations', '.*' );
328
329Irssi::command_bind('ontv' => \&cmd_ontv);
330
331Irssi::signal_add_first('complete word', \&sig_complete_word);
332
333foreach my $cmd ('search', 'current', 'next', 'tonight', 'watching', 'help', 'info') {
334    Irssi::command_bind('ontv '.$cmd =>
335         sub { cmd_ontv("$cmd ".$_[0], $_[1], $_[2]); } );
336}
337
338
339print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /ontv help for help';
340