1
|
#!/usr/bin/env perl
|
2
|
# zap2xml (c) <[email protected]> - for personal use only!
|
3
|
# not for redistribution of any kind, or conversion to other languages,
|
4
|
# not GPL. not for github, thank you.
|
5
|
|
6
|
BEGIN { $SIG{__DIE__} = sub {
|
7
|
return if $^S;
|
8
|
my $msg = join(" ", @_);
|
9
|
print STDERR "$msg";
|
10
|
if ($msg =~ /can't locate/i) {
|
11
|
print "\nSee homepage for tips on installing missing modules (example: \"perl -MCPAN -e shell\")\n";
|
12
|
if ($^O eq 'MSWin32') {
|
13
|
print "Use \"ppm install\" on windows\n";
|
14
|
}
|
15
|
}
|
16
|
if ($^O eq 'MSWin32') {
|
17
|
if ($msg =~ /uri.pm/i && $msg =~ /temp/i) {
|
18
|
print "\nIf your scanner deleted the perl URI.pm file see the homepage for tips\n";
|
19
|
if ($msg =~ /(\ .\:.+?par-.+?\\)/) {
|
20
|
print "(Delete the $1 folder and retry)\n";
|
21
|
}
|
22
|
}
|
23
|
sleep(5);
|
24
|
}
|
25
|
exit 1;
|
26
|
}}
|
27
|
|
28
|
use Compress::Zlib;
|
29
|
use Encode;
|
30
|
use File::Basename;
|
31
|
use File::Copy;
|
32
|
use Getopt::Std;
|
33
|
use HTTP::Cookies;
|
34
|
use URI;
|
35
|
use URI::Escape;
|
36
|
use LWP::UserAgent;
|
37
|
use POSIX;
|
38
|
use Time::Local;
|
39
|
use Time::Piece;
|
40
|
use JSON;
|
41
|
|
42
|
no warnings 'utf8';
|
43
|
|
44
|
STDOUT->autoflush(1);
|
45
|
STDERR->autoflush(1);
|
46
|
|
47
|
%options=();
|
48
|
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);
|
49
|
|
50
|
$homeDir = $ENV{HOME};
|
51
|
$homeDir = $ENV{USERPROFILE} if !defined($homeDir);
|
52
|
$homeDir = '.' if !defined($homeDir);
|
53
|
$confFile = $homeDir . '/.zap2xmlrc';
|
54
|
|
55
|
# Defaults
|
56
|
$start = 0;
|
57
|
$days = 7;
|
58
|
$ncdays = 0;
|
59
|
$ncsdays = 0;
|
60
|
$ncmday = -1;
|
61
|
$retries = 3;
|
62
|
$outFile = 'xmltv.xml';
|
63
|
$outFile = 'xtvd.xml' if defined $options{x};
|
64
|
$cacheDir = 'cache';
|
65
|
$lang = 'en';
|
66
|
$userEmail = '';
|
67
|
$password = '';
|
68
|
$proxy;
|
69
|
$postalcode;
|
70
|
$country;
|
71
|
$lineupId;
|
72
|
$device;
|
73
|
$sleeptime = 0;
|
74
|
$allChan = 0;
|
75
|
$shiftMinutes = 0;
|
76
|
|
77
|
$outputXTVD = 0;
|
78
|
$lineuptype;
|
79
|
$lineupname;
|
80
|
$lineuplocation;
|
81
|
|
82
|
$zapToken;
|
83
|
$zapPref='-';
|
84
|
%zapFavorites=();
|
85
|
%sidCache=();
|
86
|
|
87
|
$sTBA = "\\bTBA\\b|To Be Announced";
|
88
|
|
89
|
%tvgfavs=();
|
90
|
|
91
|
&HELP_MESSAGE() if defined $options{'?'};
|
92
|
|
93
|
$confFile = $options{C} if defined $options{C};
|
94
|
# read config file
|
95
|
if (open (CONF, $confFile))
|
96
|
{
|
97
|
&pout("Reading config file: $confFile\n");
|
98
|
while (<CONF>)
|
99
|
{
|
100
|
s/#.*//; # comments
|
101
|
if (/^\s*$/i) { }
|
102
|
elsif (/^\s*start\s*=\s*(\d+)/i) { $start = $1; }
|
103
|
elsif (/^\s*days\s*=\s*(\d+)/i) { $days = $1; }
|
104
|
elsif (/^\s*ncdays\s*=\s*(\d+)/i) { $ncdays = $1; }
|
105
|
elsif (/^\s*ncsdays\s*=\s*(\d+)/i) { $ncsdays = $1; }
|
106
|
elsif (/^\s*ncmday\s*=\s*(\d+)/i) { $ncmday = $1; }
|
107
|
elsif (/^\s*retries\s*=\s*(\d+)/i) { $retries = $1; }
|
108
|
elsif (/^\s*user[\w\s]*=\s*(.+)/i) { $userEmail = &rtrim($1); }
|
109
|
elsif (/^\s*pass[\w\s]*=\s*(.+)/i) { $password = &rtrim($1); }
|
110
|
elsif (/^\s*cache\s*=\s*(.+)/i) { $cacheDir = &rtrim($1); }
|
111
|
elsif (/^\s*icon\s*=\s*(.+)/i) { $iconDir = &rtrim($1); }
|
112
|
elsif (/^\s*trailer\s*=\s*(.+)/i) { $trailerDir = &rtrim($1); }
|
113
|
elsif (/^\s*lang\s*=\s*(.+)/i) { $lang = &rtrim($1); }
|
114
|
elsif (/^\s*outfile\s*=\s*(.+)/i) { $outFile = &rtrim($1); }
|
115
|
elsif (/^\s*proxy\s*=\s*(.+)/i) { $proxy = &rtrim($1); }
|
116
|
elsif (/^\s*outformat\s*=\s*(.+)/i) { $outputXTVD = 1 if $1 =~ /xtvd/i; }
|
117
|
elsif (/^\s*lineupid\s*=\s*(.+)/i) { $lineupId = &rtrim($1); }
|
118
|
elsif (/^\s*lineupname\s*=\s*(.+)/i) { $lineupname = &rtrim($1); }
|
119
|
elsif (/^\s*lineuptype\s*=\s*(.+)/i) { $lineuptype = &rtrim($1); }
|
120
|
elsif (/^\s*lineuplocation\s*=\s*(.+)/i) { $lineuplocation = &rtrim($1); }
|
121
|
elsif (/^\s*postalcode\s*=\s*(.+)/i) { $postalcode = &rtrim($1); }
|
122
|
else
|
123
|
{
|
124
|
die "Oddline in config file \"$confFile\".\n\t$_";
|
125
|
}
|
126
|
}
|
127
|
close (CONF);
|
128
|
}
|
129
|
&HELP_MESSAGE() if !(%options) && $userEmail eq '';
|
130
|
|
131
|
$cacheDir = $options{c} if defined $options{c};
|
132
|
$days = $options{d} if defined $options{d};
|
133
|
$ncdays = $options{n} if defined $options{n};
|
134
|
$ncsdays = $options{N} if defined $options{N};
|
135
|
$ncmday = $options{B} if defined $options{B};
|
136
|
$start = $options{s} if defined $options{s};
|
137
|
$retries = $options{r} if defined $options{r};
|
138
|
$iconDir = $options{i} if defined $options{i};
|
139
|
$trailerDir = $options{t} if defined $options{t};
|
140
|
$lang = $options{l} if defined $options{l};
|
141
|
$outFile = $options{o} if defined $options{o};
|
142
|
$password = $options{p} if defined $options{p};
|
143
|
$userEmail = $options{u} if defined $options{u};
|
144
|
$proxy = $options{P} if defined $options{P};
|
145
|
$zlineupId = $options{Y} if defined $options{Y};
|
146
|
$zipcode = $options{Z} if defined $options{Z};
|
147
|
$includeXMLTV = $options{J} if defined $options{J} && -e $options{J};
|
148
|
$outputXTVD = 1 if defined $options{x};
|
149
|
$allChan = 1 if defined($options{a});
|
150
|
$allChan = 1 if defined($zipcode) && defined($zlineupId);
|
151
|
$sleeptime = $options{S} if defined $options{S};
|
152
|
$shiftMinutes = $options{m} if defined $options{m};
|
153
|
$ncdays = $days - $ncdays; # make relative to the end
|
154
|
$urlRoot = 'https://tvlistings.zap2it.com/';
|
155
|
$urlAssets = 'https://zap2it.tmsimg.com/assets/';
|
156
|
$tvgurlRoot = 'http://mobilelistings.tvguide.com/';
|
157
|
$tvgMapiRoot = 'http://mapi.tvguide.com/';
|
158
|
$tvgurl = 'http://www.tvguide.com/';
|
159
|
$tvgspritesurl = 'http://static.tvgcdn.net/sprites/';
|
160
|
$retries = 20 if $retries > 20; # Too many
|
161
|
|
162
|
my %programs = ();
|
163
|
my $cp;
|
164
|
my %stations = ();
|
165
|
my $cs;
|
166
|
my $rcs;
|
167
|
my %schedule = ();
|
168
|
my $sch;
|
169
|
|
170
|
my $coNum = 0;
|
171
|
my $tb = 0;
|
172
|
my $treq = 0;
|
173
|
my $expired = 0;
|
174
|
my $ua;
|
175
|
my $tba = 0;
|
176
|
my $exp = 0;
|
177
|
my @fh = ();
|
178
|
|
179
|
my $XTVD_startTime;
|
180
|
my $XTVD_endTime;
|
181
|
|
182
|
if (! -d $cacheDir) {
|
183
|
mkdir($cacheDir) or die "Can't mkdir: $!\n";
|
184
|
} else {
|
185
|
opendir (DIR, "$cacheDir/");
|
186
|
@cacheFiles = grep(/\.html|\.js/,readdir(DIR));
|
187
|
closedir (DIR);
|
188
|
foreach $cacheFile (@cacheFiles) {
|
189
|
$fn = "$cacheDir/$cacheFile";
|
190
|
$atime = (stat($fn))[8];
|
191
|
if ($atime + ( ($days + 2) * 86400) < time) {
|
192
|
&pout("Deleting old cached file: $fn\n");
|
193
|
&unf($fn);
|
194
|
}
|
195
|
}
|
196
|
}
|
197
|
|
198
|
my $s1 = time();
|
199
|
if (defined($options{z})) {
|
200
|
|
201
|
&login() if !defined($options{a}); # get favorites
|
202
|
&parseTVGIcons() if defined($iconDir);
|
203
|
$gridHours = 3;
|
204
|
$maxCount = $days * (24 / $gridHours);
|
205
|
$offset = $start * 3600 * 24 * 1000;
|
206
|
$ms = &hourToMillis() + $offset;
|
207
|
|
208
|
for ($count=0; $count < $maxCount; $count++) {
|
209
|
$curday = int($count / (24/$gridHours)) + 1;
|
210
|
if ($count == 0) {
|
211
|
$XTVD_startTime = $ms;
|
212
|
} elsif ($count == $maxCount - 1) {
|
213
|
$XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
|
214
|
}
|
215
|
|
216
|
$fn = "$cacheDir/$ms\.js\.gz";
|
217
|
if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
|
218
|
&login() if !defined($zlineupId);
|
219
|
my $duration = $gridHours * 60;
|
220
|
my $tvgstart = substr($ms, 0, -3);
|
221
|
$rc = Encode::encode('utf8', &getURL($tvgurlRoot . "Listingsweb/ws/rest/schedules/$zlineupId/start/$tvgstart/duration/$duration"));
|
222
|
&wbf($fn, Compress::Zlib::memGzip($rc));
|
223
|
}
|
224
|
&pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
|
225
|
&parseTVGGrid($fn);
|
226
|
|
227
|
if (defined($options{T}) && $tba) {
|
228
|
&pout("Deleting: $fn (contains \"$sTBA\")\n");
|
229
|
&unf($fn);
|
230
|
}
|
231
|
if ($exp) {
|
232
|
&pout("Deleting: $fn (expired)\n");
|
233
|
&unf($fn);
|
234
|
}
|
235
|
$exp = 0;
|
236
|
$tba = 0;
|
237
|
$ms += ($gridHours * 3600 * 1000);
|
238
|
}
|
239
|
|
240
|
} else {
|
241
|
|
242
|
&login() if !defined($options{a}); # get favorites
|
243
|
$gridHours = 3;
|
244
|
$maxCount = $days * (24 / $gridHours);
|
245
|
$offset = $start * 3600 * 24 * 1000;
|
246
|
$ms = &hourToMillis() + $offset;
|
247
|
for ($count=0; $count < $maxCount; $count++) {
|
248
|
$curday = int($count / (24/$gridHours)) + 1;
|
249
|
if ($count == 0) {
|
250
|
$XTVD_startTime = $ms;
|
251
|
} elsif ($count == $maxCount - 1) {
|
252
|
$XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
|
253
|
}
|
254
|
|
255
|
$fn = "$cacheDir/$ms\.js\.gz";
|
256
|
if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
|
257
|
my $zstart = substr($ms, 0, -3);
|
258
|
$params = "?time=$zstart×pan=$gridHours&pref=$zapPref&";
|
259
|
$params .= &getZapGParams();
|
260
|
$params .= '&TMSID=&AffiliateID=gapzap&FromPage=TV%20Grid';
|
261
|
$params .= '&ActivityID=1&OVDID=&isOverride=true';
|
262
|
$rs = &getURL($urlRoot . "api/grid$params",'X-Requested-With' => 'XMLHttpRequest');
|
263
|
last if ($rs eq '');
|
264
|
$rc = Encode::encode('utf8', $rs);
|
265
|
&wbf($fn, Compress::Zlib::memGzip($rc));
|
266
|
}
|
267
|
|
268
|
&pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
|
269
|
&parseJSON($fn);
|
270
|
|
271
|
if (defined($options{T}) && $tba) {
|
272
|
&pout("Deleting: $fn (contains \"$sTBA\")\n");
|
273
|
&unf($fn);
|
274
|
}
|
275
|
if ($exp) {
|
276
|
&pout("Deleting: $fn (expired)\n");
|
277
|
&unf($fn);
|
278
|
}
|
279
|
$exp = 0;
|
280
|
$tba = 0;
|
281
|
$ms += ($gridHours * 3600 * 1000);
|
282
|
}
|
283
|
|
284
|
}
|
285
|
my $s2 = time();
|
286
|
|
287
|
&pout("Downloaded $tb bytes in $treq http requests.\n") if $tb > 0;
|
288
|
&pout("Expired programs: $expired\n") if $expired > 0;
|
289
|
&pout("Writing XML file: $outFile\n");
|
290
|
open($FH, ">$outFile");
|
291
|
my $enc = 'ISO-8859-1';
|
292
|
if (defined($options{U})) {
|
293
|
$enc = 'UTF-8';
|
294
|
}
|
295
|
if ($outputXTVD) {
|
296
|
&printHeaderXTVD($FH, $enc);
|
297
|
&printStationsXTVD($FH);
|
298
|
&printLineupsXTVD($FH);
|
299
|
&printSchedulesXTVD($FH);
|
300
|
&printProgramsXTVD($FH);
|
301
|
&printGenresXTVD($FH);
|
302
|
&printFooterXTVD($FH);
|
303
|
} else {
|
304
|
&printHeader($FH, $enc);
|
305
|
&printChannels($FH);
|
306
|
if (defined($includeXMLTV)) {
|
307
|
&pout("Reading XML file: $includeXMLTV\n");
|
308
|
&incXML("<channel","<programme", $FH);
|
309
|
}
|
310
|
&printProgrammes($FH);
|
311
|
&incXML("<programme","</tv", $FH) if defined($includeXMLTV);
|
312
|
&printFooter($FH);
|
313
|
}
|
314
|
|
315
|
close($FH);
|
316
|
|
317
|
my $ts = 0;
|
318
|
for my $station (keys %stations ) {
|
319
|
$ts += scalar (keys %{$schedule{$station}})
|
320
|
}
|
321
|
my $s3 = time();
|
322
|
&pout("Completed in " . ( $s3 - $s1 ) . "s (Parse: " . ( $s2 - $s1 ) . "s) " . keys(%stations) . " stations, " . keys(%programs) . " programs, $ts scheduled.\n");
|
323
|
|
324
|
if (defined($options{w})) {
|
325
|
print "Press ENTER to exit:";
|
326
|
<STDIN>;
|
327
|
} else {
|
328
|
sleep(3) if ($^O eq 'MSWin32');
|
329
|
}
|
330
|
|
331
|
exit 0;
|
332
|
|
333
|
sub incXML {
|
334
|
my ($st, $en, $FH) = @_;
|
335
|
open($XF, "<$includeXMLTV");
|
336
|
while (<$XF>) {
|
337
|
if (/^\s*$st/../^\s*$en/) {
|
338
|
print $FH $_ unless /^\s*$en/
|
339
|
}
|
340
|
}
|
341
|
close($XF);
|
342
|
}
|
343
|
|
344
|
sub pout {
|
345
|
print @_ if !defined $options{q};
|
346
|
}
|
347
|
|
348
|
sub perr {
|
349
|
warn @_;
|
350
|
}
|
351
|
|
352
|
sub rtrim {
|
353
|
my $s = shift;
|
354
|
$s =~ s/\s+$//;
|
355
|
return $s;
|
356
|
}
|
357
|
|
358
|
sub trim {
|
359
|
my $s = shift;
|
360
|
$s =~ s/^\s+//;
|
361
|
$s =~ s/\s+$//;
|
362
|
return $s;
|
363
|
}
|
364
|
|
365
|
sub trim2 {
|
366
|
my $s = &trim(shift);
|
367
|
$s =~ s/[^\w\s\(\)\,]//gsi;
|
368
|
$s =~ s/\s+/ /gsi;
|
369
|
return $s;
|
370
|
}
|
371
|
|
372
|
sub _rtrim3 {
|
373
|
my $s = shift;
|
374
|
return substr($s, 0, length($s)-3);
|
375
|
}
|
376
|
|
377
|
sub convTime {
|
378
|
my $t = shift;
|
379
|
$t += $shiftMinutes * 60 * 1000;
|
380
|
return strftime "%Y%m%d%H%M%S", localtime(&_rtrim3($t));
|
381
|
}
|
382
|
|
383
|
sub convTimeXTVD {
|
384
|
my $t = shift;
|
385
|
$t += $shiftMinutes * 60 * 1000;
|
386
|
return strftime "%Y-%m-%dT%H:%M:%SZ", gmtime(&_rtrim3($t));
|
387
|
}
|
388
|
|
389
|
sub convOAD {
|
390
|
return strftime "%Y%m%d", gmtime(&_rtrim3(shift));
|
391
|
}
|
392
|
|
393
|
sub convOADXTVD {
|
394
|
return strftime "%Y-%m-%d", gmtime(&_rtrim3(shift));
|
395
|
}
|
396
|
|
397
|
sub convDurationXTVD {
|
398
|
my $duration = shift;
|
399
|
my $hour = int($duration / 3600000);
|
400
|
my $minutes = int(($duration - ($hour * 3600000)) / 60000);
|
401
|
return sprintf("PT%02dH%02dM", $hour, $minutes);
|
402
|
}
|
403
|
|
404
|
sub appendAsterisk {
|
405
|
my ($title, $station, $s) = @_;
|
406
|
if (defined($options{A})) {
|
407
|
if (($options{A} =~ "new" && defined($schedule{$station}{$s}{new}))
|
408
|
|| ($options{A} =~ "live" && defined($schedule{$station}{$s}{live}))) {
|
409
|
$title .= " *";
|
410
|
}
|
411
|
}
|
412
|
return $title;
|
413
|
}
|
414
|
|
415
|
sub stationToChannel {
|
416
|
my $s = shift;
|
417
|
if (defined($options{z})) {
|
418
|
return sprintf("I%s.%s.tvguide.com", $stations{$s}{number},$stations{$s}{stnNum});
|
419
|
} elsif (defined($options{O})) {
|
420
|
return sprintf("C%s%s.zap2it.com",$stations{$s}{number},lc($stations{$s}{name}));
|
421
|
} elsif (defined($options{9})) {
|
422
|
return sprintf("I%s.labs.zap2it.com",$stations{$s}{stnNum});
|
423
|
}
|
424
|
return sprintf("I%s.%s.zap2it.com", $stations{$s}{number},$stations{$s}{stnNum});
|
425
|
}
|
426
|
|
427
|
sub sortChan {
|
428
|
if (defined($stations{$a}{order}) && defined($stations{$b}{order})) {
|
429
|
my $c = $stations{$a}{order} <=> $stations{$b}{order};
|
430
|
if ($c == 0) { return $stations{$a}{stnNum} <=> $stations{$b}{stnNum} }
|
431
|
else { return $c };
|
432
|
} else {
|
433
|
return $stations{$a}{name} cmp $stations{$b}{name};
|
434
|
}
|
435
|
}
|
436
|
|
437
|
sub enc {
|
438
|
my $t = shift;
|
439
|
if (!defined($options{U})) {$t = Encode::decode('utf8', $t);}
|
440
|
if (!defined($options{E}) || $options{E} =~ /amp/) {$t =~ s/&/&/gs;}
|
441
|
if (!defined($options{E}) || $options{E} =~ /quot/) {$t =~ s/"/"/gs;}
|
442
|
if (!defined($options{E}) || $options{E} =~ /apos/) {$t =~ s/'/'/gs;}
|
443
|
if (!defined($options{E}) || $options{E} =~ /lt/) {$t =~ s/</</gs;}
|
444
|
if (!defined($options{E}) || $options{E} =~ /gt/) {$t =~ s/>/>/gs;}
|
445
|
if (defined($options{e})) {
|
446
|
$t =~ s/([^\x20-\x7F])/'&#' . ord($1) . ';'/gse;
|
447
|
}
|
448
|
return $t;
|
449
|
}
|
450
|
|
451
|
sub printHeader {
|
452
|
my ($FH, $enc) = @_;
|
453
|
print $FH "<?xml version=\"1.0\" encoding=\"$enc\"?>\n";
|
454
|
print $FH "<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n\n";
|
455
|
if (defined($options{z})) {
|
456
|
print $FH "<tv source-info-url=\"http://tvguide.com/\" source-info-name=\"tvguide.com\"";
|
457
|
} else {
|
458
|
print $FH "<tv source-info-url=\"http://tvlistings.zap2it.com/\" source-info-name=\"zap2it.com\"";
|
459
|
}
|
460
|
print $FH " generator-info-name=\"zap2xml\" generator-info-url=\"zap2xml\@gmail.com\">\n";
|
461
|
}
|
462
|
|
463
|
sub printFooter {
|
464
|
my $FH = shift;
|
465
|
print $FH "</tv>\n";
|
466
|
}
|
467
|
|
468
|
sub printChannels {
|
469
|
my $FH = shift;
|
470
|
for my $key ( sort sortChan keys %stations ) {
|
471
|
$sname = &enc($stations{$key}{name});
|
472
|
$fname = &enc($stations{$key}{fullname});
|
473
|
$snum = $stations{$key}{number};
|
474
|
print $FH "\t<channel id=\"" . &stationToChannel($key) . "\">\n";
|
475
|
print $FH "\t\t<display-name>" . $sname . "</display-name>\n" if defined($options{F}) && defined($sname);
|
476
|
if (defined($snum)) {
|
477
|
©Logo($key);
|
478
|
print $FH "\t\t<display-name>" . $snum . " " . $sname . "</display-name>\n" if ($snum ne '');
|
479
|
print $FH "\t\t<display-name>" . $snum . "</display-name>\n" if ($snum ne '');
|
480
|
}
|
481
|
print $FH "\t\t<display-name>" . $sname . "</display-name>\n" if !defined($options{F}) && defined($sname);
|
482
|
print $FH "\t\t<display-name>" . $fname . "</display-name>\n" if (defined($fname));
|
483
|
if (defined($stations{$key}{logoURL})) {
|
484
|
print $FH "\t\t<icon src=\"" . $stations{$key}{logoURL} . "\" />\n";
|
485
|
}
|
486
|
print $FH "\t</channel>\n";
|
487
|
}
|
488
|
}
|
489
|
|
490
|
sub printProgrammes {
|
491
|
my $FH = shift;
|
492
|
for my $station ( sort sortChan keys %stations ) {
|
493
|
my $i = 0;
|
494
|
my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
|
495
|
foreach $s (@keyArray) {
|
496
|
if ($#keyArray <= $i && !defined($schedule{$station}{$s}{endtime})) {
|
497
|
delete $schedule{$station}{$s};
|
498
|
next;
|
499
|
}
|
500
|
my $p = $schedule{$station}{$s}{program};
|
501
|
my $startTime = &convTime($schedule{$station}{$s}{time});
|
502
|
my $startTZ = &timezone($schedule{$station}{$s}{time});
|
503
|
my $endTime;
|
504
|
if (defined($schedule{$station}{$s}{endtime})) {
|
505
|
$endTime = $schedule{$station}{$s}{endtime};
|
506
|
} else {
|
507
|
$endTime = $schedule{$station}{$keyArray[$i+1]}{time};
|
508
|
}
|
509
|
|
510
|
my $stopTime = &convTime($endTime);
|
511
|
my $stopTZ = &timezone($endTime);
|
512
|
|
513
|
print $FH "\t<programme start=\"$startTime $startTZ\" stop=\"$stopTime $stopTZ\" channel=\"" . &stationToChannel($schedule{$station}{$s}{station}) . "\">\n";
|
514
|
if (defined($programs{$p}{title})) {
|
515
|
my $title = &enc($programs{$p}{title});
|
516
|
$title = &appendAsterisk($title, $station, $s);
|
517
|
print $FH "\t\t<title lang=\"$lang\">" . $title . "</title>\n";
|
518
|
}
|
519
|
|
520
|
if (defined($programs{$p}{episode}) || (defined($options{M}) && defined($programs{$p}{movie_year}))) {
|
521
|
print $FH "\t\t<sub-title lang=\"$lang\">";
|
522
|
if (defined($programs{$p}{episode})) {
|
523
|
print $FH &enc($programs{$p}{episode});
|
524
|
} else {
|
525
|
print $FH "Movie (" . $programs{$p}{movie_year} . ")";
|
526
|
}
|
527
|
print $FH "</sub-title>\n"
|
528
|
}
|
529
|
|
530
|
print $FH "\t\t<desc lang=\"$lang\">" . &enc($programs{$p}{description}) . "</desc>\n" if defined($programs{$p}{description});
|
531
|
|
532
|
if (defined($programs{$p}{actor})
|
533
|
|| defined($programs{$p}{director})
|
534
|
|| defined($programs{$p}{writer})
|
535
|
|| defined($programs{$p}{producer})
|
536
|
|| defined($programs{$p}{preseter})
|
537
|
) {
|
538
|
print $FH "\t\t<credits>\n";
|
539
|
&printCredits($FH, $p, "director");
|
540
|
foreach my $g (sort { $programs{$p}{actor}{$a} <=> $programs{$p}{actor}{$b} } keys %{$programs{$p}{actor}} ) {
|
541
|
print $FH "\t\t\t<actor";
|
542
|
print $FH " role=\"" . &enc($programs{$p}{role}{$g}) . "\"" if (defined($programs{$p}{role}{$g}));
|
543
|
print $FH ">" . &enc($g) . "</actor>\n";
|
544
|
}
|
545
|
&printCredits($FH, $p, "writer");
|
546
|
&printCredits($FH, $p, "producer");
|
547
|
&printCredits($FH, $p, "presenter");
|
548
|
print $FH "\t\t</credits>\n";
|
549
|
}
|
550
|
|
551
|
my $date;
|
552
|
if (defined($programs{$p}{movie_year})) {
|
553
|
$date = $programs{$p}{movie_year};
|
554
|
} elsif (defined($programs{$p}{originalAirDate}) && $p =~ /^EP|^\d/) {
|
555
|
$date = &convOAD($programs{$p}{originalAirDate});
|
556
|
}
|
557
|
print $FH "\t\t<date>$date</date>\n" if defined($date);
|
558
|
|
559
|
if (defined($programs{$p}{genres})) {
|
560
|
foreach my $g (sort { $programs{$p}{genres}{$a} <=> $programs{$p}{genres}{$b} or $a cmp $b } keys %{$programs{$p}{genres}} ) {
|
561
|
print $FH "\t\t<category lang=\"$lang\">" . &enc(ucfirst($g)) . "</category>\n";
|
562
|
}
|
563
|
}
|
564
|
|
565
|
print $FH "\t\t<length units=\"minutes\">" . $programs{$p}{duration} . "</length>\n" if defined($programs{$p}{duration});
|
566
|
|
567
|
if (defined($programs{$p}{imageUrl})) {
|
568
|
print $FH "\t\t<icon src=\"" . &enc($programs{$p}{imageUrl}) . "\" />\n";
|
569
|
}
|
570
|
|
571
|
if (defined($programs{$p}{url})) {
|
572
|
print $FH "\t\t<url>" . &enc($programs{$p}{url}) . "</url>\n";
|
573
|
}
|
574
|
|
575
|
my $xs;
|
576
|
my $xe;
|
577
|
|
578
|
if (defined($programs{$p}{seasonNum}) && defined($programs{$p}{episodeNum})) {
|
579
|
my $s = $programs{$p}{seasonNum};
|
580
|
my $sf = sprintf("S%0*d", &max(2, length($s)), $s);
|
581
|
my $e = $programs{$p}{episodeNum};
|
582
|
my $ef = sprintf("E%0*d", &max(2, length($e)), $e);
|
583
|
|
584
|
$xs = int($s) - 1;
|
585
|
$xe = int($e) - 1;
|
586
|
|
587
|
if ($s > 0 || $e > 0) {
|
588
|
print $FH "\t\t<episode-num system=\"common\">" . $sf . $ef . "</episode-num>\n";
|
589
|
}
|
590
|
}
|
591
|
|
592
|
$dd_prog_id = $p;
|
593
|
if ( $dd_prog_id =~ /^(..\d{8})(\d{4})/ ) {
|
594
|
$dd_prog_id = sprintf("%s.%s",$1,$2);
|
595
|
print $FH "\t\t<episode-num system=\"dd_progid\">" . $dd_prog_id . "</episode-num>\n";
|
596
|
}
|
597
|
|
598
|
if (defined($xs) && defined($xe) && $xs >= 0 && $xe >= 0) {
|
599
|
print $FH "\t\t<episode-num system=\"xmltv_ns\">" . $xs . "." . $xe . ".</episode-num>\n";
|
600
|
}
|
601
|
|
602
|
if (defined($schedule{$station}{$s}{quality})) {
|
603
|
print $FH "\t\t<video>\n";
|
604
|
print $FH "\t\t\t<aspect>16:9</aspect>\n";
|
605
|
print $FH "\t\t\t<quality>HDTV</quality>\n";
|
606
|
print $FH "\t\t</video>\n";
|
607
|
}
|
608
|
my $new = defined($schedule{$station}{$s}{new});
|
609
|
my $live = defined($schedule{$station}{$s}{live});
|
610
|
my $cc = defined($schedule{$station}{$s}{cc});
|
611
|
|
612
|
if (! $new && ! $live && $p =~ /^EP|^SH|^\d/) {
|
613
|
print $FH "\t\t<previously-shown ";
|
614
|
if (defined($programs{$p}{originalAirDate})) {
|
615
|
$date = &convOAD($programs{$p}{originalAirDate});
|
616
|
print $FH "start=\"" . $date . "000000\" ";
|
617
|
}
|
618
|
print $FH "/>\n";
|
619
|
}
|
620
|
|
621
|
if (defined($schedule{$station}{$s}{premiere})) {
|
622
|
print $FH "\t\t<premiere>" . $schedule{$station}{$s}{premiere} . "</premiere>\n";
|
623
|
}
|
624
|
|
625
|
if (defined($schedule{$station}{$s}{finale})) {
|
626
|
print $FH "\t\t<last-chance>" . $schedule{$station}{$s}{finale} . "</last-chance>\n";
|
627
|
}
|
628
|
|
629
|
print $FH "\t\t<new />\n" if $new;
|
630
|
# not part of XMLTV format yet?
|
631
|
print $FH "\t\t<live />\n" if (defined($options{L}) && $live);
|
632
|
print $FH "\t\t<subtitles type=\"teletext\" />\n" if $cc;
|
633
|
|
634
|
if (defined($programs{$p}{rating})) {
|
635
|
print $FH "\t\t<rating>\n\t\t\t<value>" . $programs{$p}{rating} . "</value>\n\t\t</rating>\n"
|
636
|
}
|
637
|
|
638
|
if (defined($programs{$p}{starRating})) {
|
639
|
print $FH "\t\t<star-rating>\n\t\t\t<value>" . $programs{$p}{starRating} . "/4</value>\n\t\t</star-rating>\n";
|
640
|
}
|
641
|
print $FH "\t</programme>\n";
|
642
|
$i++;
|
643
|
}
|
644
|
}
|
645
|
}
|
646
|
|
647
|
sub printHeaderXTVD {
|
648
|
my ($FH, $enc) = @_;
|
649
|
print $FH "<?xml version='1.0' encoding='$enc'?>\n";
|
650
|
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";
|
651
|
}
|
652
|
|
653
|
sub printCredits {
|
654
|
my ($FH, $p, $s) = @_;
|
655
|
foreach my $g (sort { $programs{$p}{$s}{$a} <=> $programs{$p}{$s}{$b} } keys %{$programs{$p}{$s}} ) {
|
656
|
print $FH "\t\t\t<$s>" . &enc($g) . "</$s>\n";
|
657
|
}
|
658
|
}
|
659
|
|
660
|
sub printFooterXTVD {
|
661
|
my $FH = shift;
|
662
|
print $FH "</xtvd>\n";
|
663
|
}
|
664
|
|
665
|
sub printStationsXTVD {
|
666
|
my $FH = shift;
|
667
|
print $FH "<stations>\n";
|
668
|
for my $key ( sort sortChan keys %stations ) {
|
669
|
print $FH "\t<station id='" . $stations{$key}{stnNum} . "'>\n";
|
670
|
if (defined($stations{$key}{number})) {
|
671
|
$sname = &enc($stations{$key}{name});
|
672
|
print $FH "\t\t<callSign>" . $sname . "</callSign>\n";
|
673
|
print $FH "\t\t<name>" . $sname . "</name>\n";
|
674
|
print $FH "\t\t<fccChannelNumber>" . $stations{$key}{number} . "</fccChannelNumber>\n";
|
675
|
if (defined($stations{$key}{logo}) && $stations{$key}{logo} =~ /_affiliate/i) {
|
676
|
$affiliate = $stations{$key}{logo};
|
677
|
$affiliate =~ s/(.*)\_.*/uc($1)/e;
|
678
|
print $FH "\t\t<affiliate>" . $affiliate . " Affiliate</affiliate>\n";
|
679
|
}
|
680
|
©Logo($key);
|
681
|
}
|
682
|
print $FH "\t</station>\n";
|
683
|
}
|
684
|
print $FH "</stations>\n";
|
685
|
}
|
686
|
|
687
|
sub printLineupsXTVD {
|
688
|
my $FH = shift;
|
689
|
print $FH "<lineups>\n";
|
690
|
print $FH "\t<lineup id='$lineupId' name='$lineupname' location='$lineuplocation' type='$lineuptype' postalCode='$postalcode'>\n";
|
691
|
for my $key ( sort sortChan keys %stations ) {
|
692
|
if (defined($stations{$key}{number})) {
|
693
|
print $FH "\t<map station='" . $stations{$key}{stnNum} . "' channel='" . $stations{$key}{number} . "'></map>\n";
|
694
|
}
|
695
|
}
|
696
|
print $FH "\t</lineup>\n";
|
697
|
print $FH "</lineups>\n";
|
698
|
}
|
699
|
|
700
|
sub printSchedulesXTVD {
|
701
|
my $FH = shift;
|
702
|
print $FH "<schedules>\n";
|
703
|
for my $station ( sort sortChan keys %stations ) {
|
704
|
my $i = 0;
|
705
|
my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
|
706
|
foreach $s (@keyArray) {
|
707
|
if ($#keyArray <= $i) {
|
708
|
delete $schedule{$station}{$s};
|
709
|
next;
|
710
|
}
|
711
|
my $p = $schedule{$station}{$s}{program};
|
712
|
my $startTime = &convTimeXTVD($schedule{$station}{$s}{time});
|
713
|
my $stopTime = &convTimeXTVD($schedule{$station}{$keyArray[$i+1]}{time});
|
714
|
my $duration = &convDurationXTVD($schedule{$station}{$keyArray[$i+1]}{time} - $schedule{$station}{$s}{time});
|
715
|
|
716
|
print $FH "\t<schedule program='$p' station='" . $stations{$station}{stnNum} . "' time='$startTime' duration='$duration'";
|
717
|
print $FH " hdtv='true' " if (defined($schedule{$station}{$s}{quality}));
|
718
|
print $FH " new='true' " if (defined($schedule{$station}{$s}{new}) || defined($schedule{$station}{$s}{live}));
|
719
|
print $FH "/>\n";
|
720
|
$i++;
|
721
|
}
|
722
|
}
|
723
|
print $FH "</schedules>\n";
|
724
|
}
|
725
|
|
726
|
sub printProgramsXTVD {
|
727
|
my $FH = shift;
|
728
|
print $FH "<programs>\n";
|
729
|
foreach $p (keys %programs) {
|
730
|
print $FH "\t<program id='" . $p . "'>\n";
|
731
|
print $FH "\t\t<title>" . &enc($programs{$p}{title}) . "</title>\n" if defined($programs{$p}{title});
|
732
|
print $FH "\t\t<subtitle>" . &enc($programs{$p}{episode}) . "</subtitle>\n" if defined($programs{$p}{episode});
|
733
|
print $FH "\t\t<description>" . &enc($programs{$p}{description}) . "</description>\n" if defined($programs{$p}{description});
|
734
|
|
735
|
if (defined($programs{$p}{movie_year})) {
|
736
|
print $FH "\t\t<year>" . $programs{$p}{movie_year} . "</year>\n";
|
737
|
} else { #Guess
|
738
|
my $showType = "Series";
|
739
|
if ($programs{$p}{title} =~ /Paid Programming/i) {
|
740
|
$showType = "Paid Programming";
|
741
|
}
|
742
|
print $FH "\t\t<showType>$showType</showType>\n";
|
743
|
print $FH "\t\t<series>EP" . substr($p,2,8) . "</series>\n";
|
744
|
print $FH "\t\t<originalAirDate>" . &convOADXTVD($programs{$p}{originalAirDate}) . "</originalAirDate>\n" if defined($programs{$p}{originalAirDate});
|
745
|
}
|
746
|
print $FH "\t</program>\n";
|
747
|
}
|
748
|
print $FH "</programs>\n";
|
749
|
}
|
750
|
|
751
|
sub printGenresXTVD {
|
752
|
my $FH = shift;
|
753
|
print $FH "<genres>\n";
|
754
|
foreach $p (keys %programs) {
|
755
|
if (defined($programs{$p}{genres}) && $programs{$p}{genres}{movie} != 1) {
|
756
|
print $FH "\t<programGenre program='" . $p . "'>\n";
|
757
|
foreach my $g (keys %{$programs{$p}{genres}}) {
|
758
|
print $FH "\t\t<genre>\n";
|
759
|
print $FH "\t\t\t<class>" . &enc(ucfirst($g)) . "</class>\n";
|
760
|
print $FH "\t\t\t<relevance>0</relevance>\n";
|
761
|
print $FH "\t\t</genre>\n";
|
762
|
}
|
763
|
print $FH "\t</programGenre>\n";
|
764
|
}
|
765
|
}
|
766
|
print $FH "</genres>\n";
|
767
|
}
|
768
|
|
769
|
sub loginTVG {
|
770
|
$treq++;
|
771
|
my $r = $ua->get($tvgurl . 'user/_modal/');
|
772
|
if ($r->is_success) {
|
773
|
my $str = $r->decoded_content;
|
774
|
if ($str =~ /<input.+name=\"_token\".+?value=\"(.*?)\"/is) {
|
775
|
$token = $1;
|
776
|
if ($userEmail ne '' && $password ne '') {
|
777
|
my $rc = 0;
|
778
|
while ($rc++ < $retries) {
|
779
|
my $r = $ua->post($tvgurl . 'user/attempt/',
|
780
|
{
|
781
|
_token => $token,
|
782
|
email => $userEmail,
|
783
|
password => $password,
|
784
|
}, 'X-Requested-With' => 'XMLHttpRequest'
|
785
|
);
|
786
|
|
787
|
$dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
|
788
|
if ($dc =~ /success/) {
|
789
|
$ua->cookie_jar->scan(sub { if ($_[1] eq "ServiceID") { $zlineupId = $_[2]; }; });
|
790
|
if (!defined($options{a})) {
|
791
|
my $r = $ua->get($tvgurl . "user/favorites/?provider=$zlineupId",'X-Requested-With' => 'XMLHttpRequest');
|
792
|
$dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
|
793
|
if ($dc =~ /\{\"code\":200/) {
|
794
|
&parseTVGFavs($dc);
|
795
|
}
|
796
|
}
|
797
|
return $dc;
|
798
|
} else {
|
799
|
&pout("[Attempt $rc] " . $dc . "\n");
|
800
|
sleep ($sleeptime + 1);
|
801
|
}
|
802
|
}
|
803
|
die "Failed to login within $retries retries.\n";
|
804
|
}
|
805
|
} else {
|
806
|
die "Login token not found\n";
|
807
|
}
|
808
|
}
|
809
|
}
|
810
|
|
811
|
sub loginZAP {
|
812
|
my $rc = 0;
|
813
|
while ($rc++ < $retries) {
|
814
|
$treq++;
|
815
|
my $r = $ua->post($urlRoot . 'api/user/login',
|
816
|
{
|
817
|
emailid => $userEmail, password => $password,
|
818
|
usertype => '0', facebookuser =>'false',
|
819
|
}
|
820
|
);
|
821
|
|
822
|
$dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
|
823
|
if ($r->is_success) {
|
824
|
my $t = decode_json($dc);
|
825
|
$zapToken = $t->{'token'};
|
826
|
$zapPref = '';
|
827
|
$zapPref .= "m" if ($t->{isMusic});
|
828
|
$zapPref .= "p" if ($t->{isPPV});
|
829
|
$zapPref .= "h" if ($t->{isHD});
|
830
|
if ($zapPref eq '') { $zapPref = '-' }
|
831
|
else {
|
832
|
$zapPref = join(",", split(//, $zapPref));
|
833
|
}
|
834
|
|
835
|
my $prs = $t->{'properties'};
|
836
|
$postalcode = $prs->{2002};
|
837
|
$country = $prs->{2003};
|
838
|
($lineupId, $device) = split(/:/, $prs->{2004});
|
839
|
if (!defined($options{a})) {
|
840
|
my $r = $ua->post($urlRoot . "api/user/favorites", { token => $zapToken }, 'X-Requested-With' => 'XMLHttpRequest');
|
841
|
$dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
|
842
|
if ($r->is_success) {
|
843
|
&parseZFavs($dc);
|
844
|
} else {
|
845
|
&perr("FF" . $r->status_line . ": $dc\n");
|
846
|
}
|
847
|
}
|
848
|
return $dc;
|
849
|
} else {
|
850
|
&pout("[Attempt $rc] " . $dc . "\n");
|
851
|
sleep ($sleeptime + 1);
|
852
|
}
|
853
|
}
|
854
|
die "Failed to login within $retries retries.\n";
|
855
|
}
|
856
|
|
857
|
sub getZapGParams {
|
858
|
my %hash = &getZapParams();
|
859
|
$hash{country} = delete $hash{countryCode};
|
860
|
return join("&", map { "$_=$hash{$_}" } keys %hash);
|
861
|
}
|
862
|
|
863
|
sub getZapPParams {
|
864
|
my %hash = &getZapParams();
|
865
|
delete $hash{lineupId};
|
866
|
return %hash;
|
867
|
}
|
868
|
|
869
|
sub getZapParams {
|
870
|
my %phash = ();
|
871
|
if (defined($zlineupId) || defined($zipcode)) {
|
872
|
$postalcode = $zipcode;
|
873
|
$country = "USA";
|
874
|
$country = "CAN" if ($zipcode =~ /[A-z]/);
|
875
|
if ($zlineupId =~ /:/) {
|
876
|
($lineupId, $device) = split(/:/, $zlineupId);
|
877
|
} else {
|
878
|
$lineupId = $zlineupId;
|
879
|
$device = "-";
|
880
|
}
|
881
|
$phash{postalCode} = $postalcode;
|
882
|
} else {
|
883
|
$phash{token} = &getZToken();
|
884
|
}
|
885
|
$phash{lineupId} = "$country-$lineupId-DEFAULT";
|
886
|
$phash{postalCode} = $postalcode;
|
887
|
$phash{countryCode} = $country;
|
888
|
$phash{headendId} = $lineupId;
|
889
|
$phash{device} = $device;
|
890
|
$phash{aid} = 'gapzap';
|
891
|
return %phash;
|
892
|
}
|
893
|
|
894
|
sub login {
|
895
|
if (!defined($userEmail) || $userEmail eq '' || !defined($password) || $password eq '') {
|
896
|
if (!defined($zlineupId)) {
|
897
|
die "Unable to login: Unspecified username or password.\n"
|
898
|
}
|
899
|
}
|
900
|
|
901
|
if (!defined($ua)) {
|
902
|
$ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); # WIN
|
903
|
$ua->cookie_jar(HTTP::Cookies->new);
|
904
|
$ua->proxy(['http', 'https'], $proxy) if defined($proxy);
|
905
|
$ua->agent('Mozilla/4.0');
|
906
|
$ua->default_headers->push_header('Accept-Encoding' => 'gzip, deflate');
|
907
|
}
|
908
|
|
909
|
if ($userEmail ne '' && $password ne '') {
|
910
|
&pout("Logging in as \"$userEmail\" (" . localtime . ")\n");
|
911
|
if (defined($options{z})) {
|
912
|
&loginTVG();
|
913
|
} else {
|
914
|
&loginZAP();
|
915
|
}
|
916
|
} else {
|
917
|
&pout("Connecting with lineupId \"$zlineupId\" (" . localtime . ")\n");
|
918
|
}
|
919
|
}
|
920
|
|
921
|
sub getURL {
|
922
|
my $url = shift;
|
923
|
&login() if !defined($ua);
|
924
|
|
925
|
my $rc = 0;
|
926
|
while ($rc++ < $retries) {
|
927
|
&pout("[$treq] Getting: $url\n");
|
928
|
sleep $sleeptime; # do these rapid requests flood servers?
|
929
|
$treq++;
|
930
|
my $r = $ua->get($url);
|
931
|
my $cl = length($r->content);
|
932
|
$tb += $cl;
|
933
|
my $dc = $r->decoded_content( raise_error => 1 );
|
934
|
if ($r->is_success && $cl) {
|
935
|
return $dc;
|
936
|
} elsif ($r->code == 400 && $dc =~ /Invalid time stamp passed/) {
|
937
|
&pout("$dc\n");
|
938
|
&pout("Date not in range (reached zap2it limit), normal exit.\n");
|
939
|
return "";
|
940
|
} elsif ($r->code == 500 && $dc =~ /Could not load details/) {
|
941
|
&pout("$dc\n");
|
942
|
return "";
|
943
|
} else {
|
944
|
&perr("[Attempt $rc] $cl:" . $r->status_line . "\n");
|
945
|
sleep ($sleeptime + 2);
|
946
|
}
|
947
|
}
|
948
|
die "Failed to download within $retries retries.\n";
|
949
|
}
|
950
|
|
951
|
sub wbf {
|
952
|
my($f, $s) = @_;
|
953
|
open(FO, ">$f") or die "Failed to open '$f': $!";
|
954
|
binmode(FO);
|
955
|
print FO $s;
|
956
|
close(FO);
|
957
|
}
|
958
|
|
959
|
sub unf {
|
960
|
my $f = shift;
|
961
|
unlink($f) or &perr("Failed to delete '$f': $!");
|
962
|
}
|
963
|
|
964
|
sub copyLogo {
|
965
|
my $key = shift;
|
966
|
if (defined($iconDir) && defined($stations{$key}{logo})) {
|
967
|
my $num = $stations{$key}{number};
|
968
|
my $src = "$iconDir/" . $stations{$key}{logo} . $stations{$key}{logoExt};
|
969
|
my $dest1 = "$iconDir/$num" . $stations{$key}{logoExt};
|
970
|
my $dest2 = "$iconDir/$num " . $stations{$key}{name} . $stations{$key}{logoExt};
|
971
|
my $dest3 = "$iconDir/$num\_" . $stations{$key}{name} . $stations{$key}{logoExt};
|
972
|
copy($src, $dest1);
|
973
|
copy($src, $dest2);
|
974
|
copy($src, $dest3);
|
975
|
}
|
976
|
}
|
977
|
|
978
|
sub handleLogo {
|
979
|
my $url = shift;
|
980
|
if (! -d $iconDir) {
|
981
|
mkdir($iconDir) or die "Can't mkdir: $!\n";
|
982
|
}
|
983
|
my $n; my $s; ($n,$_,$s) = fileparse($url, qr"\..*");
|
984
|
$stations{$cs}{logo} = $n;
|
985
|
$stations{$cs}{logoExt} = $s;
|
986
|
$stations{$cs}{logoURL} = $url;
|
987
|
my $f = $iconDir . "/" . $n . $s;
|
988
|
if (! -e $f) { &wbf($f, &getURL($url)); }
|
989
|
}
|
990
|
|
991
|
sub setOriginalAirDate {
|
992
|
if (substr($cp,10,4) ne '0000') {
|
993
|
if (!defined($programs{$cp}{originalAirDate})
|
994
|
|| ($schedule{$cs}{$sch}{time} < $programs{$cp}{originalAirDate})) {
|
995
|
$programs{$cp}{originalAirDate} = $schedule{$cs}{$sch}{time};
|
996
|
}
|
997
|
}
|
998
|
}
|
999
|
|
1000
|
sub getZToken {
|
1001
|
&login() if (!defined($zapToken));
|
1002
|
return $zapToken;
|
1003
|
}
|
1004
|
|
1005
|
sub parseZFavs {
|
1006
|
my $buffer = shift;
|
1007
|
my $t = decode_json($buffer);
|
1008
|
if (defined($t->{'channels'})) {
|
1009
|
my $m = $t->{'channels'};
|
1010
|
foreach my $f (@{$m}) {
|
1011
|
if ($options{R}) {
|
1012
|
my $r = $ua->post($urlRoot . "api/user/ChannelAddtofav", { token => $zapToken, prgsvcid => $f, addToFav => "false" }, 'X-Requested-With' => 'XMLHttpRequest');
|
1013
|
if ($r->is_success) {
|
1014
|
&pout("Removed favorite $f\n");
|
1015
|
} else {
|
1016
|
&perr("RF" . $r->status_line . "\n");
|
1017
|
}
|
1018
|
} else {
|
1019
|
$zapFavorites{$f} = 1;
|
1020
|
}
|
1021
|
}
|
1022
|
if ($options{R}) {
|
1023
|
&pout("Removed favorites, exiting\n");
|
1024
|
exit;
|
1025
|
};
|
1026
|
&pout("Lineup favorites: " . (keys %zapFavorites) . "\n");
|
1027
|
}
|
1028
|
}
|
1029
|
|
1030
|
sub parseTVGFavs {
|
1031
|
my $buffer = shift;
|
1032
|
my $t = decode_json($buffer);
|
1033
|
if (defined($t->{'message'})) {
|
1034
|
my $m = $t->{'message'};
|
1035
|
foreach my $f (@{$m}) {
|
1036
|
my $source = $f->{"source"};
|
1037
|
my $channel = $f->{"channel"};
|
1038
|
$tvgfavs{"$channel.$source"} = 1;
|
1039
|
}
|
1040
|
&pout("Lineup $zlineupId favorites: " . (keys %tvgfavs) . "\n");
|
1041
|
}
|
1042
|
}
|
1043
|
|
1044
|
sub parseTVGIcons {
|
1045
|
require GD;
|
1046
|
$rc = Encode::encode('utf8', &getURL($tvgspritesurl . "$zlineupId\.css") );
|
1047
|
if ($rc =~ /background-image:.+?url\((.+?)\)/) {
|
1048
|
my $url = $tvgspritesurl . $1;
|
1049
|
|
1050
|
if (! -d $iconDir) {
|
1051
|
mkdir($iconDir) or die "Can't mkdir: $!\n";
|
1052
|
}
|
1053
|
|
1054
|
($n,$_,$s) = fileparse($url, qr"\..*");
|
1055
|
$f = $iconDir . "/sprites-" . $n . $s;
|
1056
|
&wbf($f, &getURL($url));
|
1057
|
|
1058
|
GD::Image->trueColor(1);
|
1059
|
$im = new GD::Image->new($f);
|
1060
|
|
1061
|
my $iconw = 30;
|
1062
|
my $iconh = 20;
|
1063
|
while ($rc =~ /listings-channel-icon-(.+?)\{.+?position:.*?\-(\d+).+?(\d+).*?\}/isg) {
|
1064
|
my $cid = $1;
|
1065
|
my $iconx = $2;
|
1066
|
my $icony = $3;
|
1067
|
|
1068
|
my $icon = new GD::Image($iconw,$iconh);
|
1069
|
$icon->alphaBlending(0);
|
1070
|
$icon->saveAlpha(1);
|
1071
|
$icon->copy($im, 0, 0, $iconx, $icony, $iconw, $iconh);
|
1072
|
|
1073
|
$stations{$cid}{logo} = "sprite-" . $cid;
|
1074
|
$stations{$cid}{logoExt} = $s;
|
1075
|
|
1076
|
my $ifn = $iconDir . "/" . $stations{$cid}{logo} . $stations{$cid}{logoExt};
|
1077
|
&wbf($ifn, $icon->png);
|
1078
|
}
|
1079
|
}
|
1080
|
}
|
1081
|
|
1082
|
sub parseTVGD {
|
1083
|
my $gz = gzopen(shift, "rb");
|
1084
|
my $buffer;
|
1085
|
$buffer .= $b while $gz->gzread($b, 65535) > 0;
|
1086
|
$gz->gzclose();
|
1087
|
my $t = decode_json($buffer);
|
1088
|
|
1089
|
if (defined($t->{'program'})) {
|
1090
|
my $prog = $t->{'program'};
|
1091
|
if (defined($prog->{'release_year'})) {
|
1092
|
$programs{$cp}{movie_year} = $prog->{'release_year'};
|
1093
|
}
|
1094
|
if (defined($prog->{'rating'}) && !defined($programs{$cp}{rating})) {
|
1095
|
$programs{$cp}{rating} = $prog->{'rating'} if $prog->{'rating'} ne 'NR';
|
1096
|
}
|
1097
|
}
|
1098
|
|
1099
|
if (defined($t->{'tvobject'})) {
|
1100
|
my $tvo = $t->{'tvobject'};
|
1101
|
if (defined($tvo->{'photos'})) {
|
1102
|
my $photos = $tvo->{'photos'};
|
1103
|
my %phash;
|
1104
|
foreach $ph (@{$photos}) {
|
1105
|
my $w = $ph->{'width'} * $ph->{'height'};
|
1106
|
my $u = $ph->{'url'};
|
1107
|
$phash{$w} = $u;
|
1108
|
}
|
1109
|
my $big = (sort {$b <=> $a} keys %phash)[0];
|
1110
|
$programs{$cp}{imageUrl} = $phash{$big};
|
1111
|
}
|
1112
|
}
|
1113
|
}
|
1114
|
|
1115
|
sub parseTVGGrid {
|
1116
|
my $gz = gzopen(shift, "rb");
|
1117
|
my $buffer;
|
1118
|
$buffer .= $b while $gz->gzread($b, 65535) > 0;
|
1119
|
$gz->gzclose();
|
1120
|
my $t = decode_json($buffer);
|
1121
|
|
1122
|
foreach my $e (@{$t}) {
|
1123
|
my $cjs = $e->{'Channel'};
|
1124
|
my $src = $cjs->{'SourceId'};
|
1125
|
my $num = $cjs->{'Number'};
|
1126
|
|
1127
|
$cs = "$num.$src";
|
1128
|
|
1129
|
if (%tvgfavs) {
|
1130
|
next if (!$tvgfavs{$cs});
|
1131
|
}
|
1132
|
|
1133
|
if (!defined($stations{$cs}{stnNum})) {
|
1134
|
$stations{$cs}{stnNum} = $src;
|
1135
|
$stations{$cs}{number} = $num;
|
1136
|
$stations{$cs}{name} = $cjs->{'Name'};
|
1137
|
if (defined($cjs->{'FullName'}) && $cjs->{'FullName'} ne $cjs->{'Name'}) {
|
1138
|
if ($cjs->{'FullName'} ne '') {
|
1139
|
$stations{$cs}{fullname} = $cjs->{'FullName'};
|
1140
|
}
|
1141
|
}
|
1142
|
|
1143
|
if (!defined($stations{$cs}{order})) {
|
1144
|
if (defined($options{b})) {
|
1145
|
$stations{$cs}{order} = $coNum++;
|
1146
|
} else {
|
1147
|
$stations{$cs}{order} = $stations{$cs}{number};
|
1148
|
}
|
1149
|
}
|
1150
|
}
|
1151
|
|
1152
|
my $cps = $e->{'ProgramSchedules'};
|
1153
|
foreach my $pe (@{$cps}) {
|
1154
|
next if (!defined($pe->{'ProgramId'}));
|
1155
|
$cp = $pe->{'ProgramId'};
|
1156
|
my $catid = $pe->{'CatId'};
|
1157
|
|
1158
|
if ($catid == 1) { $programs{$cp}{genres}{movie} = 1 }
|
1159
|
elsif ($catid == 2) { $programs{$cp}{genres}{sports} = 1 }
|
1160
|
elsif ($catid == 3) { $programs{$cp}{genres}{family} = 1 }
|
1161
|
elsif ($catid == 4) { $programs{$cp}{genres}{news} = 1 }
|
1162
|
# 5 - 10?
|
1163
|
# my $subcatid = $pe->{'SubCatId'};
|
1164
|
|
1165
|
my $ppid = $pe->{'ParentProgramId'};
|
1166
|
if ((defined($ppid) && $ppid != 0)
|
1167
|
|| (defined($options{j}) && $catid != 1)) {
|
1168
|
$programs{$cp}{genres}{series} = 99;
|
1169
|
}
|
1170
|
|
1171
|
$programs{$cp}{title} = $pe->{'Title'};
|
1172
|
$tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
|
1173
|
|
1174
|
if (defined($pe->{'EpisodeTitle'}) && $pe->{'EpisodeTitle'} ne '') {
|
1175
|
$programs{$cp}{episode} = $pe->{'EpisodeTitle'};
|
1176
|
$tba = 1 if $programs{$cp}{episode} =~ /$sTBA/i;
|
1177
|
}
|
1178
|
|
1179
|
$programs{$cp}{description} = $pe->{'CopyText'} if defined($pe->{'CopyText'}) && $pe->{'CopyText'} ne '';
|
1180
|
$programs{$cp}{rating} = $pe->{'Rating'} if defined($pe->{'Rating'}) && $pe->{'Rating'} ne '';
|
1181
|
|
1182
|
my $sch = $pe->{'StartTime'} * 1000;
|
1183
|
$schedule{$cs}{$sch}{time} = $sch;
|
1184
|
$schedule{$cs}{$sch}{endtime} = $pe->{'EndTime'} * 1000;
|
1185
|
$schedule{$cs}{$sch}{program} = $cp;
|
1186
|
$schedule{$cs}{$sch}{station} = $cs;
|
1187
|
|
1188
|
my $airat = $pe->{'AiringAttrib'};
|
1189
|
if ($airat & 1) { $schedule{$cs}{$sch}{live} = 1 }
|
1190
|
elsif ($airat & 4) { $schedule{$cs}{$sch}{new} = 1 }
|
1191
|
# other bits?
|
1192
|
|
1193
|
my $tvo = $pe->{'TVObject'};
|
1194
|
if (defined($tvo)) {
|
1195
|
if (defined($tvo->{'SeasonNumber'}) && $tvo->{'SeasonNumber'} != 0) {
|
1196
|
$programs{$cp}{seasonNum} = $tvo->{'SeasonNumber'};
|
1197
|
if (defined($tvo->{'EpisodeNumber'}) && $tvo->{'EpisodeNumber'} != 0) {
|
1198
|
$programs{$cp}{episodeNum} = $tvo->{'EpisodeNumber'};
|
1199
|
}
|
1200
|
}
|
1201
|
if (defined($tvo->{'EpisodeAirDate'})) {
|
1202
|
my $eaid = $tvo->{'EpisodeAirDate'}; # GMT @ 00:00:00
|
1203
|
$eaid =~ tr/\-0-9//cd;
|
1204
|
$programs{$cp}{originalAirDate} = $eaid if ($eaid ne '');
|
1205
|
}
|
1206
|
my $url;
|
1207
|
if (defined($tvo->{'EpisodeSEOUrl'}) && $tvo->{'EpisodeSEOUrl'} ne '') {
|
1208
|
$url = $tvo->{'EpisodeSEOUrl'};
|
1209
|
} elsif(defined($tvo->{'SEOUrl'}) && $tvo->{'SEOUrl'} ne '') {
|
1210
|
$url = $tvo->{'SEOUrl'};
|
1211
|
$url = "/movies$url" if ($catid == 1 && $url !~ /movies/);
|
1212
|
}
|
1213
|
$programs{$cp}{url} = substr($tvgurl, 0, -1) . $url if defined($url);
|
1214
|
}
|
1215
|
|
1216
|
if (defined($options{I})
|
1217
|
|| (defined($options{D}) && $programs{$cp}{genres}{movie})
|
1218
|
|| (defined($options{W}) && $programs{$cp}{genres}{movie}) ) {
|
1219
|
&getDetails(\&parseTVGD, $cp, $tvgMapiRoot . "listings/details?program=$cp", "");
|
1220
|
}
|
1221
|
}
|
1222
|
}
|
1223
|
}
|
1224
|
|
1225
|
sub getDetails {
|
1226
|
my ($func, $cp, $url, $prefix) = @_;
|
1227
|
my $fn = "$cacheDir/$prefix$cp\.js\.gz";
|
1228
|
if (! -e $fn) {
|
1229
|
my $rs = &getURL($url);
|
1230
|
if (length($rs)) {
|
1231
|
$rc = Encode::encode('utf8', $rs);
|
1232
|
&wbf($fn, Compress::Zlib::memGzip($rc));
|
1233
|
}
|
1234
|
}
|
1235
|
if (-e $fn) {
|
1236
|
my $l = length($prefix) ? $prefix : "D";
|
1237
|
&pout("[$l] Parsing: $cp\n");
|
1238
|
$func->($fn);
|
1239
|
} else {
|
1240
|
&pout("Skipping: $cp\n");
|
1241
|
}
|
1242
|
}
|
1243
|
|
1244
|
sub parseJSON {
|
1245
|
my $gz = gzopen(shift, "rb");
|
1246
|
my $buffer;
|
1247
|
$buffer .= $b while $gz->gzread($b, 65535) > 0;
|
1248
|
$gz->gzclose();
|
1249
|
my $t = decode_json($buffer);
|
1250
|
|
1251
|
my $sts = $t->{'channels'};
|
1252
|
my %zapStarred=();
|
1253
|
foreach $s (@$sts) {
|
1254
|
|
1255
|
if (defined($s->{'channelId'})) {
|
1256
|
if (!$allChan && scalar(keys %zapFavorites)) {
|
1257
|
if ($zapFavorites{$s->{channelId}}) {
|
1258
|
if ($options{8}) {
|
1259
|
next if $zapStarred{$s->{channelId}};
|
1260
|
$zapStarred{$s->{channelId}} = 1;
|
1261
|
}
|
1262
|
} else {
|
1263
|
next;
|
1264
|
}
|
1265
|
}
|
1266
|
# id (uniq) vs channelId, but id not nec consistent in cache
|
1267
|
$cs = $s->{channelNo} . "." . $s->{channelId};
|
1268
|
$stations{$cs}{stnNum} = $s->{channelId};
|
1269
|
$stations{$cs}{name} = $s->{'callSign'};
|
1270
|
$stations{$cs}{number} = $s->{'channelNo'};
|
1271
|
$stations{$cs}{number} =~ s/^0+//g;
|
1272
|
|
1273
|
if (!defined($stations{$cs}{order})) {
|
1274
|
if (defined($options{b})) {
|
1275
|
$stations{$cs}{order} = $coNum++;
|
1276
|
} else {
|
1277
|
$stations{$cs}{order} = $stations{$cs}{number};
|
1278
|
}
|
1279
|
}
|
1280
|
|
1281
|
if ($s->{'thumbnail'} ne '') {
|
1282
|
my $url = $s->{'thumbnail'};
|
1283
|
$url =~ s/\?.*//; # remove size
|
1284
|
if ($url !~ /^http/) {
|
1285
|
$url = "https:" . $url;
|
1286
|
}
|
1287
|
$stations{$cs}{logoURL} = $url;
|
1288
|
&handleLogo($url) if defined($iconDir);
|
1289
|
}
|
1290
|
|
1291
|
my $events = $s->{'events'};
|
1292
|
foreach $e (@$events) {
|
1293
|
my $program = $e->{'program'};
|
1294
|
$cp = $program->{'id'};
|
1295
|
$programs{$cp}{title} = $program->{'title'};
|
1296
|
$tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
|
1297
|
$programs{$cp}{episode} = $program->{'episodeTitle'} if ($program->{'episodeTitle'} ne '');
|
1298
|
$programs{$cp}{description} = $program->{'shortDesc'} if ($program->{'shortDesc'} ne '');
|
1299
|
$programs{$cp}{duration} = $e->{duration} if ($e->{duration} > 0);
|
1300
|
$programs{$cp}{movie_year} = $program->{releaseYear} if ($program->{releaseYear} ne '');
|
1301
|
$programs{$cp}{seasonNum} = $program->{season} if ($program->{'season'} ne '');
|
1302
|
if ($program->{'episode'} ne '') {
|
1303
|
$programs{$cp}{episodeNum} = $program->{episode};
|
1304
|
}
|
1305
|
if ($e->{'thumbnail'} ne '') {
|
1306
|
my $turl = $urlAssets;
|
1307
|
$turl .= $e->{'thumbnail'} . ".jpg";
|
1308
|
$programs{$cp}{imageUrl} = $turl;
|
1309
|
}
|
1310
|
if ($program->{'seriesId'} ne '' && $program->{'tmsId'} ne '') {
|
1311
|
$programs{$cp}{url} = $urlRoot . "/overview.html?programSeriesId="
|
1312
|
. $program->{seriesId} . "&tmsId=" . $program->{tmsId};
|
1313
|
}
|
1314
|
|
1315
|
$sch = str2time1($e->{'startTime'}) * 1000;
|
1316
|
$schedule{$cs}{$sch}{time} = $sch;
|
1317
|
$schedule{$cs}{$sch}{endTime} = str2time1($e->{'endTime'}) * 1000;
|
1318
|
$schedule{$cs}{$sch}{program} = $cp;
|
1319
|
$schedule{$cs}{$sch}{station} = $cs;
|
1320
|
|
1321
|
if ($e->{'filter'}) {
|
1322
|
my $genres = $e->{'filter'};
|
1323
|
my $i = 1;
|
1324
|
foreach $g (@{$genres}) {
|
1325
|
$g =~ s/filter-//i;
|
1326
|
${$programs{$cp}{genres}}{lc($g)} = $i++;
|
1327
|
}
|
1328
|
}
|
1329
|
|
1330
|
$programs{$cp}{rating} = $e->{rating} if ($e->{rating} ne '');
|
1331
|
|
1332
|
if ($e->{'tags'}) {
|
1333
|
my $tags = $e->{'tags'};
|
1334
|
if (grep $_ eq 'CC', @{$tags}) {
|
1335
|
$schedule{$cs}{$sch}{cc} = 1
|
1336
|
}
|
1337
|
}
|
1338
|
|
1339
|
if ($e->{'flag'}) {
|
1340
|
my $flags = $e->{'flag'};
|
1341
|
if (grep $_ eq 'New', @{$flags}) {
|
1342
|
$schedule{$cs}{$sch}{new} = 'New'
|
1343
|
&setOriginalAirDate();
|
1344
|
}
|
1345
|
if (grep $_ eq 'Live', @{$flags}) {
|
1346
|
$schedule{$cs}{$sch}{live} = 'Live'
|
1347
|
&setOriginalAirDate(); # live to tape?
|
1348
|
}
|
1349
|
if (grep $_ eq 'Premiere', @{$flags}) {
|
1350
|
$schedule{$cs}{$sch}{premiere} = 'Premiere';
|
1351
|
}
|
1352
|
if (grep $_ eq 'Finale', @{$flags}) {
|
1353
|
$schedule{$cs}{$sch}{finale} = 'Finale';
|
1354
|
}
|
1355
|
}
|
1356
|
|
1357
|
if ($options{D} && !$program->{isGeneric}) {
|
1358
|
&postJSONO($cp, $program->{seriesId});
|
1359
|
}
|
1360
|
if (defined($options{j}) && $cp !~ /^MV/) {
|
1361
|
$programs{$cp}{genres}{series} = 99;
|
1362
|
}
|
1363
|
}
|
1364
|
}
|
1365
|
}
|
1366
|
return 0;
|
1367
|
}
|
1368
|
|
1369
|
sub postJSONO {
|
1370
|
my ($cp, $sid) = @_;
|
1371
|
my $fn = "$cacheDir/O$cp\.js\.gz";
|
1372
|
|
1373
|
if (! -e $fn && defined($sidCache{$sid}) && -e $sidCache{$sid}) {
|
1374
|
copy($sidCache{$sid}, $fn);
|
1375
|
}
|
1376
|
if (! -e $fn) {
|
1377
|
my $url = $urlRoot . 'api/program/overviewDetails';
|
1378
|
&pout("[$treq] Post $sid: $url\n");
|
1379
|
sleep $sleeptime; # do these rapid requests flood servers?
|
1380
|
$treq++;
|
1381
|
my %phash = &getZapPParams();
|
1382
|
$phash{programSeriesID} = $sid;
|
1383
|
$phash{'clickstream[FromPage]'} = 'TV%20Grid';
|
1384
|
my $r = $ua->post($url, \%phash, 'X-Requested-With' => 'XMLHttpRequest');
|
1385
|
my $cl = length($r->content);
|
1386
|
$tb += $cl;
|
1387
|
if ($r->is_success) {
|
1388
|
$dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
|
1389
|
&wbf($fn, Compress::Zlib::memGzip($dc));
|
1390
|
$sidCache{$sid} = $fn;
|
1391
|
} else {
|
1392
|
&perr($id . " :" . $r->status_line);
|
1393
|
}
|
1394
|
}
|
1395
|
if (-e $fn) {
|
1396
|
&pout("[D] Parsing: $cp\n");
|
1397
|
my $gz = gzopen($fn, "rb");
|
1398
|
my $buffer;
|
1399
|
$buffer .= $b while $gz->gzread($b, 65535) > 0;
|
1400
|
$gz->gzclose();
|
1401
|
my $t = decode_json($buffer);
|
1402
|
|
1403
|
if ($t->{seriesGenres} ne '') {
|
1404
|
my $i = 2;
|
1405
|
my %gh = %{$programs{$cp}{genres}};
|
1406
|
if (keys %gh) {
|
1407
|
my @genArr = sort { $gh{$a} <=> $gh{$b} } keys %gh;
|
1408
|
my $max = $genArr[-1];
|
1409
|
$i = $gh{$max} + 1;
|
1410
|
}
|
1411
|
foreach my $sg (split(/\|/, lc($t->{seriesGenres}))) {
|
1412
|
if (!${$programs{$cp}{genres}}{$sg}) {
|
1413
|
${$programs{$cp}{genres}}{$sg} = $i++;
|
1414
|
}
|
1415
|
}
|
1416
|
}
|
1417
|
|
1418
|
my $i = 1;
|
1419
|
foreach my $c (@{$t->{'overviewTab'}->{cast}}) {
|
1420
|
my $n = $c->{name};
|
1421
|
my $cn = $c->{characterName};
|
1422
|
my $cr = lc($c->{role});
|
1423
|
|
1424
|
if ($cr eq 'host') {
|
1425
|
${$programs{$cp}{presenter}}{$n} = $i++;
|
1426
|
} else {
|
1427
|
${$programs{$cp}{actor}}{$n} = $i++;
|
1428
|
${$programs{$cp}{role}}{$n} = $cn if length($cn);
|
1429
|
}
|
1430
|
}
|
1431
|
$i = 1;
|
1432
|
foreach my $c (@{$t->{'overviewTab'}->{crew}}) {
|
1433
|
my $n = $c->{name};
|
1434
|
my $cr = lc($c->{role});
|
1435
|
if ($cr =~ /producer/) {
|
1436
|
${$programs{$cp}{producer}}{$n} = $i++;
|
1437
|
} elsif ($cr =~ /director/) {
|
1438
|
${$programs{$cp}{director}}{$n} = $i++;
|
1439
|
} elsif ($cr =~ /writer/) {
|
1440
|
${$programs{$cp}{writer}}{$n} = $i++;
|
1441
|
}
|
1442
|
}
|
1443
|
if (!defined($programs{$cp}{imageUrl}) && $t->{seriesImage} ne '') {
|
1444
|
my $turl = $urlAssets;
|
1445
|
$turl .= $t->{seriesImage} . ".jpg";
|
1446
|
$programs{$cp}{imageUrl} = $turl;
|
1447
|
}
|
1448
|
if ($cp =~ /^MV|^SH/ && length($t->{seriesDescription}) > length($programs{$cp}{description})) {
|
1449
|
$programs{$cp}{description} = $t->{seriesDescription};
|
1450
|
}
|
1451
|
if ($cp =~ /^EP/) { # GMT @ 00:00:00
|
1452
|
my $ue = $t->{overviewTab}->{upcomingEpisode};
|
1453
|
if (defined($ue) && lc($ue->{tmsID}) eq lc($cp)
|
1454
|
&& $ue->{originalAirDate} ne ''
|
1455
|
&& $ue->{originalAirDate} ne '1000-01-01T00:00Z') {
|
1456
|
$oad = str2time2($ue->{originalAirDate}) ;
|
1457
|
$oad *= 1000;
|
1458
|
$programs{$cp}{originalAirDate} = $oad;
|
1459
|
} else {
|
1460
|
foreach my $ue (@{$t->{upcomingEpisodeTab}}) {
|
1461
|
if (lc($ue->{tmsID}) eq lc($cp)
|
1462
|
&& $ue->{originalAirDate} ne ''
|
1463
|
&& $ue->{originalAirDate} ne '1000-01-01T00:00Z'
|
1464
|
) {
|
1465
|
$oad = str2time2($ue->{originalAirDate}) ;
|
1466
|
$oad *= 1000;
|
1467
|
$programs{$cp}{originalAirDate} = $oad;
|
1468
|
last;
|
1469
|
}
|
1470
|
}
|
1471
|
}
|
1472
|
}
|
1473
|
} else {
|
1474
|
&pout("Skipping: $sid\n");
|
1475
|
}
|
1476
|
}
|
1477
|
|
1478
|
sub str2time1 {
|
1479
|
my $t = Time::Piece->strptime(shift, '%Y-%m-%dT%H:%M:%SZ');
|
1480
|
return $t->epoch();
|
1481
|
}
|
1482
|
|
1483
|
sub str2time2 {
|
1484
|
my $t = Time::Piece->strptime(shift, '%Y-%m-%dT%H:%MZ');
|
1485
|
return $t->epoch();
|
1486
|
}
|
1487
|
|
1488
|
sub hourToMillis {
|
1489
|
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
|
1490
|
if ($start == 0) {
|
1491
|
$hour = int($hour/$gridHours) * $gridHours;
|
1492
|
} else {
|
1493
|
$hour = 0;
|
1494
|
}
|
1495
|
$t = timegm(0,0,$hour,$mday,$mon,$year);
|
1496
|
$t = $t - (&tz_offset * 3600) if !defined($options{g});
|
1497
|
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t);
|
1498
|
$t = timegm($sec, $min, $hour,$mday,$mon,$year);
|
1499
|
return $t . "000";
|
1500
|
}
|
1501
|
|
1502
|
sub tz_offset {
|
1503
|
my $n = defined $_[0] ? $_[0] : time;
|
1504
|
my ($lm, $lh, $ly, $lyd) = (localtime $n)[1, 2, 5, 7];
|
1505
|
my ($gm, $gh, $gy, $gyd) = (gmtime $n)[1, 2, 5, 7];
|
1506
|
($lm - $gm)/60 + $lh - $gh + 24 * ($ly - $gy || $lyd - $gyd)
|
1507
|
}
|
1508
|
|
1509
|
sub timezone {
|
1510
|
my $tztime = defined $_[0] ? &_rtrim3(shift) : time;
|
1511
|
my $os = sprintf "%.1f", (timegm(localtime($tztime)) - $tztime) / 3600;
|
1512
|
my $mins = sprintf "%02d", abs( $os - int($os) ) * 60;
|
1513
|
return sprintf("%+03d", int($os)) . $mins;
|
1514
|
}
|
1515
|
|
1516
|
sub max ($$) { $_[$_[0] < $_[1]] }
|
1517
|
sub min ($$) { $_[$_[0] > $_[1]] }
|
1518
|
|
1519
|
sub HELP_MESSAGE {
|
1520
|
print <<END;
|
1521
|
zap2xml <zap2xml\@gmail.com> (2018-01-12)
|
1522
|
-u <username>
|
1523
|
-p <password>
|
1524
|
-d <# of days> (default = $days)
|
1525
|
-n <# of no-cache days> (from end) (default = $ncdays)
|
1526
|
-N <# of no-cache days> (from start) (default = $ncsdays)
|
1527
|
-B <no-cache day>
|
1528
|
-s <start day offset> (default = $start)
|
1529
|
-o <output xml filename> (default = "$outFile")
|
1530
|
-c <cacheDirectory> (default = "$cacheDir")
|
1531
|
-l <lang> (default = "$lang")
|
1532
|
-i <iconDirectory> (default = don't download channel icons)
|
1533
|
-m <#> = offset program times by # minutes (better to use TZ env var)
|
1534
|
-b = retain website channel order
|
1535
|
-x = output XTVD xml file format (default = XMLTV)
|
1536
|
-w = wait on exit (require keypress before exiting)
|
1537
|
-q = quiet (no status output)
|
1538
|
-r <# of connection retries before failure> (default = $retries, max 20)
|
1539
|
-e = hex encode entities (html special characters like accents)
|
1540
|
-E "amp apos quot lt gt" = selectively encode standard XML entities
|
1541
|
-F = output channel names first (rather than "number name")
|
1542
|
-O = use old tv_grab_na style channel ids (C###nnnn.zap2it.com)
|
1543
|
-A "new live" = append " *" to program titles that are "new" and/or "live"
|
1544
|
-M = copy movie_year to empty movie sub-title tags
|
1545
|
-U = UTF-8 encoding (default = "ISO-8859-1")
|
1546
|
-L = output "<live />" tag (not part of xmltv.dtd)
|
1547
|
-T = don't cache files containing programs with "$sTBA" titles
|
1548
|
-P <http://proxyhost:port> = to use an http proxy
|
1549
|
-C <configuration file> (default = "$confFile")
|
1550
|
-S <#seconds> = sleep between requests to prevent flooding of server
|
1551
|
-D = include details = 1 extra http request per program!
|
1552
|
-I = include icons (image URLs) - 1 extra http request per program!
|
1553
|
-J <xmltv> = include xmltv file in output
|
1554
|
-Y <lineupId> (if not using username/password)
|
1555
|
-Z <zipcode> (if not using username/password)
|
1556
|
-z = use tvguide.com instead of zap2it.com
|
1557
|
-a = output all channels (not just favorites)
|
1558
|
-j = add "series" category to all non-movie programs
|
1559
|
END
|
1560
|
sleep(5) if ($^O eq 'MSWin32');
|
1561
|
exit 0;
|
1562
|
}
|