Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env perl
- # zap2xml (c) <zap2xml@gmail.com> - for personal use only!
- # not for redistribution of any kind, or conversion to other languages,
- # not GPL. not for github, thank you.
- BEGIN { $SIG{__DIE__} = sub {
- return if $^S;
- my $msg = join(" ", @_);
- print STDERR "$msg";
- if ($msg =~ /can't locate/i) {
- print "\nSee homepage for tips on installing missing modules (example: \"perl -MCPAN -e shell\")\n";
- if ($^O eq 'MSWin32') {
- print "Use \"ppm install\" on windows\n";
- }
- }
- if ($^O eq 'MSWin32') {
- if ($msg =~ /uri.pm/i && $msg =~ /temp/i) {
- print "\nIf your scanner deleted the perl URI.pm file see the homepage for tips\n";
- if ($msg =~ /(\ .\:.+?par-.+?\\)/) {
- print "(Delete the $1 folder and retry)\n";
- }
- }
- sleep(5);
- }
- exit 1;
- }}
- use Compress::Zlib;
- use Encode;
- use File::Basename;
- use File::Copy;
- use Getopt::Std;
- use HTTP::Cookies;
- use URI;
- use URI::Escape;
- use LWP::UserAgent;
- use POSIX;
- use Time::Local;
- use Date::Parse;
- use JSON;
- no warnings 'utf8';
- STDOUT->autoflush(1);
- STDERR->autoflush(1);
- %options=();
- getopts("?aA:bB:c:C:d:DeE:Fgi:IjJ:l:Lm:Mn:N:o:Op:P:qRr:s:S:t:Tu:UwWxY:zZ:",\%options);
- $homeDir = $ENV{HOME};
- $homeDir = $ENV{USERPROFILE} if !defined($homeDir);
- $homeDir = '.' if !defined($homeDir);
- $confFile = $homeDir . '/.zap2xmlrc';
- # Defaults
- $start = 0;
- $days = 7;
- $ncdays = 0;
- $ncsdays = 0;
- $ncmday = -1;
- $retries = 3;
- $outFile = 'xmltv.xml';
- $outFile = 'xtvd.xml' if defined $options{x};
- $cacheDir = 'cache';
- $lang = 'en';
- $userEmail = '';
- $password = '';
- $proxy;
- $postalcode;
- $country;
- $lineupId;
- $device;
- $sleeptime = 0;
- $allChan = 0;
- $shiftMinutes = 0;
- $outputXTVD = 0;
- $lineuptype;
- $lineupname;
- $lineuplocation;
- $zapToken;
- %zapFavorites=();
- %sidCache=();
- $sTBA = "\\bTBA\\b|To Be Announced";
- %tvgfavs=();
- &HELP_MESSAGE() if defined $options{'?'};
- $confFile = $options{C} if defined $options{C};
- # read config file
- if (open (CONF, $confFile))
- {
- &pout("Reading config file: $confFile\n");
- while (<CONF>)
- {
- s/#.*//; # comments
- if (/^\s*$/i) { }
- elsif (/^\s*start\s*=\s*(\d+)/i) { $start = $1; }
- elsif (/^\s*days\s*=\s*(\d+)/i) { $days = $1; }
- elsif (/^\s*ncdays\s*=\s*(\d+)/i) { $ncdays = $1; }
- elsif (/^\s*ncsdays\s*=\s*(\d+)/i) { $ncsdays = $1; }
- elsif (/^\s*ncmday\s*=\s*(\d+)/i) { $ncmday = $1; }
- elsif (/^\s*retries\s*=\s*(\d+)/i) { $retries = $1; }
- elsif (/^\s*user[\w\s]*=\s*(.+)/i) { $userEmail = &rtrim($1); }
- elsif (/^\s*pass[\w\s]*=\s*(.+)/i) { $password = &rtrim($1); }
- elsif (/^\s*cache\s*=\s*(.+)/i) { $cacheDir = &rtrim($1); }
- elsif (/^\s*icon\s*=\s*(.+)/i) { $iconDir = &rtrim($1); }
- elsif (/^\s*trailer\s*=\s*(.+)/i) { $trailerDir = &rtrim($1); }
- elsif (/^\s*lang\s*=\s*(.+)/i) { $lang = &rtrim($1); }
- elsif (/^\s*outfile\s*=\s*(.+)/i) { $outFile = &rtrim($1); }
- elsif (/^\s*proxy\s*=\s*(.+)/i) { $proxy = &rtrim($1); }
- elsif (/^\s*outformat\s*=\s*(.+)/i) { $outputXTVD = 1 if $1 =~ /xtvd/i; }
- elsif (/^\s*lineupid\s*=\s*(.+)/i) { $lineupId = &rtrim($1); }
- elsif (/^\s*lineupname\s*=\s*(.+)/i) { $lineupname = &rtrim($1); }
- elsif (/^\s*lineuptype\s*=\s*(.+)/i) { $lineuptype = &rtrim($1); }
- elsif (/^\s*lineuplocation\s*=\s*(.+)/i) { $lineuplocation = &rtrim($1); }
- elsif (/^\s*postalcode\s*=\s*(.+)/i) { $postalcode = &rtrim($1); }
- else
- {
- die "Oddline in config file \"$confFile\".\n\t$_";
- }
- }
- close (CONF);
- }
- &HELP_MESSAGE() if !(%options) && $userEmail eq '';
- $cacheDir = $options{c} if defined $options{c};
- $days = $options{d} if defined $options{d};
- $ncdays = $options{n} if defined $options{n};
- $ncsdays = $options{N} if defined $options{N};
- $ncmday = $options{B} if defined $options{B};
- $start = $options{s} if defined $options{s};
- $retries = $options{r} if defined $options{r};
- $iconDir = $options{i} if defined $options{i};
- $trailerDir = $options{t} if defined $options{t};
- $lang = $options{l} if defined $options{l};
- $outFile = $options{o} if defined $options{o};
- $password = $options{p} if defined $options{p};
- $userEmail = $options{u} if defined $options{u};
- $proxy = $options{P} if defined $options{P};
- $zlineupId = $options{Y} if defined $options{Y};
- $zipcode = $options{Z} if defined $options{Z};
- $includeXMLTV = $options{J} if defined $options{J} && -e $options{J};
- $outputXTVD = 1 if defined $options{x};
- $allChan = 1 if defined($options{a});
- $allChan = 1 if defined($zipcode) && defined($zlineupId);
- $sleeptime = $options{S} if defined $options{S};
- $shiftMinutes = $options{m} if defined $options{m};
- $ncdays = $days - $ncdays; # make relative to the end
- $urlRoot = 'https://tvlistings.zap2it.com/';
- $urlAssets = 'https://zap2it.tmsimg.com/assets/';
- $tvgurlRoot = 'http://mobilelistings.tvguide.com/';
- $tvgMapiRoot = 'http://mapi.tvguide.com/';
- $tvgurl = 'http://www.tvguide.com/';
- $tvgspritesurl = 'http://static.tvgcdn.net/sprites/';
- $retries = 20 if $retries > 20; # Too many
- my %programs = ();
- my $cp;
- my %stations = ();
- my $cs;
- my $rcs;
- my %schedule = ();
- my $sch;
- my $coNum = 0;
- my $tb = 0;
- my $treq = 0;
- my $expired = 0;
- my $ua;
- my $tba = 0;
- my $exp = 0;
- my @fh = ();
- my $XTVD_startTime;
- my $XTVD_endTime;
- if (! -d $cacheDir) {
- mkdir($cacheDir) or die "Can't mkdir: $!\n";
- } else {
- opendir (DIR, "$cacheDir/");
- @cacheFiles = grep(/\.html|\.js/,readdir(DIR));
- closedir (DIR);
- foreach $cacheFile (@cacheFiles) {
- $fn = "$cacheDir/$cacheFile";
- $atime = (stat($fn))[8];
- if ($atime + ( ($days + 2) * 86400) < time) {
- &pout("Deleting old cached file: $fn\n");
- &unf($fn);
- }
- }
- }
- my $s1 = time();
- if (defined($options{z})) {
- &login() if !defined($options{a}); # get favorites
- &parseTVGIcons() if defined($iconDir);
- $gridHours = 3;
- $maxCount = $days * (24 / $gridHours);
- $offset = $start * 3600 * 24 * 1000;
- $ms = &hourToMillis() + $offset;
- for ($count=0; $count < $maxCount; $count++) {
- $curday = int($count / (24/$gridHours)) + 1;
- if ($count == 0) {
- $XTVD_startTime = $ms;
- } elsif ($count == $maxCount - 1) {
- $XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
- }
- $fn = "$cacheDir/$ms\.js\.gz";
- if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
- &login() if !defined($zlineupId);
- my $duration = $gridHours * 60;
- my $tvgstart = substr($ms, 0, -3);
- $rc = Encode::encode('utf8', &getURL($tvgurlRoot . "Listingsweb/ws/rest/schedules/$zlineupId/start/$tvgstart/duration/$duration"));
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
- &pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
- &parseTVGGrid($fn);
- if (defined($options{T}) && $tba) {
- &pout("Deleting: $fn (contains \"$sTBA\")\n");
- &unf($fn);
- }
- if ($exp) {
- &pout("Deleting: $fn (expired)\n");
- &unf($fn);
- }
- $exp = 0;
- $tba = 0;
- $ms += ($gridHours * 3600 * 1000);
- }
- } else {
- &login() if !defined($options{a}); # get favorites
- $gridHours = 3;
- $maxCount = $days * (24 / $gridHours);
- $offset = $start * 3600 * 24 * 1000;
- $ms = &hourToMillis() + $offset;
- for ($count=0; $count < $maxCount; $count++) {
- $curday = int($count / (24/$gridHours)) + 1;
- if ($count == 0) {
- $XTVD_startTime = $ms;
- } elsif ($count == $maxCount - 1) {
- $XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
- }
- $fn = "$cacheDir/$ms\.js\.gz";
- if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
- my $zstart = substr($ms, 0, -3);
- $params = "?time=$zstart×pan=$gridHours&";
- $params .= &getZapGParams();
- $params .= '&TMSID=&AffiliateID=gapzap&FromPage=TV%20Grid';
- $params .= '&ActivityID=1&OVDID=&isOverride=true&pref=-';
- $rs = &getURL($urlRoot . "api/grid$params",'X-Requested-With' => 'XMLHttpRequest');
- last if ($rs eq '');
- $rc = Encode::encode('utf8', $rs);
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
- &pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
- &parseJSON($fn);
- if (defined($options{T}) && $tba) {
- &pout("Deleting: $fn (contains \"$sTBA\")\n");
- &unf($fn);
- }
- if ($exp) {
- &pout("Deleting: $fn (expired)\n");
- &unf($fn);
- }
- $exp = 0;
- $tba = 0;
- $ms += ($gridHours * 3600 * 1000);
- }
- }
- my $s2 = time();
- &pout("Downloaded $tb bytes in $treq http requests.\n") if $tb > 0;
- &pout("Expired programs: $expired\n") if $expired > 0;
- &pout("Writing XML file: $outFile\n");
- open($FH, ">$outFile");
- my $enc = 'ISO-8859-1';
- if (defined($options{U})) {
- $enc = 'UTF-8';
- }
- if ($outputXTVD) {
- &printHeaderXTVD($FH, $enc);
- &printStationsXTVD($FH);
- &printLineupsXTVD($FH);
- &printSchedulesXTVD($FH);
- &printProgramsXTVD($FH);
- &printGenresXTVD($FH);
- &printFooterXTVD($FH);
- } else {
- &printHeader($FH, $enc);
- &printChannels($FH);
- if (defined($includeXMLTV)) {
- &pout("Reading XML file: $includeXMLTV\n");
- &incXML("<channel","<programme", $FH);
- }
- &printProgrammes($FH);
- &incXML("<programme","</tv", $FH) if defined($includeXMLTV);
- &printFooter($FH);
- }
- close($FH);
- my $ts = 0;
- for my $station (keys %stations ) {
- $ts += scalar (keys %{$schedule{$station}})
- }
- my $s3 = time();
- &pout("Completed in " . ( $s3 - $s1 ) . "s (Parse: " . ( $s2 - $s1 ) . "s) " . keys(%stations) . " stations, " . keys(%programs) . " programs, $ts scheduled.\n");
- if (defined($options{w})) {
- print "Press ENTER to exit:";
- <STDIN>;
- } else {
- sleep(3) if ($^O eq 'MSWin32');
- }
- exit 0;
- sub incXML {
- my ($st, $en, $FH) = @_;
- open($XF, "<$includeXMLTV");
- while (<$XF>) {
- if (/^\s*$st/../^\s*$en/) {
- print $FH $_ unless /^\s*$en/
- }
- }
- close($XF);
- }
- sub pout {
- print @_ if !defined $options{q};
- }
- sub perr {
- warn @_;
- }
- sub rtrim {
- my $s = shift;
- $s =~ s/\s+$//;
- return $s;
- }
- sub trim {
- my $s = shift;
- $s =~ s/^\s+//;
- $s =~ s/\s+$//;
- return $s;
- }
- sub trim2 {
- my $s = &trim(shift);
- $s =~ s/[^\w\s\(\)\,]//gsi;
- $s =~ s/\s+/ /gsi;
- return $s;
- }
- sub _rtrim3 {
- my $s = shift;
- return substr($s, 0, length($s)-3);
- }
- sub convTime {
- my $t = shift;
- $t += $shiftMinutes * 60 * 1000;
- return strftime "%Y%m%d%H%M%S", localtime(&_rtrim3($t));
- }
- sub convTimeXTVD {
- my $t = shift;
- $t += $shiftMinutes * 60 * 1000;
- return strftime "%Y-%m-%dT%H:%M:%SZ", gmtime(&_rtrim3($t));
- }
- sub convOAD {
- return strftime "%Y%m%d", gmtime(&_rtrim3(shift));
- }
- sub convOADXTVD {
- return strftime "%Y-%m-%d", gmtime(&_rtrim3(shift));
- }
- sub convDurationXTVD {
- my $duration = shift;
- my $hour = int($duration / 3600000);
- my $minutes = int(($duration - ($hour * 3600000)) / 60000);
- return sprintf("PT%02dH%02dM", $hour, $minutes);
- }
- sub appendAsterisk {
- my ($title, $station, $s) = @_;
- if (defined($options{A})) {
- if (($options{A} =~ "new" && defined($schedule{$station}{$s}{new}))
- || ($options{A} =~ "live" && defined($schedule{$station}{$s}{live}))) {
- $title .= " *";
- }
- }
- return $title;
- }
- sub stationToChannel {
- my $s = shift;
- if (defined($options{z})) {
- return sprintf("I%s.%s.tvguide.com", $stations{$s}{number},$stations{$s}{stnNum});
- } elsif (defined($options{O})) {
- return sprintf("C%s%s.zap2it.com",$stations{$s}{number},lc($stations{$s}{name}));
- }
- return sprintf("I%s.labs.zap2it.com", $stations{$s}{stnNum});
- }
- sub sortChan {
- if (defined($stations{$a}{order}) && defined($stations{$b}{order})) {
- return $stations{$a}{order} <=> $stations{$b}{order};
- } else {
- return $stations{$a}{name} cmp $stations{$b}{name};
- }
- }
- sub enc {
- my $t = shift;
- if (!defined($options{U})) {$t = Encode::decode('utf8', $t);}
- if (!defined($options{E}) || $options{E} =~ /amp/) {$t =~ s/&/&/gs;}
- if (!defined($options{E}) || $options{E} =~ /quot/) {$t =~ s/"/"/gs;}
- if (!defined($options{E}) || $options{E} =~ /apos/) {$t =~ s/'/'/gs;}
- if (!defined($options{E}) || $options{E} =~ /lt/) {$t =~ s/</</gs;}
- if (!defined($options{E}) || $options{E} =~ /gt/) {$t =~ s/>/>/gs;}
- if (defined($options{e})) {
- $t =~ s/([^\x20-\x7F])/'&#' . ord($1) . ';'/gse;
- }
- return $t;
- }
- sub printHeader {
- my ($FH, $enc) = @_;
- print $FH "<?xml version=\"1.0\" encoding=\"$enc\"?>\n";
- print $FH "<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n\n";
- if (defined($options{z})) {
- print $FH "<tv source-info-url=\"http://tvguide.com/\" source-info-name=\"tvguide.com\"";
- } else {
- print $FH "<tv source-info-url=\"http://tvschedule.zap2it.com/\" source-info-name=\"zap2it.com\"";
- }
- print $FH " generator-info-name=\"zap2xml\" generator-info-url=\"zap2xml\@gmail.com\">\n";
- }
- sub printFooter {
- my $FH = shift;
- print $FH "</tv>\n";
- }
- sub printChannels {
- my $FH = shift;
- for my $key ( sort sortChan keys %stations ) {
- $sname = &enc($stations{$key}{name});
- $fname = &enc($stations{$key}{fullname});
- $snum = $stations{$key}{number};
- print $FH "\t<channel id=\"" . &stationToChannel($key) . "\">\n";
- print $FH "\t\t<display-name>" . $sname . "</display-name>\n" if defined($options{F}) && defined($sname);
- if (defined($snum)) {
- ©Logo($key);
- print $FH "\t\t<display-name>" . $snum . " " . $sname . "</display-name>\n";
- print $FH "\t\t<display-name>" . $snum . "</display-name>\n";
- }
- print $FH "\t\t<display-name>" . $sname . "</display-name>\n" if !defined($options{F}) && defined($sname);
- print $FH "\t\t<display-name>" . $fname . "</display-name>\n" if (defined($fname));
- if (defined($stations{$key}{logoURL})) {
- print $FH "\t\t<icon src=\"" . $stations{$key}{logoURL} . "\" />\n";
- }
- print $FH "\t</channel>\n";
- }
- }
- sub printProgrammes {
- my $FH = shift;
- for my $station ( sort sortChan keys %stations ) {
- my $i = 0;
- my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
- foreach $s (@keyArray) {
- if ($#keyArray <= $i && !defined($schedule{$station}{$s}{endtime})) {
- delete $schedule{$station}{$s};
- next;
- }
- my $p = $schedule{$station}{$s}{program};
- my $startTime = &convTime($schedule{$station}{$s}{time});
- my $startTZ = &timezone($schedule{$station}{$s}{time});
- my $endTime;
- if (defined($schedule{$station}{$s}{endtime})) {
- $endTime = $schedule{$station}{$s}{endtime};
- } else {
- $endTime = $schedule{$station}{$keyArray[$i+1]}{time};
- }
- my $stopTime = &convTime($endTime);
- my $stopTZ = &timezone($endTime);
- print $FH "\t<programme start=\"$startTime $startTZ\" stop=\"$stopTime $stopTZ\" channel=\"" . &stationToChannel($schedule{$station}{$s}{station}) . "\">\n";
- if (defined($programs{$p}{title})) {
- my $title = &enc($programs{$p}{title});
- $title = &appendAsterisk($title, $station, $s);
- print $FH "\t\t<title lang=\"$lang\">" . $title . "</title>\n";
- }
- if (defined($programs{$p}{episode}) || (defined($options{M}) && defined($programs{$p}{movie_year}))) {
- print $FH "\t\t<sub-title lang=\"$lang\">";
- if (defined($programs{$p}{episode})) {
- print $FH &enc($programs{$p}{episode});
- } else {
- print $FH "Movie (" . $programs{$p}{movie_year} . ")";
- }
- print $FH "</sub-title>\n"
- }
- print $FH "\t\t<desc lang=\"$lang\">" . &enc($programs{$p}{description}) . "</desc>\n" if defined($programs{$p}{description});
- if (defined($programs{$p}{actor})
- || defined($programs{$p}{director})
- || defined($programs{$p}{writer})
- || defined($programs{$p}{producer})
- || defined($programs{$p}{preseter})
- ) {
- print $FH "\t\t<credits>\n";
- &printCredits($FH, $p, "director");
- foreach my $g (sort { $programs{$p}{actor}{$a} <=> $programs{$p}{actor}{$b} } keys %{$programs{$p}{actor}} ) {
- print $FH "\t\t\t<actor";
- print $FH " role=\"" . &enc($programs{$p}{role}{$g}) . "\"" if (defined($programs{$p}{role}{$g}));
- print $FH ">" . &enc($g) . "</actor>\n";
- }
- &printCredits($FH, $p, "writer");
- &printCredits($FH, $p, "producer");
- &printCredits($FH, $p, "presenter");
- print $FH "\t\t</credits>\n";
- }
- my $date;
- if (defined($programs{$p}{movie_year})) {
- $date = $programs{$p}{movie_year};
- } elsif (defined($programs{$p}{originalAirDate}) && $p =~ /^EP|^\d/) {
- $date = &convOAD($programs{$p}{originalAirDate});
- }
- print $FH "\t\t<date>$date</date>\n" if defined($date);
- if (defined($programs{$p}{genres})) {
- foreach my $g (sort { $programs{$p}{genres}{$a} <=> $programs{$p}{genres}{$b} } keys %{$programs{$p}{genres}} ) {
- print $FH "\t\t<category lang=\"$lang\">" . &enc(ucfirst($g)) . "</category>\n";
- }
- }
- print $FH "\t\t<length units=\"minutes\">" . $programs{$p}{duration} . "</length>\n" if defined($programs{$p}{duration});
- if (defined($programs{$p}{imageUrl})) {
- print $FH "\t\t<icon src=\"" . &enc($programs{$p}{imageUrl}) . "\" />\n";
- }
- if (defined($programs{$p}{url})) {
- print $FH "\t\t<url>" . &enc($programs{$p}{url}) . "</url>\n";
- }
- my $xs;
- my $xe;
- if (defined($programs{$p}{seasonNum}) && defined($programs{$p}{episodeNum})) {
- my $s = $programs{$p}{seasonNum};
- my $sf = sprintf("S%0*d", &max(2, length($s)), $s);
- my $e = $programs{$p}{episodeNum};
- my $ef = sprintf("E%0*d", &max(2, length($e)), $e);
- $xs = int($s) - 1;
- $xe = int($e) - 1;
- if ($s > 0 || $e > 0) {
- print $FH "\t\t<episode-num system=\"common\">" . $sf . $ef . "</episode-num>\n";
- }
- }
- $dd_prog_id = $p;
- if ( $dd_prog_id =~ /^(..\d{8})(\d{4})/ ) {
- $dd_prog_id = sprintf("%s.%s",$1,$2);
- print $FH "\t\t<episode-num system=\"dd_progid\">" . $dd_prog_id . "</episode-num>\n";
- }
- if (defined($xs) && defined($xe) && $xs >= 0 && $xe >= 0) {
- print $FH "\t\t<episode-num system=\"xmltv_ns\">" . $xs . "." . $xe . ".</episode-num>\n";
- }
- if (defined($schedule{$station}{$s}{quality})) {
- print $FH "\t\t<video>\n";
- print $FH "\t\t\t<aspect>16:9</aspect>\n";
- print $FH "\t\t\t<quality>HDTV</quality>\n";
- print $FH "\t\t</video>\n";
- }
- my $new = defined($schedule{$station}{$s}{new});
- my $live = defined($schedule{$station}{$s}{live});
- my $cc = defined($schedule{$station}{$s}{cc});
- if (! $new && ! $live && $p =~ /^EP|^SH|^\d/) {
- print $FH "\t\t<previously-shown ";
- if (defined($programs{$p}{originalAirDate})) {
- $date = &convOAD($programs{$p}{originalAirDate});
- print $FH "start=\"" . $date . "000000\" ";
- }
- print $FH "/>\n";
- }
- if (defined($schedule{$station}{$s}{premiere})) {
- print $FH "\t\t<premiere>" . $schedule{$station}{$s}{premiere} . "</premiere>\n";
- }
- if (defined($schedule{$station}{$s}{finale})) {
- print $FH "\t\t<last-chance>" . $schedule{$station}{$s}{finale} . "</last-chance>\n";
- }
- print $FH "\t\t<new />\n" if $new;
- # not part of XMLTV format yet?
- print $FH "\t\t<live />\n" if (defined($options{L}) && $live);
- print $FH "\t\t<subtitles type=\"teletext\" />\n" if $cc;
- if (defined($programs{$p}{rating})) {
- print $FH "\t\t<rating>\n\t\t\t<value>" . $programs{$p}{rating} . "</value>\n\t\t</rating>\n"
- }
- if (defined($programs{$p}{starRating})) {
- print $FH "\t\t<star-rating>\n\t\t\t<value>" . $programs{$p}{starRating} . "/4</value>\n\t\t</star-rating>\n";
- }
- print $FH "\t</programme>\n";
- $i++;
- }
- }
- }
- sub printHeaderXTVD {
- my ($FH, $enc) = @_;
- print $FH "<?xml version='1.0' encoding='$enc'?>\n";
- print $FH "<xtvd from='" . &convTimeXTVD($XTVD_startTime) . "' to='" . &convTimeXTVD($XTVD_endTime) . "' schemaVersion='1.3' xmlns='urn:TMSWebServices' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='urn:TMSWebServices http://docs.tms.tribune.com/tech/xml/schemas/tmsxtvd.xsd'>\n";
- }
- sub printCredits {
- my ($FH, $p, $s) = @_;
- foreach my $g (sort { $programs{$p}{$s}{$a} <=> $programs{$p}{$s}{$b} } keys %{$programs{$p}{$s}} ) {
- print $FH "\t\t\t<$s>" . &enc($g) . "</$s>\n";
- }
- }
- sub printFooterXTVD {
- my $FH = shift;
- print $FH "</xtvd>\n";
- }
- sub printStationsXTVD {
- my $FH = shift;
- print $FH "<stations>\n";
- for my $key ( sort sortChan keys %stations ) {
- print $FH "\t<station id='" . $stations{$key}{stnNum} . "'>\n";
- if (defined($stations{$key}{number})) {
- $sname = &enc($stations{$key}{name});
- print $FH "\t\t<callSign>" . $sname . "</callSign>\n";
- print $FH "\t\t<name>" . $sname . "</name>\n";
- print $FH "\t\t<fccChannelNumber>" . $stations{$key}{number} . "</fccChannelNumber>\n";
- if (defined($stations{$key}{logo}) && $stations{$key}{logo} =~ /_affiliate/i) {
- $affiliate = $stations{$key}{logo};
- $affiliate =~ s/(.*)\_.*/uc($1)/e;
- print $FH "\t\t<affiliate>" . $affiliate . " Affiliate</affiliate>\n";
- }
- ©Logo($key);
- }
- print $FH "\t</station>\n";
- }
- print $FH "</stations>\n";
- }
- sub printLineupsXTVD {
- my $FH = shift;
- print $FH "<lineups>\n";
- print $FH "\t<lineup id='$lineupId' name='$lineupname' location='$lineuplocation' type='$lineuptype' postalCode='$postalcode'>\n";
- for my $key ( sort sortChan keys %stations ) {
- if (defined($stations{$key}{number})) {
- print $FH "\t<map station='" . $stations{$key}{stnNum} . "' channel='" . $stations{$key}{number} . "'></map>\n";
- }
- }
- print $FH "\t</lineup>\n";
- print $FH "</lineups>\n";
- }
- sub printSchedulesXTVD {
- my $FH = shift;
- print $FH "<schedules>\n";
- for my $station ( sort sortChan keys %stations ) {
- my $i = 0;
- my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
- foreach $s (@keyArray) {
- if ($#keyArray <= $i) {
- delete $schedule{$station}{$s};
- next;
- }
- my $p = $schedule{$station}{$s}{program};
- my $startTime = &convTimeXTVD($schedule{$station}{$s}{time});
- my $stopTime = &convTimeXTVD($schedule{$station}{$keyArray[$i+1]}{time});
- my $duration = &convDurationXTVD($schedule{$station}{$keyArray[$i+1]}{time} - $schedule{$station}{$s}{time});
- print $FH "\t<schedule program='$p' station='" . $stations{$station}{stnNum} . "' time='$startTime' duration='$duration'";
- print $FH " hdtv='true' " if (defined($schedule{$station}{$s}{quality}));
- print $FH " new='true' " if (defined($schedule{$station}{$s}{new}) || defined($schedule{$station}{$s}{live}));
- print $FH "/>\n";
- $i++;
- }
- }
- print $FH "</schedules>\n";
- }
- sub printProgramsXTVD {
- my $FH = shift;
- print $FH "<programs>\n";
- foreach $p (keys %programs) {
- print $FH "\t<program id='" . $p . "'>\n";
- print $FH "\t\t<title>" . &enc($programs{$p}{title}) . "</title>\n" if defined($programs{$p}{title});
- print $FH "\t\t<subtitle>" . &enc($programs{$p}{episode}) . "</subtitle>\n" if defined($programs{$p}{episode});
- print $FH "\t\t<description>" . &enc($programs{$p}{description}) . "</description>\n" if defined($programs{$p}{description});
- if (defined($programs{$p}{movie_year})) {
- print $FH "\t\t<year>" . $programs{$p}{movie_year} . "</year>\n";
- } else { #Guess
- my $showType = "Series";
- if ($programs{$p}{title} =~ /Paid Programming/i) {
- $showType = "Paid Programming";
- }
- print $FH "\t\t<showType>$showType</showType>\n";
- print $FH "\t\t<series>EP" . substr($p,2,8) . "</series>\n";
- print $FH "\t\t<originalAirDate>" . &convOADXTVD($programs{$p}{originalAirDate}) . "</originalAirDate>\n" if defined($programs{$p}{originalAirDate});
- }
- print $FH "\t</program>\n";
- }
- print $FH "</programs>\n";
- }
- sub printGenresXTVD {
- my $FH = shift;
- print $FH "<genres>\n";
- foreach $p (keys %programs) {
- if (defined($programs{$p}{genres}) && $programs{$p}{genres}{movie} != 1) {
- print $FH "\t<programGenre program='" . $p . "'>\n";
- foreach my $g (keys %{$programs{$p}{genres}}) {
- print $FH "\t\t<genre>\n";
- print $FH "\t\t\t<class>" . &enc(ucfirst($g)) . "</class>\n";
- print $FH "\t\t\t<relevance>0</relevance>\n";
- print $FH "\t\t</genre>\n";
- }
- print $FH "\t</programGenre>\n";
- }
- }
- print $FH "</genres>\n";
- }
- sub loginTVG {
- $treq++;
- my $r = $ua->get($tvgurl . 'user/_modal/');
- if ($r->is_success) {
- my $str = $r->decoded_content;
- if ($str =~ /<input.+name=\"_token\".+?value=\"(.*?)\"/is) {
- $token = $1;
- if ($userEmail ne '' && $password ne '') {
- my $rc = 0;
- while ($rc++ < $retries) {
- my $r = $ua->post($tvgurl . 'user/attempt/',
- {
- _token => $token,
- email => $userEmail,
- password => $password,
- }, 'X-Requested-With' => 'XMLHttpRequest'
- );
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($dc =~ /success/) {
- $ua->cookie_jar->scan(sub { if ($_[1] eq "ServiceID") { $zlineupId = $_[2]; }; });
- if (!defined($options{a})) {
- my $r = $ua->get($tvgurl . "user/favorites/?provider=$zlineupId",'X-Requested-With' => 'XMLHttpRequest');
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($dc =~ /\{\"code\":200/) {
- &parseTVGFavs($dc);
- }
- }
- return $dc;
- } else {
- &pout("[Attempt $rc] " . $dc . "\n");
- sleep ($sleeptime + 1);
- }
- }
- die "Failed to login within $retries retries.\n";
- }
- } else {
- die "Login token not found\n";
- }
- }
- }
- sub loginZAP {
- my $rc = 0;
- while ($rc++ < $retries) {
- $treq++;
- my $r = $ua->post($urlRoot . 'api/user/login',
- {
- emailid => $userEmail, password => $password,
- usertype => '0', facebookuser =>'false',
- }
- );
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($r->is_success) {
- my $t = decode_json($dc);
- $zapToken = $t->{'token'};
- my $prs = $t->{'properties'};
- $postalcode = $prs->{2002};
- $country = $prs->{2003};
- ($lineupId, $device) = split(/:/, $prs->{2004});
- if (!defined($options{a})) {
- my $r = $ua->post($urlRoot . "api/user/favorites", { token => $zapToken }, 'X-Requested-With' => 'XMLHttpRequest');
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($r->is_success) {
- &parseZFavs($dc);
- } else {
- &perr("FF" . $r->status_line . ": $dc\n");
- }
- }
- return $dc;
- } else {
- &pout("[Attempt $rc] " . $dc . "\n");
- sleep ($sleeptime + 1);
- }
- }
- die "Failed to login within $retries retries.\n";
- }
- sub getZapGParams {
- my %hash = &getZapParams();
- $hash{country} = delete $hash{countryCode};
- return join("&", map { "$_=$hash{$_}" } keys %hash);
- }
- sub getZapPParams {
- my %hash = &getZapParams();
- delete $hash{lineupId};
- return %hash;
- }
- sub getZapParams {
- my %phash = ();
- if (defined($zlineupId) || defined($zipcode)) {
- $postalcode = $zipcode;
- $country = "USA";
- $country = "CAN" if ($zipcode =~ /[A-z]/);
- if ($zlineupId =~ /:/) {
- ($lineupId, $device) = split(/:/, $zlineupId);
- } else {
- $lineupId = $zlineupId;
- $device = "-";
- }
- $phash{postalCode} = $postalcode;
- } else {
- $phash{token} = &getZToken();
- }
- $phash{lineupId} = "$country-$lineupId-DEFAULT";
- $phash{postalCode} = $postalcode;
- $phash{countryCode} = $country;
- $phash{headendId} = $lineupId;
- $phash{device} = $device;
- $phash{aid} = 'gapzap';
- return %phash;
- }
- sub login {
- if (!defined($userEmail) || $userEmail eq '' || !defined($password) || $password eq '') {
- if (!defined($zlineupId)) {
- die "Unable to login: Unspecified username or password.\n"
- }
- }
- if (!defined($ua)) {
- $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); # WIN
- $ua->cookie_jar(HTTP::Cookies->new);
- $ua->proxy(['http', 'https'], $proxy) if defined($proxy);
- $ua->agent('Mozilla/4.0');
- $ua->default_headers->push_header('Accept-Encoding' => 'gzip, deflate');
- }
- if ($userEmail ne '' && $password ne '') {
- &pout("Logging in as \"$userEmail\" (" . localtime . ")\n");
- if (defined($options{z})) {
- &loginTVG();
- } else {
- &loginZAP();
- }
- } else {
- &pout("Connecting with lineupId \"$zlineupId\" (" . localtime . ")\n");
- }
- }
- sub getURL {
- my $url = shift;
- &login() if !defined($ua);
- my $rc = 0;
- while ($rc++ < $retries) {
- &pout("[$treq] Getting: $url\n");
- sleep $sleeptime; # do these rapid requests flood servers?
- $treq++;
- my $r = $ua->get($url);
- my $cl = length($r->content);
- $tb += $cl;
- my $dc = $r->decoded_content( raise_error => 1 );
- if ($r->is_success && $cl) {
- return $dc;
- } elsif ($r->code == 400 && $dc =~ /Invalid time stamp passed/) {
- &pout("$dc\n");
- &pout("Date not in range (reached zap2it limit), normal exit.\n");
- return "";
- } elsif ($r->code == 500 && $dc =~ /Could not load details/) {
- &pout("$dc\n");
- return "";
- } else {
- &perr("[Attempt $rc] $cl:" . $r->status_line . "\n");
- sleep ($sleeptime + 2);
- }
- }
- die "Failed to download within $retries retries.\n";
- }
- sub wbf {
- my($f, $s) = @_;
- open(FO, ">$f") or die "Failed to open '$f': $!";
- binmode(FO);
- print FO $s;
- close(FO);
- }
- sub unf {
- my $f = shift;
- unlink($f) or &perr("Failed to delete '$f': $!");
- }
- sub copyLogo {
- my $key = shift;
- if (defined($iconDir) && defined($stations{$key}{logo})) {
- my $num = $stations{$key}{number};
- my $src = "$iconDir/" . $stations{$key}{logo} . $stations{$key}{logoExt};
- my $dest1 = "$iconDir/$num" . $stations{$key}{logoExt};
- my $dest2 = "$iconDir/$num " . $stations{$key}{name} . $stations{$key}{logoExt};
- copy($src, $dest1);
- copy($src, $dest2);
- }
- }
- sub handleLogo {
- my $url = shift;
- if (! -d $iconDir) {
- mkdir($iconDir) or die "Can't mkdir: $!\n";
- }
- my $n; my $s; ($n,$_,$s) = fileparse($url, qr"\..*");
- $stations{$cs}{logo} = $n;
- $stations{$cs}{logoExt} = $s;
- $stations{$cs}{logoURL} = $url;
- my $f = $iconDir . "/" . $n . $s;
- if (! -e $f) { &wbf($f, &getURL($url)); }
- }
- sub setOriginalAirDate {
- if (substr($cp,10,4) ne '0000') {
- if (!defined($programs{$cp}{originalAirDate})
- || ($schedule{$cs}{$sch}{time} < $programs{$cp}{originalAirDate})) {
- $programs{$cp}{originalAirDate} = $schedule{$cs}{$sch}{time};
- }
- }
- }
- sub getZToken {
- &login() if (!defined($zapToken));
- return $zapToken;
- }
- sub parseZFavs {
- my $buffer = shift;
- my $t = decode_json($buffer);
- if (defined($t->{'channels'})) {
- my $m = $t->{'channels'};
- foreach my $f (@{$m}) {
- if ($options{R}) {
- my $r = $ua->post($urlRoot . "api/user/ChannelAddtofav", { token => $zapToken, prgsvcid => $f, addToFav => "false" }, 'X-Requested-With' => 'XMLHttpRequest');
- if ($r->is_success) {
- &pout("Removed favorite $f\n");
- } else {
- &perr("RF" . $r->status_line . "\n");
- }
- } else {
- $zapFavorites{$f} = 1;
- }
- }
- if ($options{R}) {
- &pout("Removed favorites, exiting\n");
- exit;
- };
- &pout("Lineup favorites: " . (keys %zapFavorites) . "\n");
- }
- }
- sub parseTVGFavs {
- my $buffer = shift;
- my $t = decode_json($buffer);
- if (defined($t->{'message'})) {
- my $m = $t->{'message'};
- foreach my $f (@{$m}) {
- my $source = $f->{"source"};
- my $channel = $f->{"channel"};
- $tvgfavs{$channel} = $source;
- }
- &pout("Lineup $zlineupId favorites: " . (keys %tvgfavs) . "\n");
- }
- }
- sub parseTVGIcons {
- require GD;
- $rc = Encode::encode('utf8', &getURL($tvgspritesurl . "$zlineupId\.css") );
- if ($rc =~ /background-image:.+?url\((.+?)\)/) {
- my $url = $tvgspritesurl . $1;
- if (! -d $iconDir) {
- mkdir($iconDir) or die "Can't mkdir: $!\n";
- }
- ($n,$_,$s) = fileparse($url, qr"\..*");
- $f = $iconDir . "/sprites-" . $n . $s;
- &wbf($f, &getURL($url));
- GD::Image->trueColor(1);
- $im = new GD::Image->new($f);
- my $iconw = 30;
- my $iconh = 20;
- while ($rc =~ /listings-channel-icon-(.+?)\{.+?position:.*?\-(\d+).+?(\d+).*?\}/isg) {
- my $cid = $1;
- my $iconx = $2;
- my $icony = $3;
- my $icon = new GD::Image($iconw,$iconh);
- $icon->alphaBlending(0);
- $icon->saveAlpha(1);
- $icon->copy($im, 0, 0, $iconx, $icony, $iconw, $iconh);
- $stations{$cid}{logo} = "sprite-" . $cid;
- $stations{$cid}{logoExt} = $s;
- my $ifn = $iconDir . "/" . $stations{$cid}{logo} . $stations{$cid}{logoExt};
- &wbf($ifn, $icon->png);
- }
- }
- }
- sub parseTVGD {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
- if (defined($t->{'program'})) {
- my $prog = $t->{'program'};
- if (defined($prog->{'release_year'})) {
- $programs{$cp}{movie_year} = $prog->{'release_year'};
- }
- if (defined($prog->{'rating'}) && !defined($programs{$cp}{rating})) {
- $programs{$cp}{rating} = $prog->{'rating'} if $prog->{'rating'} ne 'NR';
- }
- }
- if (defined($t->{'tvobject'})) {
- my $tvo = $t->{'tvobject'};
- if (defined($tvo->{'photos'})) {
- my $photos = $tvo->{'photos'};
- my %phash;
- foreach $ph (@{$photos}) {
- my $w = $ph->{'width'} * $ph->{'height'};
- my $u = $ph->{'url'};
- $phash{$w} = $u;
- }
- my $big = (sort {$b <=> $a} keys %phash)[0];
- $programs{$cp}{imageUrl} = $phash{$big};
- }
- }
- }
- sub parseTVGGrid {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
- foreach my $e (@{$t}) {
- my $cjs = $e->{'Channel'};
- $cs = $cjs->{'SourceId'};
- if (%tvgfavs) {
- if (defined($cjs->{'Number'}) && $cjs->{'Number'} ne '') {
- my $n = $cjs->{'Number'};
- if ($cs != $tvgfavs{$n}) {
- next;
- }
- }
- }
- if (!defined($stations{$cs}{stnNum})) {
- $stations{$cs}{stnNum} = $cs;
- $stations{$cs}{number} = $cjs->{'Number'} if defined($cjs->{'Number'}) && $cjs->{'Number'} ne '';
- $stations{$cs}{name} = $cjs->{'Name'};
- if (defined($cjs->{'FullName'}) && $cjs->{'FullName'} ne $cjs->{'Name'}) {
- if ($cjs->{'FullName'} ne '') {
- $stations{$cs}{fullname} = $cjs->{'FullName'};
- }
- }
- if (!defined($stations{$cs}{order})) {
- if (defined($options{b})) {
- $stations{$cs}{order} = $coNum++;
- } else {
- $stations{$cs}{order} = $stations{$cs}{number};
- }
- }
- }
- my $cps = $e->{'ProgramSchedules'};
- foreach my $pe (@{$cps}) {
- next if (!defined($pe->{'ProgramId'}));
- $cp = $pe->{'ProgramId'};
- my $catid = $pe->{'CatId'};
- if ($catid == 1) { $programs{$cp}{genres}{movie} = 1 }
- elsif ($catid == 2) { $programs{$cp}{genres}{sports} = 1 }
- elsif ($catid == 3) { $programs{$cp}{genres}{family} = 1 }
- elsif ($catid == 4) { $programs{$cp}{genres}{news} = 1 }
- # 5 - 10?
- # my $subcatid = $pe->{'SubCatId'};
- my $ppid = $pe->{'ParentProgramId'};
- if ((defined($ppid) && $ppid != 0)
- || (defined($options{j}) && $catid != 1)) {
- $programs{$cp}{genres}{series} = 9;
- }
- $programs{$cp}{title} = $pe->{'Title'};
- $tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
- if (defined($pe->{'EpisodeTitle'}) && $pe->{'EpisodeTitle'} ne '') {
- $programs{$cp}{episode} = $pe->{'EpisodeTitle'};
- $tba = 1 if $programs{$cp}{episode} =~ /$sTBA/i;
- }
- $programs{$cp}{description} = $pe->{'CopyText'} if defined($pe->{'CopyText'}) && $pe->{'CopyText'} ne '';
- $programs{$cp}{rating} = $pe->{'Rating'} if defined($pe->{'Rating'}) && $pe->{'Rating'} ne '';
- my $sch = $pe->{'StartTime'} * 1000;
- $schedule{$cs}{$sch}{time} = $sch;
- $schedule{$cs}{$sch}{endtime} = $pe->{'EndTime'} * 1000;
- $schedule{$cs}{$sch}{program} = $cp;
- $schedule{$cs}{$sch}{station} = $cs;
- my $airat = $pe->{'AiringAttrib'};
- if ($airat & 1) { $schedule{$cs}{$sch}{live} = 1 }
- elsif ($airat & 4) { $schedule{$cs}{$sch}{new} = 1 }
- # other bits?
- my $tvo = $pe->{'TVObject'};
- if (defined($tvo)) {
- if (defined($tvo->{'SeasonNumber'}) && $tvo->{'SeasonNumber'} != 0) {
- $programs{$cp}{seasonNum} = $tvo->{'SeasonNumber'};
- if (defined($tvo->{'EpisodeNumber'}) && $tvo->{'EpisodeNumber'} != 0) {
- $programs{$cp}{episodeNum} = $tvo->{'EpisodeNumber'};
- }
- }
- if (defined($tvo->{'EpisodeAirDate'})) {
- my $eaid = $tvo->{'EpisodeAirDate'}; # GMT @ 00:00:00
- $eaid =~ tr/0-9//cd;
- $programs{$cp}{originalAirDate} = $eaid if ($eaid ne '');
- }
- my $url;
- if (defined($tvo->{'EpisodeSEOUrl'}) && $tvo->{'EpisodeSEOUrl'} ne '') {
- $url = $tvo->{'EpisodeSEOUrl'};
- } elsif(defined($tvo->{'SEOUrl'}) && $tvo->{'SEOUrl'} ne '') {
- $url = $tvo->{'SEOUrl'};
- $url = "/movies$url" if ($catid == 1 && $url !~ /movies/);
- }
- $programs{$cp}{url} = substr($tvgurl, 0, -1) . $url if defined($url);
- }
- if (defined($options{I})
- || (defined($options{D}) && $programs{$cp}{genres}{movie})
- || (defined($options{W}) && $programs{$cp}{genres}{movie}) ) {
- &getDetails(\&parseTVGD, $cp, $tvgMapiRoot . "listings/details?program=$cp", "");
- }
- }
- }
- }
- sub getDetails {
- my ($func, $cp, $url, $prefix) = @_;
- my $fn = "$cacheDir/$prefix$cp\.js\.gz";
- if (! -e $fn) {
- my $rs = &getURL($url);
- if (length($rs)) {
- $rc = Encode::encode('utf8', $rs);
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
- }
- if (-e $fn) {
- my $l = length($prefix) ? $prefix : "D";
- &pout("[$l] Parsing: $cp\n");
- $func->($fn);
- } else {
- &pout("Skipping: $cp\n");
- }
- }
- sub parseJSON {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
- my $sts = $t->{'channels'};
- foreach $s (@$sts) {
- if (defined($s->{'id'})) {
- my $xyz = scalar(keys %zapFavorites);
- if (!$allChan && scalar(keys %zapFavorites)) {
- next unless $zapFavorites{$s->{channelId}};
- }
- $cs = $s->{'id'};
- $stations{$cs}{stnNum} = $cs;
- $stations{$cs}{name} = $s->{'callSign'};
- $stations{$cs}{number} = $s->{'channelNo'};
- $stations{$cs}{number} =~ s/^0+//g;
- if (!defined($stations{$cs}{order})) {
- if (defined($options{b})) {
- $stations{$cs}{order} = $coNum++;
- } else {
- $stations{$cs}{order} = $stations{$cs}{number};
- }
- }
- if ($s->{'thumbnail'} ne '') {
- my $url = $s->{'thumbnail'};
- $url =~ s/\?.*//; # remove size
- if ($url !~ /^http/) {
- $url = "https:" . $url;
- }
- $stations{$cs}{logoURL} = $url;
- &handleLogo($url) if defined($iconDir);
- }
- my $events = $s->{'events'};
- foreach $e (@$events) {
- my $program = $e->{'program'};
- $cp = $program->{'id'};
- $programs{$cp}{title} = $program->{'title'};
- $tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
- $programs{$cp}{episode} = $program->{'episodeTitle'} if ($program->{'episodeTitle'} ne '');
- $programs{$cp}{description} = $program->{'shortDesc'} if ($program->{'shortDesc'} ne '');
- $programs{$cp}{duration} = $e->{duration} if ($e->{duration} > 0);
- $programs{$cp}{movie_year} = $program->{releaseYear} if ($program->{releaseYear} ne '');
- $programs{$cp}{seasonNum} = $program->{season} if ($program->{'season'} ne '');
- if ($program->{'episode'} ne '') {
- $programs{$cp}{episodeNum} = $program->{episode};
- }
- if ($e->{'thumbnail'} ne '') {
- my $turl = $urlAssets;
- $turl .= $e->{'thumbnail'} . ".jpg";
- $programs{$cp}{imageUrl} = $turl;
- }
- if ($program->{'seriesId'} ne '' && $program->{'tmsId'} ne '') {
- $programs{$cp}{url} = $urlRoot . "/overview.html?programSeriesId="
- . $program->{seriesId} . "&tmsId=" . $program->{tmsId};
- }
- $sch = str2time($e->{'startTime'}) * 1000;
- $schedule{$cs}{$sch}{time} = $sch;
- $schedule{$cs}{$sch}{endTime} = str2time($e->{'endTime'}) * 1000;
- $schedule{$cs}{$sch}{program} = $cp;
- $schedule{$cs}{$sch}{station} = $cs;
- if ($e->{'filter'}) {
- my $genres = $e->{'filter'};
- foreach $g (@{$genres}) {
- $g =~ s/filter-//;
- ${$programs{$cp}{genres}}{$g} = 1;
- }
- }
- $programs{$cp}{rating} = $e->{rating} if ($e->{rating} ne '');
- if ($e->{'tags'}) {
- my $tags = $e->{'tags'};
- if (grep $_ eq 'CC', @{$tags}) {
- $schedule{$cs}{$sch}{cc} = 1
- }
- }
- if ($e->{'flag'}) {
- my $flags = $e->{'flag'};
- if (grep $_ eq 'New', @{$flags}) {
- $schedule{$cs}{$sch}{new} = 'New'
- &setOriginalAirDate();
- }
- if (grep $_ eq 'Live', @{$flags}) {
- $schedule{$cs}{$sch}{live} = 'Live'
- &setOriginalAirDate(); # live to tape?
- }
- if (grep $_ eq 'Premiere', @{$flags}) {
- $schedule{$cs}{$sch}{premiere} = 'Premiere';
- }
- if (grep $_ eq 'Finale', @{$flags}) {
- $schedule{$cs}{$sch}{finale} = 'Finale';
- }
- }
- if ($options{D} && !$program->{isGeneric}) {
- &postJSONO($cp, $program->{seriesId});
- }
- }
- }
- }
- return 0;
- }
- sub postJSONO {
- my ($cp, $sid) = @_;
- my $fn = "$cacheDir/O$cp\.js\.gz";
- if (! -e $fn && defined($sidCache{$sid}) && -e $sidCache{$sid}) {
- copy($sidCache{$sid}, $fn);
- }
- if (! -e $fn) {
- my $url = $urlRoot . 'api/program/overviewDetails';
- &pout("[$treq] Post $sid: $url\n");
- $treq++;
- my %phash = &getZapPParams();
- $phash{programSeriesID} = $sid;
- $phash{'clickstream[FromPage]'} = 'TV%20Grid';
- my $r = $ua->post($url, \%phash, 'X-Requested-With' => 'XMLHttpRequest');
- my $cl = length($r->content);
- $tb += $cl;
- if ($r->is_success) {
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- &wbf($fn, Compress::Zlib::memGzip($dc));
- $sidCache{$sid} = $fn;
- } else {
- &perr($id . " :" . $r->status_line);
- }
- }
- if (-e $fn) {
- &pout("[D] Parsing: $cp\n");
- my $gz = gzopen($fn, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
- foreach my $sg (split(/\|/, $t->{seriesGenres})) {
- ${$programs{$cp}{genres}}{lc($sg)} = 1;
- }
- my $i = 1;
- foreach my $c (@{$t->{'overviewTab'}->{cast}}) {
- my $n = $c->{name};
- my $cn = $c->{characterName};
- my $cr = lc($c->{role});
- if ($cr eq 'host') {
- ${$programs{$cp}{presenter}}{$n} = $i++;
- } else {
- ${$programs{$cp}{actor}}{$n} = $i++;
- ${$programs{$cp}{role}}{$n} = $cn if length($cn);
- }
- }
- $i = 1;
- foreach my $c (@{$t->{'overviewTab'}->{crew}}) {
- my $n = $c->{name};
- my $cr = lc($c->{role});
- if ($cr =~ /producer/) {
- ${$programs{$cp}{producer}}{$n} = $i++;
- } elsif ($cr =~ /director/) {
- ${$programs{$cp}{director}}{$n} = $i++;
- } elsif ($cr =~ /writer/) {
- ${$programs{$cp}{writer}}{$n} = $i++;
- }
- }
- if (!defined($programs{$cp}{imageUrl}) && $t->{seriesImage} ne '') {
- my $turl = $urlAssets;
- $turl .= $t->{seriesImage} . ".jpg";
- $programs{$cp}{imageUrl} = $turl;
- }
- if ($cp =~ /^MV|^SH/ && length($t->{seriesDescription}) > length($programs{$cp}{description})) {
- $programs{$cp}{description} = $t->{seriesDescription};
- }
- if ($cp =~ /^EP/) { # GMT @ 00:00:00
- my $ue = $t->{overviewTab}->{upcomingEpisode};
- if (defined($ue) && lc($ue->{tmsID}) eq lc($cp)) {
- $oad = str2time($ue->{originalAirDate}) ;
- if ($oad > 0) { #'1000-01-01T00:00Z'
- $oad *= 1000;
- $programs{$cp}{originalAirDate} = $oad;
- }
- } else {
- foreach my $ue (@{$t->{upcomingEpisodeTab}}) {
- if (lc($ue->{tmsID}) eq lc($cp)) {
- $oad = str2time($ue->{originalAirDate}) ;
- if ($oad > 0) {
- $oad *= 1000;
- $programs{$cp}{originalAirDate} = $oad;
- last;
- }
- }
- }
- }
- }
- } else {
- &pout("Skipping: $sid\n");
- }
- }
- sub hourToMillis {
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
- if ($start == 0) {
- $hour = int($hour/$gridHours) * $gridHours;
- } else {
- $hour = 0;
- }
- $t = timegm(0,0,$hour,$mday,$mon,$year);
- $t = $t - (&tz_offset * 3600) if !defined($options{g});
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t);
- $t = timegm($sec, $min, $hour,$mday,$mon,$year);
- return $t . "000";
- }
- sub tz_offset {
- my $n = defined $_[0] ? $_[0] : time;
- my ($lm, $lh, $ly, $lyd) = (localtime $n)[1, 2, 5, 7];
- my ($gm, $gh, $gy, $gyd) = (gmtime $n)[1, 2, 5, 7];
- ($lm - $gm)/60 + $lh - $gh + 24 * ($ly - $gy || $lyd - $gyd)
- }
- sub timezone {
- my $tztime = defined $_[0] ? &_rtrim3(shift) : time;
- my $os = sprintf "%.1f", (timegm(localtime($tztime)) - $tztime) / 3600;
- my $mins = sprintf "%02d", abs( $os - int($os) ) * 60;
- return sprintf("%+03d", int($os)) . $mins;
- }
- sub max ($$) { $_[$_[0] < $_[1]] }
- sub min ($$) { $_[$_[0] > $_[1]] }
- sub HELP_MESSAGE {
- print <<END;
- zap2xml <zap2xml\@gmail.com> (2018-01-08)
- -u <username>
- -p <password>
- -d <# of days> (default = $days)
- -n <# of no-cache days> (from end) (default = $ncdays)
- -N <# of no-cache days> (from start) (default = $ncsdays)
- -B <no-cache day>
- -s <start day offset> (default = $start)
- -o <output xml filename> (default = "$outFile")
- -c <cacheDirectory> (default = "$cacheDir")
- -l <lang> (default = "$lang")
- -i <iconDirectory> (default = don't download channel icons)
- -m <#> = offset program times by # minutes (better to use TZ env var)
- -b = retain website channel order
- -x = output XTVD xml file format (default = XMLTV)
- -w = wait on exit (require keypress before exiting)
- -q = quiet (no status output)
- -r <# of connection retries before failure> (default = $retries, max 20)
- -e = hex encode entities (html special characters like accents)
- -E "amp apos quot lt gt" = selectively encode standard XML entities
- -F = output channel names first (rather than "number name")
- -O = use old tv_grab_na style channel ids (C###nnnn.zap2it.com)
- -A "new live" = append " *" to program titles that are "new" and/or "live"
- -M = copy movie_year to empty movie sub-title tags
- -U = UTF-8 encoding (default = "ISO-8859-1")
- -L = output "<live />" tag (not part of xmltv.dtd)
- -T = don't cache files containing programs with "$sTBA" titles
- -P <http://proxyhost:port> = to use an http proxy
- -C <configuration file> (default = "$confFile")
- -S <#seconds> = sleep between requests to prevent flooding of server
- -D = include details = 1 extra http request per program!
- -I = include icons (image URLs) - 1 extra http request per program!
- -J <xmltv> = include xmltv file in output
- -Y <lineupId> (if not using username/password)
- -Z <zipcode> (if not using username/password)
- -z = use tvguide.com instead of zap2it.com
- -a = output all channels (not just favorites)
- -j = add "series" category to all non-movie programs
- END
- sleep(5) if ($^O eq 'MSWin32');
- exit 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement