#!/usr/bin/perl # getskyepg.pl: Get EPG data from Sky's web pages # # Connects to a running VDR instance via SVDRP, gets the channel data # for the Sky channels and connects to Sky's web pages to extract the # EPG data for these channels. The result is sent to VDR via SVDRP. # # See the README file for copyright information and how to reach the author. # # $Id: getskyepg.pl 1.2 2003/04/02 16:21:47 kls Exp $ use Getopt::Std; use Time::Local; $Usage = qq{ Usage: $0 [options] Options: -d hostname destination hostname (default: localhost) -p port SVDRP port number (default: 2001) -S source channel source (default: S28.2E) -D days days to get EPG for (1..7, default: 2) }; die $Usage if (!getopts("d:D:hp:S:") || $opt_h); $Dest = $opt_d || "localhost"; $Port = $opt_p || 2001; $Source = $opt_S || "S28.2E"; $Days = $opt_D || 2; $SkyWebPage = "www.ananova.com/tv/frontpage.html"; $WGET = "/usr/bin/wget -q -O-"; $LOGGER = "/usr/bin/logger -t SKYEPG"; $DST = -3600; ##XXX TODO find out whether DST is active! $SecsInDay = 86400; $MaxFrequency = 1000; $idxName = 0; $idxFrequency = 1; $idxSource = 3; $idxSid = 9; Error("days out of range: $Days") unless (1 <= $Days && $Days <= 7); sub Log { system("$LOGGER '@_'"); } sub Error { Log(@_); die "$0: @_\n"; } sub GetChannels { SVDRPsend("LSTC"); my @channels = (); for (SVDRPreceive(250)) { my @a = split(':', substr($_, 4)); if ($a[$idxSource] eq $Source && $a[$idxFrequency] < $MaxFrequency) { push(@channels, [@a]); } } return @channels; } sub GetPage { my $channel = shift; my $day = shift; my $url = "$SkyWebPage?c=$channel&day=day$day"; Log("reading $url"); my @page = split("\n", `$WGET '$url'`); Log("received " . ($#page + 1) . " lines"); return @page; } # In order to get the duration we need to buffer the last event: $Id = ""; $Time = 0; $Title = ""; $Episode = ""; $Descr = ""; sub GetEpgData { my ($channel, $channelID) = @_; my $numEvents = 0; SVDRPsend("C $channelID"); $Time = 0; for $day (1 .. $Days) { my $dt = 0; my $ap = ""; my @page = GetPage($channel, $day); for $line (@page) { if ($line =~ /^<\/tr>(.*?)<\/b>.*?(.*?)<\/b> *(.*?<\/i>)? *(.*?) *<\/small>/); my ($h, $m, $a) = ($time =~ /([0-9]+)\.([0-9]+)(.)m/); # handle am/pm: $dt = $SecsInDay if ($ap eq "p" && $a eq "a"); $ap = $a; $h += 12 if ($a eq "p" && $h < 12); $h -= 12 if ($a eq "a" && $h == 12); # convert to time_t: my @gmt = gmtime; $gmt[0] = 0; # seconds $gmt[1] = $m; # minutes $gmt[2] = $h; # hours $time = timegm(@gmt) + ($day - 1) * $SecsInDay + $dt + $DST; # create EPG data: if ($Time) { $duration = $time - $Time; SVDRPsend("E $Id $Time $duration"); SVDRPsend("T $Title"); SVDRPsend("S $Episode"); SVDRPsend("D $Descr"); SVDRPsend("e"); $numEvents++; } # buffer the last event: $Id = $time / 60 % 0xFFFF; # this gives us unique ids for every minute of over 6 weeks $Time = $time; ($Title = $title) =~ s/<[^>]+>//g; ($Episode = $episode) =~ s/<[^>]+>//g; ($Descr = $descr) =~ s/<[^>]+>//g; } } } SVDRPsend("c"); Log("generated $numEvents EPG events"); } sub ProcessEpg { Log("getting Sky channel definitions"); my @channels = GetChannels(); Error("no Sky channels found") unless @channels; Log("found " . ($#channels + 1) . " channels"); for (@channels) { my $channel = @$_[$idxSid]; my $channelID = "@$_[$idxSource]-0-@$_[$idxFrequency]-$channel"; Log("processing channel @$_[0]"); SVDRPsend("PUTE"); SVDRPreceive(354); GetEpgData($channel, $channelID); SVDRPsend("."); SVDRPreceive(250); } Log("done"); } #--------------------------------------------------------------------------- # TODO: make this a Perl module??? What about Error()??? use Socket; $Timeout = 300; # max. seconds to wait for response $SIG{ALRM} = sub { Error("timeout"); }; alarm($Timeout); $iaddr = inet_aton($Dest) || Error("no host: $Dest"); $paddr = sockaddr_in($Port, $iaddr); $proto = getprotobyname('tcp'); socket(SOCK, PF_INET, SOCK_STREAM, $proto) || Error("socket: $!"); connect(SOCK, $paddr) || Error("connect: $!"); select(SOCK); $| = 1; SVDRPreceive(220); ProcessEpg(); SVDRPsend("QUIT"); sub SVDRPsend { my $s = shift; print SOCK "$s\r\n"; } sub SVDRPreceive { my $expect = shift | 0; my @a = (); while () { s/\s*$//; # 'chomp' wouldn't work with "\r\n" push(@a, $_); if (substr($_, 3, 1) ne "-") { my $code = substr($_, 0, 3); Error("expected SVDRP code $expect, but received $code") if ($code != $expect); last; } } return @a; } #---------------------------------------------------------------------------