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