1
|
#!/usr/bin/perl
|
2
|
my $Version = 'get_eit v0.01';
|
3
|
|
4
|
# Originally from myth_update_events, part of the package for myth_scanner
|
5
|
#
|
6
|
# Borrowed and heavily modified by me, Norm D.
|
7
|
|
8
|
sub usage
|
9
|
{
|
10
|
die <<"EndUsage";
|
11
|
$Version
|
12
|
|
13
|
Usage:
|
14
|
-adapter <n> : Use adapter number <num>.
|
15
|
-pid <n> : Read EIT PID <n>.
|
16
|
-nice : Process events more slowly.
|
17
|
-ignore_versions : Don't track nor use event versions numbers.
|
18
|
-verbose : Spit out a whole lot of stuff.
|
19
|
-debug : Place in debug mode - caches dvb table information for faster runs.
|
20
|
|
21
|
EndUsage
|
22
|
}
|
23
|
|
24
|
use Getopt::Long;
|
25
|
use Carp qw(cluck);
|
26
|
use Time::Local;
|
27
|
use Time::HiRes qw( usleep );
|
28
|
use List::MoreUtils 'true';
|
29
|
use IO::Handle;
|
30
|
use IO::File;
|
31
|
use IO::Select;
|
32
|
use DBI;
|
33
|
use Data::Dumper;
|
34
|
use Encode;
|
35
|
use POSIX;
|
36
|
use strict;
|
37
|
|
38
|
use Linux::DVB;
|
39
|
use SI::Parse 0.32;
|
40
|
|
41
|
my $VERBOSE;
|
42
|
my %THEMECACHE;
|
43
|
|
44
|
use constant {
|
45
|
DMX_READ_BUF_SIZE => (32 * 1024),
|
46
|
TRUE => 1,
|
47
|
FALSE => 0,
|
48
|
|
49
|
# From libmyth/mythcontext.h
|
50
|
LP_EMERG => 0,
|
51
|
LP_ALERT => 1,
|
52
|
LP_CRITICAL => 2,
|
53
|
LP_ERROR => 3,
|
54
|
LP_WARNING => 4,
|
55
|
LP_NOTICE => 5,
|
56
|
LP_INFO => 6,
|
57
|
LP_DEBUG => 7,
|
58
|
};
|
59
|
|
60
|
# Debug stuff
|
61
|
my $_DEBUG_;
|
62
|
my $_TRANSCACHE_ = '/tmp/transports.dbg';
|
63
|
my $_EITCACHE_ = '/tmp/eit.dbg';
|
64
|
my $_EITRAW_ = '/tmp/eitraw.dbg';
|
65
|
my $_CHANCACHE_ = '/tmp/channels.dbg';
|
66
|
my $_MISSING_CATS_LOG_ = '/tmp/events_missing_categories.log';
|
67
|
|
68
|
# Timeout to get a complete table
|
69
|
my $TIMEOUT = 60 * 30; # PID 0x300 can take a while
|
70
|
|
71
|
# These are in order of preference.
|
72
|
my @EITPIDS = (SI::Parse::EIT_DISH9_PID,
|
73
|
SI::Parse::EIT_BEV9_PID,
|
74
|
SI::Parse::EIT_PID);
|
75
|
|
76
|
# Encoding overrides
|
77
|
my %ENCODINGS = (256 => 'iso-8859-1',
|
78
|
257 => 'iso-8859-1');
|
79
|
|
80
|
#These are the valid TID's for scanning channels on dish, which would be huge without this
|
81
|
my @valid_nids = 256, 257, 4098, 4101;
|
82
|
|
83
|
&main();
|
84
|
|
85
|
sub getArgs {
|
86
|
my $adapter;
|
87
|
my $iversions;
|
88
|
my $pid;
|
89
|
my $nice;
|
90
|
my $always;
|
91
|
my $log_missing_cats;
|
92
|
my $help;
|
93
|
my $xmlfile;
|
94
|
my $p = new Getopt::Long::Parser;
|
95
|
|
96
|
if (!$p->getoptions('adapter=i' => \$adapter,
|
97
|
'pid=s' => \$pid,
|
98
|
'nice' => \$nice,
|
99
|
'ignore_versions' => \$iversions,
|
100
|
'always' => \$always,
|
101
|
'log_missing_cats' => \$log_missing_cats,
|
102
|
'verbose' => \$VERBOSE,
|
103
|
'h' => \$help,
|
104
|
'xmlfile=s' => \$xmlfile,
|
105
|
'debug' => \$_DEBUG_)) {
|
106
|
print "Invalid parameters given.\n";
|
107
|
usage();
|
108
|
}
|
109
|
|
110
|
usage() if (defined($help));
|
111
|
|
112
|
$VERBOSE = TRUE if (defined($VERBOSE));
|
113
|
$_DEBUG_ = TRUE if (defined($_DEBUG_));
|
114
|
$nice = TRUE if (defined($nice));
|
115
|
$iversions = TRUE if (defined($iversions));
|
116
|
|
117
|
$pid = hex($pid) if ($pid =~ /^0x/);
|
118
|
$adapter = 0 if (!defined($adapter));
|
119
|
$xmlfile = 'xmltv.xml' if (!defined($xmlfile));
|
120
|
|
121
|
return ($adapter, $pid, $nice, $iversions, $always, $log_missing_cats, $xmlfile);
|
122
|
}
|
123
|
|
124
|
sub main {
|
125
|
my($adapter, $pid, $nice, $iversions, $always, $log_missing_cats, $xmlfile) = getArgs();
|
126
|
|
127
|
my $num_transports = 0;
|
128
|
my $num_channels = 0;
|
129
|
my $num_eit = 0;
|
130
|
|
131
|
STDOUT->autoflush(1);
|
132
|
|
133
|
foreach my $id (keys %SI::Parse::DISH_THEMES) {
|
134
|
$THEMECACHE{$SI::Parse::DISH_THEMES{$id}} = '';
|
135
|
}
|
136
|
|
137
|
# Add some additional acceptions for BEV
|
138
|
$THEMECACHE{'Series'} = 'Series/Special';
|
139
|
$THEMECACHE{"S\xe9ries"} = 'Series/Special';
|
140
|
$THEMECACHE{'News'} = 'News/Business';
|
141
|
$THEMECACHE{'Nouvelles'} = 'News/Business';
|
142
|
$THEMECACHE{'Film'} = 'Movie';
|
143
|
$THEMECACHE{'Music'} = 'Music/Art';
|
144
|
$THEMECACHE{'Children'} = 'Family/Children';
|
145
|
$THEMECACHE{'Enfants'} = 'Family/Children';
|
146
|
|
147
|
# createVersionTable();
|
148
|
my $versions;
|
149
|
|
150
|
# if (!$iversions) {
|
151
|
# print "\n-> Retrieving event version information: ";
|
152
|
# $versions = getVersions();
|
153
|
# print "done.\n";
|
154
|
# }
|
155
|
|
156
|
my $t_offset = 0;
|
157
|
|
158
|
preparefile($xmlfile);
|
159
|
|
160
|
print "\n-> Using time offset of $t_offset.\n";
|
161
|
|
162
|
print "\n-> Looking for network information: ";
|
163
|
my($nid, $name) = getNetwork($adapter);
|
164
|
print "done.\n";
|
165
|
print "\t-> Found network ID $nid ($name).\n";
|
166
|
|
167
|
if (!defined($pid)) {
|
168
|
print "\n-> Finding best EIT PID: ";
|
169
|
$pid = findBestPID($adapter);
|
170
|
print "$pid .... done.\n";
|
171
|
die("\n\nFATAL: Unable to find any available EIT PIDS, aborting!\n")
|
172
|
if (!defined($pid));
|
173
|
} else {
|
174
|
die("\n\nFATAL: Specified PID $pid is not available, aborting!\n")
|
175
|
if (!checkPID($adapter, $pid));
|
176
|
}
|
177
|
|
178
|
if (($pid == SI::Parse::EIT_PID) && (($name =~ /Nimiq/) || ($name =~ /EchoStar/))) {
|
179
|
print sprintf("\n\nWARNING: Scanning on PID 0x%02x which only includes now/next guide data. ".
|
180
|
"If you're wanting to scan the 9-day guide, ensure your DVB adapter ".
|
181
|
"is tuned to the appropriate network/transport.\n\n", $pid);
|
182
|
}
|
183
|
|
184
|
print sprintf("\n-> Scanning on pid 0x%x.\n", $pid);
|
185
|
|
186
|
print "\n-> Scanning for transports: ";
|
187
|
print "\n" if ($VERBOSE);
|
188
|
my $transports = getTransports($adapter);
|
189
|
print "\010done.\n";
|
190
|
|
191
|
foreach my $nit (keys %{$transports}) {
|
192
|
$num_transports += keys %{$$transports{$nit}};
|
193
|
}
|
194
|
|
195
|
print "\t-> Found $num_transports transport(s).\n\n";
|
196
|
|
197
|
print "-> Scanning for channels: ";
|
198
|
print "\n" if ($VERBOSE);
|
199
|
my ($channels, $miss) = getChannels($adapter, $transports);
|
200
|
print "\010done.\n";
|
201
|
|
202
|
foreach my $nit (keys %{$channels}) {
|
203
|
foreach my $tid (keys %{$$channels{$nit}}) {
|
204
|
$num_channels += keys %{$$channels{$nit}->{$tid}};
|
205
|
}
|
206
|
}
|
207
|
print "\t-> Found $num_channels channel(s)";
|
208
|
print ", $miss rescanned section(s).\n\n";
|
209
|
|
210
|
my $cache;
|
211
|
print "-> Scanning for EIT data: ";
|
212
|
print "\n" if ($VERBOSE);
|
213
|
($cache, $versions) = getEvents($adapter, $channels, $versions, $pid, $iversions);
|
214
|
|
215
|
my $n_items = $#{$cache};
|
216
|
|
217
|
print "\010done, found ".($n_items + 1)." event tables to process.\n\n";
|
218
|
|
219
|
print "-> Processing EIT data: ";
|
220
|
print "\n" if ($VERBOSE);
|
221
|
my $updated = processEvents($cache, $t_offset, $nice, $always, $log_missing_cats);
|
222
|
print "\010done, $updated programs added/updated.\n";
|
223
|
|
224
|
print "\n";
|
225
|
|
226
|
closefile();
|
227
|
|
228
|
}
|
229
|
|
230
|
sub cleanupString {
|
231
|
my $string = shift;
|
232
|
|
233
|
$string =~ s/^\s+//;
|
234
|
$string =~ s/\s+$//;
|
235
|
|
236
|
return $string;
|
237
|
}
|
238
|
|
239
|
############################################################################
|
240
|
# DVB functions #
|
241
|
############################################################################
|
242
|
|
243
|
sub getTransports {
|
244
|
my $adapter = shift;
|
245
|
my %transports;
|
246
|
my $all_seen = FALSE;
|
247
|
my $scan_start = FALSE;
|
248
|
my $count = 0;
|
249
|
my $select = new IO::Select;
|
250
|
my $nit_ver;
|
251
|
my %networks;
|
252
|
|
253
|
if ($_DEBUG_ && (-f $_TRANSCACHE_)) {
|
254
|
return getHash($_TRANSCACHE_);
|
255
|
}
|
256
|
|
257
|
my @sparkle = qw( \ | / - );
|
258
|
my $cur_sparkle = 0;
|
259
|
|
260
|
my $dvb_sup = new SI::Parse($_DEBUG_);
|
261
|
|
262
|
my $pid = SI::Parse::NIT_PID;
|
263
|
|
264
|
my $dmx = new Linux::DVB::Demux "/dev/dvb/adapter$adapter/demux0";
|
265
|
|
266
|
$dmx->buffer(DMX_READ_BUF_SIZE);
|
267
|
$dmx->sct_filter($pid, undef, undef, 0, DMX_CHECK_CRC|DMX_IMMEDIATE_START);
|
268
|
$dmx->blocking(0);
|
269
|
|
270
|
$select->add($dmx->fh);
|
271
|
|
272
|
my $now = time();
|
273
|
|
274
|
while (!$all_seen && (time() <= ($now + $TIMEOUT))) {
|
275
|
my @ready = $select->can_read(5);
|
276
|
my $handle = pop @ready;
|
277
|
|
278
|
die(sprintf("FATAL: Unable to scan PID 0x%02x.\n", $pid)) if (!$handle);
|
279
|
|
280
|
my $section;
|
281
|
|
282
|
sysread($handle, $section, DMX_READ_BUF_SIZE);
|
283
|
|
284
|
my $table = $dvb_sup->decodeSection($section);
|
285
|
|
286
|
next if (!defined($table));
|
287
|
next if ($$table{'table_id'} == SI::Parse::STUFFING_TABLE);
|
288
|
|
289
|
$nit_ver = $$table{'version_number'} if (!defined($nit_ver));
|
290
|
|
291
|
die("\n\nFATAL: NIT version number changed while scanning!\n")
|
292
|
if ($nit_ver != $$table{'version_number'});
|
293
|
|
294
|
my $nid = $$table{'network_id'};
|
295
|
|
296
|
next if ($nid != 256 && $nid != 257 && $nid != 4098 && $nid != 4101 && $nid != 4102);
|
297
|
|
298
|
foreach my $net (@{$$table{'network_descriptors'}}) {
|
299
|
if ($$net{'dvb_descriptor_tag'} == SI::Parse::NETWORK_NAME_DESCRIPTOR) {
|
300
|
$networks{$nid}++;
|
301
|
$scan_start = TRUE;
|
302
|
}
|
303
|
}
|
304
|
|
305
|
next if (!$scan_start);
|
306
|
|
307
|
foreach my $trans (@{$$table{'transport_descriptors'}}) {
|
308
|
my $tid = $$trans{'transport_stream_id'};
|
309
|
|
310
|
foreach my $desc (@{$$trans{'descriptors'}}) {
|
311
|
next if ($$desc{'dvb_descriptor_tag'} !=
|
312
|
SI::Parse::SAT_DELIVERY_DESCRIPTOR);
|
313
|
|
314
|
my $freq = $$desc{'frequency'};
|
315
|
my $sr = $$desc{'symbol_rate'};
|
316
|
|
317
|
my $pol;
|
318
|
for ($$desc{'polarization'}) {
|
319
|
if ($_ == SI::Parse::POL_HORIZ) {$pol = 'h'}
|
320
|
if ($_ == SI::Parse::POL_VERT) {$pol = 'v'}
|
321
|
if ($_ == SI::Parse::POL_LEFT) {$pol = 'l'}
|
322
|
if ($_ == SI::Parse::POL_RIGHT) {$pol = 'r'}
|
323
|
if ($_ > SI::Parse::POL_RIGHT) {
|
324
|
print "WARNING: Unknown polarity '$_' found!\n";
|
325
|
}
|
326
|
}
|
327
|
|
328
|
my $mod;
|
329
|
for($$desc{'modulation'}) {
|
330
|
if ($_ == SI::Parse::MOD_QPSK_0) {$mod = 'qpsk'}
|
331
|
if ($_ == SI::Parse::MOD_QPSK_1) {$mod = 'qpsk'}
|
332
|
if ($_ == SI::Parse::MOD_8PSK) {$mod = '8psk'}
|
333
|
if ($_ == SI::Parse::MOD_TQPSK) {$mod = 't_qpsk'}
|
334
|
if ($_ > SI::Parse::MOD_TQPSK) {
|
335
|
print "WARNING: Unknown modulation '$_' found!\n";
|
336
|
}
|
337
|
}
|
338
|
|
339
|
my $fec;
|
340
|
for($$desc{'fec_inner'}) {
|
341
|
if ($_ == SI::Parse::FEC_1_2) {$fec = '1/2'}
|
342
|
if ($_ == SI::Parse::FEC_2_3) {$fec = '2/3'}
|
343
|
if ($_ == SI::Parse::FEC_3_4) {$fec = '3/4'}
|
344
|
if ($_ == SI::Parse::FEC_4_5) {$fec = '4/5'}
|
345
|
if ($_ == SI::Parse::FEC_5_6) {$fec = '5/6'}
|
346
|
if ($_ == SI::Parse::FEC_7_8) {$fec = '7/8'}
|
347
|
if ($_ == SI::Parse::FEC_8_9) {$fec = '8/9'}
|
348
|
if ($_ == SI::Parse::FEC_3_5) {$fec = '3/5'}
|
349
|
if ($_ == SI::Parse::FEC_4_5) {$fec = '4/5'}
|
350
|
if ($_ == SI::Parse::FEC_9_10) {$fec = '9/10'}
|
351
|
if (!$_ || $_ > SI::Parse::FEC_8_9) {
|
352
|
print "WARNING: Unknown fec '$_' found!\n";
|
353
|
}
|
354
|
}
|
355
|
|
356
|
$count++;
|
357
|
|
358
|
if (!defined($transports{$nid}->{$freq}->{'tids'}->{$tid})) {
|
359
|
if ($VERBOSE) {
|
360
|
print "\t-> TRANSPORT: $nid $tid $freq $pol $mod $sr $fec\n";
|
361
|
} else {
|
362
|
$cur_sparkle = ($cur_sparkle + 1) % @sparkle;
|
363
|
print "\010$sparkle[$cur_sparkle]" if (!($count % 10));
|
364
|
}
|
365
|
|
366
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'polarity'} = $pol;
|
367
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'modulation'} = $mod;
|
368
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'symbolrate'} = $sr;
|
369
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'fec'} = $fec;
|
370
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'count'} = 0;
|
371
|
} else {
|
372
|
$transports{$nid}->{$freq}->{'tids'}->{$tid}->{'count'}++;
|
373
|
|
374
|
if (!$VERBOSE) {
|
375
|
$cur_sparkle = ($cur_sparkle + 1) % @sparkle;
|
376
|
print "\010$sparkle[$cur_sparkle]" if (!($count % 10));
|
377
|
}
|
378
|
}
|
379
|
}
|
380
|
}
|
381
|
|
382
|
$all_seen = TRUE;
|
383
|
|
384
|
foreach my $_nid (keys %networks) {
|
385
|
if ($networks{$_nid} < 3) {
|
386
|
$all_seen = FALSE;
|
387
|
last;
|
388
|
}
|
389
|
}
|
390
|
}
|
391
|
|
392
|
$dmx->stop();
|
393
|
|
394
|
if (!$all_seen) {
|
395
|
die("FATAL: Timeout while reading transport tables, aborting.\n");
|
396
|
}
|
397
|
|
398
|
saveHash($_TRANSCACHE_, \%transports) if ($_DEBUG_);
|
399
|
|
400
|
return \%transports;
|
401
|
}
|
402
|
|
403
|
sub getChannels {
|
404
|
my $adapter = shift;
|
405
|
my $transports = shift;
|
406
|
my $count = 0;
|
407
|
my $num_chan = 0;
|
408
|
my $num_found = 0;
|
409
|
my %channels;
|
410
|
my %_transports;
|
411
|
my $all_seen = FALSE;
|
412
|
my $scan_start = FALSE;
|
413
|
my $select = new IO::Select;
|
414
|
my $last_section = 0;
|
415
|
my $sdt_ver;
|
416
|
my $miss_errors = 0;
|
417
|
my $percent = 0;
|
418
|
|
419
|
if ($_DEBUG_ && (-f $_CHANCACHE_)) {
|
420
|
return (getHash($_CHANCACHE_), 0, 0);
|
421
|
}
|
422
|
|
423
|
foreach my $_nid (keys %{$transports}) {
|
424
|
foreach my $_freq (keys %{$$transports{$_nid}}) {
|
425
|
foreach my $_tid (keys %{$$transports{$_nid}->{$_freq}->{'tids'}}) {
|
426
|
$_transports{$_nid}->{$_tid}->{'got_channels'} = FALSE;
|
427
|
$num_chan++;
|
428
|
}
|
429
|
}
|
430
|
}
|
431
|
|
432
|
print " ";
|
433
|
|
434
|
my $dvb_sup = new SI::Parse($_DEBUG_);
|
435
|
|
436
|
my $pid = SI::Parse::SDT_PID;
|
437
|
|
438
|
my $dmx = new Linux::DVB::Demux "/dev/dvb/adapter$adapter/demux0";
|
439
|
|
440
|
$dmx->buffer(DMX_READ_BUF_SIZE);
|
441
|
$dmx->sct_filter($pid, undef, undef, 0, DMX_CHECK_CRC|DMX_IMMEDIATE_START);
|
442
|
$dmx->blocking(0);
|
443
|
|
444
|
$select->add($dmx->fh);
|
445
|
|
446
|
my $now = time();
|
447
|
|
448
|
while (!$all_seen && (time() <= ($now + $TIMEOUT))) {
|
449
|
my @ready = $select->can_read(5);
|
450
|
my $handle = pop @ready;
|
451
|
|
452
|
die(sprintf("FATAL: Unable to scan PID 0x%02x.\n", $pid)) if (!$handle);
|
453
|
|
454
|
$count++;
|
455
|
|
456
|
my $section;
|
457
|
|
458
|
sysread($handle, $section, DMX_READ_BUF_SIZE);
|
459
|
|
460
|
my $table = $dvb_sup->decodeSection($section);
|
461
|
|
462
|
next if (!defined($table));
|
463
|
next if ($$table{'table_id'} == SI::Parse::STUFFING_TABLE);
|
464
|
|
465
|
my $c_nid = $$table{'original_network_id'};
|
466
|
my $c_tid = $$table{'transport_stream_id'};
|
467
|
|
468
|
$sdt_ver = $$table{'version_number'} if (!defined($sdt_ver));
|
469
|
|
470
|
die("\n\nFATAL: SDT version number changed while scanning!\n")
|
471
|
if ($sdt_ver != $$table{'version_number'});
|
472
|
|
473
|
$scan_start = TRUE if ($$table{'section_number'} == 0);
|
474
|
next if (!$scan_start);
|
475
|
|
476
|
if ($$table{'section_number'} &&
|
477
|
(($$table{'section_number'} - $last_section) != 1)) {
|
478
|
$miss_errors++;
|
479
|
}
|
480
|
|
481
|
if ($$table{'section_number'} > $$table{'last_section_number'}) {
|
482
|
$miss_errors++;
|
483
|
}
|
484
|
|
485
|
if (!defined($_transports{$c_nid}->{$c_tid})) {
|
486
|
#print "\n\nWARNING: Found channel defined for non-existant ".
|
487
|
# "transport $c_nid/$c_tid, ignoring.\n";
|
488
|
next;
|
489
|
}
|
490
|
|
491
|
$last_section = $$table{'section_number'};
|
492
|
|
493
|
if (!$VERBOSE) {
|
494
|
if (!($count % 10)) {
|
495
|
my $_percent = ($num_found / $num_chan) * 100;
|
496
|
if ($_percent != $percent) {
|
497
|
print sprintf("\010\010\010\010%3i\%", ($_percent, 3));
|
498
|
$percent = $_percent;
|
499
|
}
|
500
|
}
|
501
|
}
|
502
|
|
503
|
next if ($_transports{$c_nid}->{$c_tid}->{'got_channels'});
|
504
|
|
505
|
if (!defined($_transports{$c_nid}->{$c_tid}->{'sections'})) {
|
506
|
for (my $x = 0; $x <= $$table{'last_section_number'}; $x++) {
|
507
|
$_transports{$c_nid}->{$c_tid}->{'sections'}->{$x} = TRUE;
|
508
|
}
|
509
|
}
|
510
|
|
511
|
foreach my $svc (@{$$table{'services'}}) {
|
512
|
my $sid = $$svc{'service_id'};
|
513
|
my $eit_schedule_flag = $$svc{'eit_schedule_flag'};
|
514
|
my $callsign;
|
515
|
my $name;
|
516
|
my $service_type;
|
517
|
|
518
|
foreach my $desc (@{$$svc{'service_descriptors'}}) {
|
519
|
if ($$desc{'dvb_descriptor_tag'} == SI::Parse::SERVICE_DESCRIPTOR) {
|
520
|
$callsign = $$desc{'service_name'};
|
521
|
$name = $$desc{'service_provider_name'};
|
522
|
$service_type = $$desc{'service_type'};
|
523
|
} # We don't care about the other descriptors - for now
|
524
|
}
|
525
|
|
526
|
if (!defined($channels{$c_nid}->{$c_tid}->{$sid})) {
|
527
|
if ($VERBOSE) {
|
528
|
print "\t-> Channel: $c_nid tp: $c_tid $sid ".
|
529
|
"'$callsign' \"$name\" Type $service_type\n";
|
530
|
}
|
531
|
$callsign =~ s/\&/&/g;
|
532
|
$name =~ s/\&/&/g;
|
533
|
print XMLFILE "\t<channel id=\"$sid-$c_nid.geteit\">\n";
|
534
|
print XMLFILE "\t\t<display-name>$callsign</display-name>\n";
|
535
|
print XMLFILE "\t\t<display-name>$sid $callsign</display-name>\n";
|
536
|
print XMLFILE "\t\t<display-name>$sid</display-name>\n";
|
537
|
print XMLFILE "\t\t<display-name>$name</display-name>\n";
|
538
|
print XMLFILE "\t</channel>\n";
|
539
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'callsign'} = $callsign;
|
540
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'name'} = $name;
|
541
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'eit_schedule_flag'} = $eit_schedule_flag;
|
542
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'service_type'} = $service_type;
|
543
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'used'} = 0;
|
544
|
} else {
|
545
|
$channels{$c_nid}->{$c_tid}->{$sid}->{'used'}++;
|
546
|
}
|
547
|
}
|
548
|
|
549
|
delete $_transports{$c_nid}->{$c_tid}->{'sections'}->{$$table{'section_number'}};
|
550
|
|
551
|
if (defined($_transports{$c_nid}->{$c_tid}->{'sections'}) &&
|
552
|
!keys %{$_transports{$c_nid}->{$c_tid}->{'sections'}}) {
|
553
|
$num_found++ if (defined($_transports{$c_nid}->{$c_tid}));
|
554
|
$_transports{$c_nid}->{$c_tid}->{'got_channels'} = TRUE;
|
555
|
}
|
556
|
|
557
|
$all_seen = TRUE;
|
558
|
|
559
|
foreach my $_nid (keys %_transports) {
|
560
|
foreach my $_tid (keys %{$_transports{$_nid}}) {
|
561
|
$all_seen = FALSE if (!$_transports{$_nid}->{$_tid}->{'got_channels'});
|
562
|
last if (!$all_seen);
|
563
|
}
|
564
|
|
565
|
last if (!$all_seen);
|
566
|
}
|
567
|
}
|
568
|
|
569
|
$dmx->stop();
|
570
|
|
571
|
if (!$all_seen) {
|
572
|
die("FATAL: Timeout while reading channel tables, aborting.\n");
|
573
|
}
|
574
|
|
575
|
saveHash($_CHANCACHE_, \%channels) if ($_DEBUG_);
|
576
|
|
577
|
print "\010\010\010";
|
578
|
|
579
|
return (\%channels, $miss_errors);
|
580
|
}
|
581
|
|
582
|
sub checkPID {
|
583
|
my $adapter = shift;
|
584
|
my $pid = shift;
|
585
|
my $select = new IO::Select;
|
586
|
|
587
|
my $dvb_sup = new SI::Parse($_DEBUG_);
|
588
|
|
589
|
my $dmx = new Linux::DVB::Demux "/dev/dvb/adapter$adapter/demux0";
|
590
|
|
591
|
$dmx->buffer(DMX_READ_BUF_SIZE);
|
592
|
$dmx->sct_filter($pid, undef, undef, 0, DMX_CHECK_CRC|DMX_IMMEDIATE_START);
|
593
|
$dmx->blocking(0);
|
594
|
|
595
|
$select->add($dmx->fh);
|
596
|
|
597
|
my @ready = $select->can_read(2);
|
598
|
my $handle = pop @ready;
|
599
|
|
600
|
$dmx->stop();
|
601
|
|
602
|
return defined($handle) ? TRUE : FALSE;
|
603
|
}
|
604
|
|
605
|
sub findBestPID {
|
606
|
my $adapter = shift;
|
607
|
|
608
|
foreach my $pid (@EITPIDS) {
|
609
|
return $pid if (checkPID($adapter, $pid));
|
610
|
}
|
611
|
|
612
|
return undef;
|
613
|
}
|
614
|
|
615
|
sub getEvents {
|
616
|
my $adapter = shift;
|
617
|
my $channels = shift;
|
618
|
my $versions = shift;
|
619
|
my $pid = shift;
|
620
|
my $iversions = shift;
|
621
|
my @cache;
|
622
|
my $count = 0;
|
623
|
my $num_chan = 0;
|
624
|
my $num_found = 0;
|
625
|
my $all_seen = FALSE;
|
626
|
my $has_eit = FALSE;
|
627
|
my %_channels;
|
628
|
my %_events;
|
629
|
my $select = new IO::Select;
|
630
|
my $num = 0;
|
631
|
my %versions_t;
|
632
|
my $percent = 0;
|
633
|
|
634
|
if ($_DEBUG_ && (-f $_EITRAW_)) {
|
635
|
return getHash($_EITRAW_);
|
636
|
}
|
637
|
|
638
|
foreach my $nid (keys %{$channels}) {
|
639
|
foreach my $tid (keys %{$$channels{$nid}}) {
|
640
|
foreach my $sid (keys %{$$channels{$nid}->{$tid}}) {
|
641
|
if ($$channels{$nid}->{$tid}->{$sid}->{'eit_schedule_flag'}) {
|
642
|
my $nts = sprintf("%04x%04x%04x", $nid, $tid, $sid);
|
643
|
my %empty = ('ZZ' => 1);
|
644
|
$_channels{$nts} = \%empty;
|
645
|
$num_chan++;
|
646
|
}
|
647
|
}
|
648
|
}
|
649
|
}
|
650
|
|
651
|
print " ";
|
652
|
|
653
|
my $dvb_sup = new SI::Parse($_DEBUG_);
|
654
|
|
655
|
my $dmx = new Linux::DVB::Demux "/dev/dvb/adapter0/demux0";
|
656
|
|
657
|
$dmx->buffer(DMX_READ_BUF_SIZE);
|
658
|
$dmx->sct_filter($pid, undef, undef, 0, DMX_CHECK_CRC|DMX_IMMEDIATE_START);
|
659
|
$dmx->blocking(0);
|
660
|
|
661
|
$select->add($dmx->fh);
|
662
|
|
663
|
my $now = time();
|
664
|
|
665
|
while (!$all_seen && (time() <= ($now + $TIMEOUT))) {
|
666
|
my $skip_event = FALSE;
|
667
|
my @ready = $select->can_read(5);
|
668
|
my $handle = pop @ready;
|
669
|
|
670
|
die(sprintf("FATAL: Unable to scan PID 0x%02x.\n", $pid)) if (!$handle);
|
671
|
|
672
|
my $section;
|
673
|
|
674
|
sysread($handle, $section, DMX_READ_BUF_SIZE);
|
675
|
next if (!$section);
|
676
|
my $sec_a = to_hex($section);
|
677
|
my $tableid = $$sec_a[0];
|
678
|
my $sid = ($$sec_a[3] << 0x08) + $$sec_a[4];
|
679
|
my $version = ($$sec_a[5] & 0x7c) >> 0x01;
|
680
|
my $sec_num = $$sec_a[6];
|
681
|
my $sec_last = $$sec_a[7];
|
682
|
my $c_tid = ($$sec_a[8] << 0x08) + $$sec_a[9];
|
683
|
my $c_nid = ($$sec_a[10] << 0x08) + $$sec_a[11];
|
684
|
my $last_seg = $$sec_a[12];
|
685
|
my $last_tbid = $$sec_a[13];
|
686
|
my $nts = sprintf("%04x%04x%04x", $c_nid, $c_tid, $sid);
|
687
|
my $ts = sprintf("%02x%02x", $tableid, $sec_num);
|
688
|
$count++;
|
689
|
|
690
|
if (!$VERBOSE) {
|
691
|
if (!($count % 10)) {
|
692
|
my $_percent = ($num_found / $num_chan) * 100;
|
693
|
if ($_percent != $percent) {
|
694
|
print sprintf("\010\010\010\010%3i\%", ($_percent, 3));
|
695
|
$percent = $_percent;
|
696
|
}
|
697
|
}
|
698
|
}
|
699
|
next if (!defined($_channels{$nts}));
|
700
|
|
701
|
if (!$iversions) {
|
702
|
$skip_event = TRUE if ($$versions{$c_nid}->{$c_tid}->{$sid}->{$tableid} == $version);
|
703
|
$versions_t{$c_nid}->{$c_tid}->{$sid}->{$tableid} = $version;
|
704
|
}
|
705
|
|
706
|
if (defined($_channels{$nts}->{'ZZ'})) {
|
707
|
my $low_id = 0;
|
708
|
|
709
|
if (($tableid == SI::Parse::EIT_ACTUAL_TRANSPORT) ||
|
710
|
($tableid == SI::Parse::EIT_OTHER_TRANSPORT)) {
|
711
|
$low_id = $tableid;
|
712
|
} elsif (($tableid >= SI::Parse::EITS_ACTUAL_LOW) &&
|
713
|
($tableid <= SI::Parse::EITS_ACTUAL_HIGH)) {
|
714
|
$low_id = SI::Parse::EITS_ACTUAL_LOW;
|
715
|
} elsif (($tableid >= SI::Parse::EITS_OTHER_LOW) &&
|
716
|
($tableid <= SI::Parse::EITS_OTHER_HIGH)) {
|
717
|
$low_id = SI::Parse::EITS_OTHER_LOW
|
718
|
} elsif (($tableid >= SI::Parse::DISH_EVENT_EXT_LOW) &&
|
719
|
($tableid <= SI::Parse::DISH_EVENT_EXT_HIGH)) {
|
720
|
$low_id = SI::Parse::DISH_EVENT_EXT_LOW
|
721
|
} else {
|
722
|
print "\n\nWARNING: Invalid table id $tableid found. Unable to continue.\n";
|
723
|
next;
|
724
|
}
|
725
|
|
726
|
for (my $y = $low_id; $y <= $last_tbid; $y++) {
|
727
|
for (my $x = 0; $x <= $sec_last; $x++) {
|
728
|
my $_ts = sprintf("%02x%02x", $y, $x);
|
729
|
$_channels{$nts}->{$_ts} = TRUE;
|
730
|
delete $_channels{$nts}->{'ZZ'};
|
731
|
}
|
732
|
}
|
733
|
}
|
734
|
|
735
|
delete $_channels{$nts}->{$ts};
|
736
|
|
737
|
if (!($count % 100)) {
|
738
|
$all_seen = TRUE;
|
739
|
$num_found = 0;
|
740
|
|
741
|
foreach my $_nts (keys %_channels) {
|
742
|
$all_seen = FALSE if (defined($_channels{$_nts}->{'ZZ'}));
|
743
|
$num_found++ if (!keys %{$_channels{$_nts}});
|
744
|
}
|
745
|
}
|
746
|
|
747
|
if (!defined($_events{$nts.$ts})) {
|
748
|
push @cache, $section if (!$skip_event);
|
749
|
$num++;
|
750
|
$_events{$nts.$ts}++;
|
751
|
}
|
752
|
|
753
|
$has_eit = TRUE;
|
754
|
}
|
755
|
|
756
|
if (!$has_eit) {
|
757
|
if ($VERBOSE) {
|
758
|
print "WARNING: No EIT tables were found.\n";
|
759
|
} else {
|
760
|
print "\010\010\010 - no EIT tables were found: ";
|
761
|
}
|
762
|
}
|
763
|
|
764
|
$dmx->stop();
|
765
|
|
766
|
if (!$all_seen && $has_eit) {
|
767
|
die("FATAL: Timeout while reading event tables, aborting.\n");
|
768
|
}
|
769
|
|
770
|
print "\010\010\010";
|
771
|
|
772
|
saveHash($_EITRAW_, \@cache) if ($_DEBUG_);
|
773
|
|
774
|
return \@cache, \%versions_t;
|
775
|
}
|
776
|
|
777
|
sub processEvents {
|
778
|
my $cache = shift;
|
779
|
my $t_offset = shift;
|
780
|
my $nice = shift;
|
781
|
my $always = shift;
|
782
|
my $log_missing_categories = shift;
|
783
|
my $count = 0;
|
784
|
my $updated = 0;
|
785
|
my $log_missing = 0;
|
786
|
|
787
|
my $num_entries = $#{$cache} + 1;
|
788
|
|
789
|
print " ";
|
790
|
|
791
|
my $dvb_sup = new SI::Parse($_DEBUG_);
|
792
|
|
793
|
foreach my $section (@{$cache}) {
|
794
|
my $table = $dvb_sup->decodeSection($section);
|
795
|
|
796
|
next if (!defined($table));
|
797
|
next if ($#{$$table{'events'}} == -1);
|
798
|
|
799
|
my $tableid = $$table{'table_id'};
|
800
|
my $serviceid = $$table{'service_id'};
|
801
|
my $networkid = $$table{'original_network_id'};
|
802
|
my $transportid = $$table{'transport_stream_id'};
|
803
|
|
804
|
foreach my $_event (@{$$table{'events'}}) {
|
805
|
next if (!$$_event{'descriptors_loop_length'});
|
806
|
|
807
|
my $title;
|
808
|
my $description;
|
809
|
my $properties;
|
810
|
my $programinfo;
|
811
|
my $mpaainfo;
|
812
|
my $vchipinfo;
|
813
|
my $rightsinfo;
|
814
|
my $c_theme;
|
815
|
my $c_category;
|
816
|
my $language;
|
817
|
my $upped;
|
818
|
|
819
|
my $starttime_t = timegm($$_event{'start_second'},
|
820
|
$$_event{'start_minute'},
|
821
|
$$_event{'start_hour'},
|
822
|
$$_event{'start_day'},
|
823
|
($$_event{'start_month'}-1),
|
824
|
$$_event{'start_year'});
|
825
|
|
826
|
my $duration = ($$_event{'duration_hour'} * 60 * 60) +
|
827
|
($$_event{'duration_minute'} * 60) +
|
828
|
$$_event{'duration_second'};
|
829
|
my $starttime = realDate($starttime_t);
|
830
|
my $endtime = realDate($starttime_t + $duration);
|
831
|
|
832
|
foreach my $desc (@{$$_event{'event_descriptors'}}) {
|
833
|
my $tag = $$desc{'dvb_descriptor_tag'};
|
834
|
|
835
|
if ($tag == SI::Parse::SHORT_EVENT_DESCRIPTOR) {
|
836
|
$title = $$desc{'event_name'};
|
837
|
} elsif ($tag == SI::Parse::EXTEND_EVENT_DESCRIPTOR) {
|
838
|
$description = $$desc{'text'};
|
839
|
$language = $$desc{'iso639_2_language_code'};
|
840
|
} elsif ($tag == SI::Parse::DISH_EVENT_NAME) {
|
841
|
$title = $dvb_sup->decompressDishEventName($tableid, $$desc{'descriptor_data'});
|
842
|
} elsif ($tag == SI::Parse::DISH_EVENT_DESCRIPTION) {
|
843
|
$description = $dvb_sup->decompressDishEventDescription($tableid, $$desc{'descriptor_data'});
|
844
|
} elsif ($tag == SI::Parse::DISH_EVENT_PROPERTIES) {
|
845
|
$properties = $dvb_sup->decompressDishEventProperties($tableid, $$desc{'descriptor_data'});
|
846
|
} elsif ($tag == SI::Parse::CONTENT_DESCRIPTOR) {
|
847
|
$c_theme = $dvb_sup->dishContentTheme($$desc{'content_nibble_level_2'});
|
848
|
$c_category = $dvb_sup->dishContentCategory($$desc{'user_nibble'});
|
849
|
#$c_category = substr($description,1,index($description,"."));
|
850
|
if (!defined($c_category) && $log_missing_categories) {
|
851
|
$log_missing = $$desc{'user_nibble'};
|
852
|
}
|
853
|
} elsif ($tag == SI::Parse::DISH_EVENT_PROGRAMID) {
|
854
|
$programinfo = $desc;
|
855
|
} elsif ($tag == SI::Parse::DISH_EVENT_MPAA_FLAGS) {
|
856
|
$mpaainfo = $desc;
|
857
|
} elsif ($tag == SI::Parse::DISH_EVENT_VCHIP_FLAGS) {
|
858
|
$vchipinfo = $desc;
|
859
|
} elsif ($tag == SI::Parse::TIER_EVENT_RIGHTS) {
|
860
|
$rightsinfo = $desc;
|
861
|
}
|
862
|
}
|
863
|
|
864
|
if ($log_missing) {
|
865
|
logMissingCategory($log_missing, $c_theme, $title, $description);
|
866
|
}
|
867
|
|
868
|
my $p_event = processEvent($title, $description);
|
869
|
my $p_properties = processProperties($properties);
|
870
|
|
871
|
my %event;
|
872
|
|
873
|
$event{'networkid'} = $networkid;
|
874
|
#$event{'chanids'} = ;
|
875
|
$event{'starttime'} = $starttime;
|
876
|
$event{'endtime'} = $endtime;
|
877
|
$event{'title'} = $$p_event{'title'};
|
878
|
$event{'subtitle'} = $$p_event{'subtitle'};
|
879
|
$event{'description'} = $$p_event{'description'};
|
880
|
$event{'language'} = substr($language,0,2);
|
881
|
#if (defined($$p_event{'actors'})) { my @actors = @{$$p_event{'actors'}};}
|
882
|
|
883
|
if ($c_theme && $c_category) {
|
884
|
$event{'category_type'} = $c_theme;
|
885
|
$event{'category'} = $c_category;
|
886
|
} elsif ($$p_event{'theme'}) {
|
887
|
$event{'category'} = $event{'category_type'}
|
888
|
= $$p_event{'theme'};
|
889
|
} else {
|
890
|
$event{'category'} = $event{'category_type'}
|
891
|
= 'Unknown';
|
892
|
}
|
893
|
|
894
|
if ($programinfo) {
|
895
|
if ($$programinfo{'originalairdate_day'}) {
|
896
|
$event{'originalairdate'} = simpleDate($$programinfo{'originalairdate_month'},
|
897
|
$$programinfo{'originalairdate_day'},
|
898
|
$$programinfo{'originalairdate_year'});
|
899
|
|
900
|
|
901
|
if (dateLessThan($$programinfo{'originalairdate_month'},
|
902
|
$$programinfo{'originalairdate_day'},
|
903
|
$$programinfo{'originalairdate_year'},
|
904
|
(time() + $t_offset))) {
|
905
|
$event{'previouslyshown'} = 1;
|
906
|
} else {
|
907
|
$event{'previouslyshown'} = 0;
|
908
|
}
|
909
|
$event{'airdate'} = $$programinfo{'originalairdate_year'};
|
910
|
}
|
911
|
|
912
|
$event{'programid'} = $$programinfo{'programid'};
|
913
|
$event{'syndicatedepisodenumber'} = $$programinfo{'episode'};
|
914
|
if (($$programinfo{'seriesid'} !~ /^MV/) &&
|
915
|
($$programinfo{'seriesid'} !~ /^SP/)) {
|
916
|
$event{'seriesid'} = $$programinfo{'seriesid'};
|
917
|
}
|
918
|
} elsif ($$p_event{'tear'}) {
|
919
|
$event{'airdate'} = $$p_event{'year'};
|
920
|
}
|
921
|
|
922
|
my $audioprop;
|
923
|
my $videoprop;
|
924
|
my $subtitletypes;
|
925
|
if ($p_properties) {
|
926
|
if ($$p_properties{'stereo'}) {
|
927
|
$audioprop .= ',STEREO';
|
928
|
$event{'stereo'} = 1;
|
929
|
}
|
930
|
if ($$p_properties{'closedcaptioned'}) {
|
931
|
$audioprop .= ',HARDHEAR';
|
932
|
$subtitletypes .= ',HARDHEAR';
|
933
|
$event{'closecaptioned'} = 1;
|
934
|
}
|
935
|
} else {
|
936
|
if ($$p_event{'stereo'}) {
|
937
|
$audioprop .= ',STEREO';
|
938
|
$event{'stereo'} = 1;
|
939
|
}
|
940
|
if ($$p_event{'closedcaptioned'}) {
|
941
|
$audioprop .= ',HARDHEAR';
|
942
|
$subtitletypes .= ',HARDHEAR';
|
943
|
$event{'closecaptioned'} = 1;
|
944
|
}
|
945
|
}
|
946
|
|
947
|
if ($$p_event{'hdtv'}) {
|
948
|
$videoprop .= ',HDTV';
|
949
|
}
|
950
|
$audioprop =~ s/^\,//;
|
951
|
$videoprop =~ s/^\,//;
|
952
|
$subtitletypes =~ s/^\,//;
|
953
|
|
954
|
$event{'audioprop'} = $audioprop;
|
955
|
$event{'videoprop'} = $videoprop;
|
956
|
$event{'subtitletypes'} = $subtitletypes;
|
957
|
|
958
|
if ($mpaainfo) {
|
959
|
if ($$mpaainfo{'star_raw'}) {
|
960
|
$event{'stars'} = $$mpaainfo{'star_raw'};
|
961
|
}
|
962
|
}
|
963
|
print XMLFILE "\t<programme start=\"$event{'starttime'} -0400\" stop=\"$event{'endtime'} -0400\" channel=\"$serviceid-$networkid.geteit\">\n";
|
964
|
print XMLFILE "\t\t<title lang=\"$event{'language'}\">$event{'title'}</title>\n";
|
965
|
print XMLFILE "\t\t<desc lang=\"$event{'language'}\">\n\t\t\t$event{'description'}\n\t\t</desc>\n";
|
966
|
print XMLFILE "\t\t<category lang=\"$event{'language'}\">$event{'category'}</category>\n" if ($event{'category'});
|
967
|
if ($$p_event{'actors'}) {
|
968
|
print XMLFILE "\t\t<credits>\n";
|
969
|
foreach my $actor (@{$$p_event{'actors'}}) {
|
970
|
print XMLFILE "\t\t\t<actor>\"$actor\"</actor>\n"
|
971
|
}
|
972
|
print XMLFILE "\t\t</credits>\n";
|
973
|
}
|
974
|
print XMLFILE "\t\t<date>$event{'originalairdate'}</date>\n" if ($event{'originalairdate'});
|
975
|
print XMLFILE "\t\t<video>\n\t\t\t<quality>$event{'videoprop'}</quality>\n\t\t</video>\n" if ($event{'videoprop'});
|
976
|
print XMLFILE "\t\t<episode-num system=\"xmltv_ns\">$event{'syndicatedepisodenumber'}</episode-num>\n" if ($event{'syndicatedepisodenumber'});
|
977
|
print XMLFILE "\t\t<star-rating>\n\t\t\t<value>$event{'stars'}</value>\n\t\t</star-rating>\n" if ($event{'stars'});
|
978
|
print XMLFILE "\t</programme>\n";
|
979
|
|
980
|
#print "channel id=$serviceid-$networkid.geteit\n";
|
981
|
#print "Program ID: $event{'programid'}\n";
|
982
|
#print "Start time: $event{'starttime'}\n";
|
983
|
#print "Duration: $starttime_t $duration\n";
|
984
|
#print "End Time: $event{'endtime'}\n";
|
985
|
#print "Title: $event{'title'}\n";
|
986
|
#print "SubTitle: $event{'subtitle'}\n";
|
987
|
#print "Subtitle types: $event{'subtitletypes'}\n";
|
988
|
#print "Description: $event{'description'}\n";
|
989
|
#print "Language: $event{'language'}\n";
|
990
|
#print "Theme: $event{'theme'}\n";
|
991
|
#print "HDTV: $event{'hdtv'}\n";
|
992
|
#print "Year: $event{'year'}\n";
|
993
|
#print "Stereo: $event{'stereo'}\n";
|
994
|
#print "Closed Captioned: $event{'closedcaptioned'}\n";
|
995
|
#print "Category: $event{'category'}\n";
|
996
|
#print "Category types: $event{'category_types'}\n";
|
997
|
#print "Original Airdate: $event{'originalairdate'}\n";
|
998
|
#print "Previously Shown: $event{'previouslyshown'}\n";
|
999
|
#print "Air Date: $event{'airdate'}\n";
|
1000
|
#print "Syndicated episode ID: $event{'syndicatedepisodeid'}\n";
|
1001
|
#print "Series ID: $event{'seriesid'}\n";
|
1002
|
#print "Stars: $event{'stars'}\n";
|
1003
|
#print "MPAA info: $$mpaainfo{'star_raw'}\n";
|
1004
|
#print "VCHIP info: $vchipinfo\n";
|
1005
|
#print "Rights info: $rightsinfo\n";
|
1006
|
#print "Audio prop: $event{'audioprop'}\n";
|
1007
|
#print "video prop: $event{'videoprop'}\n";
|
1008
|
|
1009
|
#print "----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n";
|
1010
|
$updated++ if ($upped);
|
1011
|
|
1012
|
usleep(50000) if ($nice);
|
1013
|
}
|
1014
|
|
1015
|
$count++;
|
1016
|
|
1017
|
if ($VERBOSE) {
|
1018
|
print "\t-> EIT Data: $networkid tp: $transportid $serviceid\n";
|
1019
|
} else {
|
1020
|
if (!($count % 10)) {
|
1021
|
print sprintf("\010\010\010\010%3i\%",
|
1022
|
(($count / $num_entries) * 100), 3);
|
1023
|
}
|
1024
|
}
|
1025
|
}
|
1026
|
|
1027
|
print "\010\010\010";
|
1028
|
return $updated;
|
1029
|
}
|
1030
|
|
1031
|
sub getNetwork {
|
1032
|
my $adapter = shift;
|
1033
|
my $dvb_sup = new SI::Parse;
|
1034
|
my $network_name;
|
1035
|
my $network_id;
|
1036
|
my $found = FALSE;
|
1037
|
my $select = new IO::Select;
|
1038
|
|
1039
|
my $pid = SI::Parse::NIT_PID;
|
1040
|
|
1041
|
my $dmx = new Linux::DVB::Demux "/dev/dvb/adapter$adapter/demux0";
|
1042
|
|
1043
|
$dmx->buffer(DMX_READ_BUF_SIZE);
|
1044
|
$dmx->sct_filter($pid, undef, undef, 0, DMX_CHECK_CRC|DMX_IMMEDIATE_START);
|
1045
|
$dmx->blocking(0);
|
1046
|
|
1047
|
$select->add($dmx->fh);
|
1048
|
|
1049
|
my $now = time();
|
1050
|
|
1051
|
while (!$found && (time() <= ($now + $TIMEOUT))) {
|
1052
|
my @ready = $select->can_read(5);
|
1053
|
my $handle = pop @ready;
|
1054
|
|
1055
|
die(sprintf("FATAL: Unable to scan PID 0x%02x.\n", $pid)) if (!$handle);
|
1056
|
|
1057
|
my $section;
|
1058
|
|
1059
|
sysread($handle, $section, DMX_READ_BUF_SIZE);
|
1060
|
|
1061
|
my $table = $dvb_sup->decodeSection($section);
|
1062
|
|
1063
|
next if (!$table);
|
1064
|
next if ($$table{'table_id'} != SI::Parse::NIT_ACTUAL_NETWORK);
|
1065
|
|
1066
|
$network_id = $$table{'network_id'};
|
1067
|
|
1068
|
foreach my $net (@{$$table{'network_descriptors'}}) {
|
1069
|
if ($$net{'dvb_descriptor_tag'} == SI::Parse::NETWORK_NAME_DESCRIPTOR) {
|
1070
|
$network_name = $$net{'network_name'};
|
1071
|
}
|
1072
|
|
1073
|
$found = TRUE;
|
1074
|
}
|
1075
|
}
|
1076
|
|
1077
|
$dmx->stop();
|
1078
|
|
1079
|
if (!$found) {
|
1080
|
die("FATAL: Timeout while reading transport tables, aborting.\n");
|
1081
|
}
|
1082
|
|
1083
|
return($network_id, $network_name);
|
1084
|
}
|
1085
|
|
1086
|
sub hex_to_array {
|
1087
|
my $string = shift;
|
1088
|
my @array;
|
1089
|
my $octet;
|
1090
|
|
1091
|
foreach my $nibble (split(//, $string)) {
|
1092
|
$octet .= $nibble;
|
1093
|
|
1094
|
if (length($octet) == 2) {
|
1095
|
push @array, hex($octet);
|
1096
|
$octet = undef;
|
1097
|
}
|
1098
|
}
|
1099
|
|
1100
|
return \@array;
|
1101
|
}
|
1102
|
|
1103
|
sub to_hex {
|
1104
|
my $string = shift;
|
1105
|
my @array = map(ord, split(//, $string));
|
1106
|
|
1107
|
return \@array;
|
1108
|
}
|
1109
|
|
1110
|
sub table {
|
1111
|
my $buf = shift;
|
1112
|
my $start = shift;
|
1113
|
my $len = shift;
|
1114
|
my @table;
|
1115
|
my $overflow = 0;
|
1116
|
|
1117
|
for (my $i = $start; $i < ($start + $len); $i++) {
|
1118
|
if ($i <= $#{$buf}) {
|
1119
|
push @table, $$buf[$i];
|
1120
|
} else {
|
1121
|
$overflow++;
|
1122
|
}
|
1123
|
}
|
1124
|
|
1125
|
if ($overflow) {
|
1126
|
#print "WARNING: Table copy overflowed by $overflow: start == $start, len = $len:\n";
|
1127
|
#print hex_ascii($buf);
|
1128
|
}
|
1129
|
|
1130
|
return \@table;
|
1131
|
}
|
1132
|
|
1133
|
|
1134
|
# Real simple date calculator
|
1135
|
sub dateLessThan {
|
1136
|
my $omonth = shift;
|
1137
|
my $oday = shift;
|
1138
|
my $oyear = shift;
|
1139
|
my $now = shift;
|
1140
|
|
1141
|
my @t = gmtime($now);
|
1142
|
my $year = shift @t;
|
1143
|
my $month = shift @t;
|
1144
|
my $day = shift @t;
|
1145
|
|
1146
|
my $a = sprintf("%04i%02i%02i", $year, $month, $day);
|
1147
|
my $b = sprintf("%04i%02i%02i", $oyear, $omonth, $oday);
|
1148
|
|
1149
|
return ($b < $a);
|
1150
|
}
|
1151
|
|
1152
|
sub simpleDate {
|
1153
|
my $month = shift;
|
1154
|
my $day = shift;
|
1155
|
my $year = shift;
|
1156
|
|
1157
|
return sprintf("%02i-%02i-%02i", $year, $month, $day);
|
1158
|
}
|
1159
|
|
1160
|
sub processEvent {
|
1161
|
my $title = shift;
|
1162
|
my $description = shift;
|
1163
|
my %event;
|
1164
|
|
1165
|
$title = cleanupString($title);
|
1166
|
$description = cleanupString($description);
|
1167
|
|
1168
|
if ($title =~ /\(HD\)/) {
|
1169
|
$event{'hdtv'} = TRUE;
|
1170
|
$title =~ s/\(HD\)//;
|
1171
|
}
|
1172
|
|
1173
|
if ($title =~ /^HD\s*\-\s*/) {
|
1174
|
$event{'hdtv'} = TRUE;
|
1175
|
$title =~ s/^HD\s*\-\s*//;
|
1176
|
}
|
1177
|
|
1178
|
if ($title =~ /^HD\s+/) {
|
1179
|
$event{'hdtv'} = TRUE;
|
1180
|
# $title =~ s/^HD\s+//;
|
1181
|
}
|
1182
|
|
1183
|
$title =~ s/\s\(All\sDay.*\)$//;
|
1184
|
$title =~ s/\:\s*$//;
|
1185
|
$title =~ s/\s(5|7)\.1$//;
|
1186
|
$title =~ s/\s\-\sPremier\sRelease//;
|
1187
|
$title =~ s/\s\-\sPremier//;
|
1188
|
$title =~ s/\:\sSortie\sPrimeur$//;
|
1189
|
$title =~ s/\:\sPrimeur$//; # Two different spellings?
|
1190
|
$title =~ s/\:\sPrimuer$//;
|
1191
|
$title =~ s/\:\sPrime$//;
|
1192
|
$title =~ s/\s*\(RC\)//;
|
1193
|
$title =~ s/\,\sv\..{1,3}$//;
|
1194
|
$title =~ s/\(v\..{1,3}\.*\)//;
|
1195
|
$title =~ s/\&/\&/g;
|
1196
|
$title =~ s/\s*\(INFO\)//;
|
1197
|
|
1198
|
$event{'title'} = $title;
|
1199
|
|
1200
|
($event{'subtitle'}, $description) = split(/\s*\x0d/, $description, 2)
|
1201
|
if ($description =~ /\x0d\s/);
|
1202
|
|
1203
|
if ((length($event{'title'}) > 15) &&
|
1204
|
(length($event{'subtitle'}) > length($event{'title'})) &&
|
1205
|
(index($event{'subtitle'}, $event{'title'}) >= 0) &&
|
1206
|
(length($event{'subtitle'}) - length($event{'title'}) < 7)) {
|
1207
|
$event{'subtitle'} = '';
|
1208
|
}
|
1209
|
|
1210
|
my $position = index($description, '.');
|
1211
|
my $after = substr($description, ($position + 1), 1);
|
1212
|
if ((($position < length($description)) && ($after eq ' ')) ||
|
1213
|
(($position + 1) == length($description))) {
|
1214
|
if (($position > 0) && ($position <= 20)) {
|
1215
|
my $old_description = $description;
|
1216
|
($event{'theme'}, $description) = split(/\./, $description, 2);
|
1217
|
$event{'theme'} =~ s/^\s//;
|
1218
|
if ($event{'theme'} =~ /-/) {
|
1219
|
$description = $old_description;
|
1220
|
delete $event{'theme'}; }
|
1221
|
elsif ($THEMECACHE{$event{'theme'}}) {
|
1222
|
$event{'theme'} = $THEMECACHE{$event{'theme'}};
|
1223
|
}
|
1224
|
}
|
1225
|
}
|
1226
|
|
1227
|
$event{'subtitle'} =~ s/^All\sDay\s\(.*\sEastern\)\s*$//;
|
1228
|
|
1229
|
if ($event{'subtitle'} =~ /Free\spreview\suntil/) {
|
1230
|
$event{'subtitle'} = '';
|
1231
|
};
|
1232
|
$event{'subtitle'} =~ s/\&/\&/g;
|
1233
|
|
1234
|
my @actors;
|
1235
|
|
1236
|
$description =~ s/.*SAME\sDAY\sAS\sDVD\!//;
|
1237
|
$description =~ s/.*SAME\sAS\sDVD\!//;
|
1238
|
$description =~ s/.*jour\sque\sle\sDVD\!//;
|
1239
|
$description =~ s/^\s*\-\.\s*//;
|
1240
|
$description =~ s/\&/\&/g;
|
1241
|
|
1242
|
if ($description =~ /\s\s\((\d{4})\)/) { # year
|
1243
|
$event{'year'} = $1;
|
1244
|
|
1245
|
my $left;
|
1246
|
($left, $description) = split(/\(\d{4}\)/, $description, 2);
|
1247
|
$left = cleanupString($left);
|
1248
|
|
1249
|
$description = cleanupString($description);
|
1250
|
|
1251
|
if ($left && ($left !~ /Off\sAir/) && ($left !~ /\s\-\s/) &&
|
1252
|
($left !~ /^\d/) && ($left !~ /\!/) && ($left !~ /\$/)) { # actors
|
1253
|
$left = cleanupString($left);
|
1254
|
$left =~ s/\.$//;
|
1255
|
my @_actors = split(/\,\s/, $left);
|
1256
|
@actors = (@actors, @_actors);
|
1257
|
}
|
1258
|
}
|
1259
|
|
1260
|
# We may have actors even without a year
|
1261
|
if ($description =~ /Voice\sof\:\s/) {
|
1262
|
my $actors;
|
1263
|
($actors, $description) = split(/\./, $description, 2);
|
1264
|
$description = cleanupString($description);
|
1265
|
$actors = cleanupString($actors);
|
1266
|
my @_actors = split(/\,\s/, $actors);
|
1267
|
@actors = (@actors, @_actors);
|
1268
|
}
|
1269
|
|
1270
|
$description =~ s/^\s+//;
|
1271
|
|
1272
|
my $position = index($description, '.');
|
1273
|
my $init_test = substr($description, ($position - 2), 4);
|
1274
|
|
1275
|
if ($init_test =~ /\s\w\.\s/) {
|
1276
|
$position = index($description, '.', ($position + 1));
|
1277
|
}
|
1278
|
if (($position > 1) && (($position + 1) < length($description))) {
|
1279
|
my $test = substr($description, 0, $position);
|
1280
|
|
1281
|
if ($test =~ /\,\s/) {
|
1282
|
my @_actors;
|
1283
|
my $no_actors = FALSE;
|
1284
|
foreach my $actor (split(/\,\s/, $test)) {
|
1285
|
my $spaces = ($actor =~ tr/ //);
|
1286
|
|
1287
|
if (($spaces == 1) || ($spaces == 2)) {
|
1288
|
push @_actors, $actor;
|
1289
|
} else {
|
1290
|
# bale out!
|
1291
|
$no_actors = TRUE;
|
1292
|
last;
|
1293
|
}
|
1294
|
}
|
1295
|
|
1296
|
if (!$no_actors) {
|
1297
|
$description = substr($description, ($position + 1));
|
1298
|
@actors = (@actors, @_actors);
|
1299
|
}
|
1300
|
}
|
1301
|
}
|
1302
|
|
1303
|
$event{'actors'} = \@actors if ($#actors > -1);
|
1304
|
|
1305
|
if ($description =~ /\(Stereo\)/) {
|
1306
|
$event{'stereo'} = TRUE;
|
1307
|
$description =~ s/\(Stereo\)//;
|
1308
|
}
|
1309
|
|
1310
|
if ($description =~ /\(HD\)/) {
|
1311
|
$event{'hdtv'} = TRUE;
|
1312
|
$description =~ s/\(HD\)//;
|
1313
|
}
|
1314
|
|
1315
|
if ($description =~ /\(CC\)/) {
|
1316
|
$event{'closedcaptioned'} = TRUE;
|
1317
|
$description =~ s/\(CC\)//;
|
1318
|
}
|
1319
|
|
1320
|
if ($description =~ /\(SAP\)/) {
|
1321
|
$event{'closedcaptioned'} = TRUE;
|
1322
|
$description =~ s/\(SAP\)//;
|
1323
|
}
|
1324
|
|
1325
|
$description =~ s/\s*\([0-9|A-Z]{5}\)\s*$//;
|
1326
|
$description =~ s/New\.\s*//;
|
1327
|
$description =~ s/\(DD\)\s*//;
|
1328
|
$description =~ s/\(All\sDay\s*//;
|
1329
|
$description =~ s/(Series|Season)\s(Finale|Premier|Premiere)\.\s*//;
|
1330
|
$description =~ s/Premiere*(\.|\!)\s*//;
|
1331
|
$description =~ s/\-*Call\s1\-(\d+).*ORDER\..*\#*//;
|
1332
|
$description =~ s/Event\sID\s*\#//;
|
1333
|
$description =~ s/Event\s*\#//;
|
1334
|
$description =~ s/Appelez\s1\-\d+.*\s\#*//;
|
1335
|
$description =~ s/\[Avail\.\sin\sHD\son\s.*\]//;
|
1336
|
|
1337
|
$event{'description'} = cleanupString($description);
|
1338
|
|
1339
|
return \%event;
|
1340
|
}
|
1341
|
|
1342
|
sub processProperties {
|
1343
|
my $raw = shift;
|
1344
|
my %properties;
|
1345
|
|
1346
|
foreach my $property ($raw =~ m/\{(.+?)\}/g) {
|
1347
|
my($numeric, undef) = split(/\|/, $property, 2);
|
1348
|
|
1349
|
SWITCH: {
|
1350
|
$numeric == 6 && do {
|
1351
|
$properties{'closedcaptioned'} = TRUE;
|
1352
|
last SWITCH;
|
1353
|
};
|
1354
|
$numeric == 7 && do {
|
1355
|
$properties{'stereo'} = TRUE;
|
1356
|
last SWITCH;
|
1357
|
};
|
1358
|
}
|
1359
|
}
|
1360
|
|
1361
|
return \%properties;
|
1362
|
}
|
1363
|
|
1364
|
sub processStars {
|
1365
|
my $raw = shift;
|
1366
|
|
1367
|
my @stars = (undef, 1, 1.5, 2, 2.5, 3, 3.5, 4);
|
1368
|
return @stars[$raw] / 4;
|
1369
|
}
|
1370
|
|
1371
|
# Ported from MythTV
|
1372
|
sub utcOffset {
|
1373
|
my $t = time();
|
1374
|
my $utc = mktime(gmtime($t));
|
1375
|
my $loc = mktime(localtime($t));
|
1376
|
|
1377
|
my $utc_offset = $loc - $utc;
|
1378
|
|
1379
|
# clamp to nearest minute if within 10 seconds
|
1380
|
my $off = $utc_offset % 60;
|
1381
|
$utc_offset -= $off if (abs($off) < 10);
|
1382
|
$utc_offset -= 60 + $off if (($off < -50) && ($off > -60));
|
1383
|
$utc_offset += 60 - $off if (($off > +50) && ($off < +60));
|
1384
|
|
1385
|
return $utc_offset;
|
1386
|
}
|
1387
|
|
1388
|
############################################################################
|
1389
|
# Debugging functions #
|
1390
|
############################################################################
|
1391
|
|
1392
|
sub logMissingCategory {
|
1393
|
my $nibble = shift;
|
1394
|
my $theme = shift;
|
1395
|
my $title = shift;
|
1396
|
my $description = shift;
|
1397
|
my $io = new IO::File $_MISSING_CATS_LOG_, O_WRONLY|O_APPEND;
|
1398
|
|
1399
|
return if (!$io);
|
1400
|
|
1401
|
print $io sprintf("0x%02x: %s: %s: %s\n", $nibble, $theme, $title, $description);
|
1402
|
|
1403
|
close $io;
|
1404
|
}
|
1405
|
|
1406
|
sub saveHash {
|
1407
|
my $file = shift;
|
1408
|
my $hash = shift;
|
1409
|
|
1410
|
$Data::Dumper::Purity = 1;
|
1411
|
|
1412
|
my $fd = new IO::File $file, O_CREAT|O_WRONLY;
|
1413
|
|
1414
|
return TRUE if (!defined($fd));
|
1415
|
|
1416
|
print $fd (Data::Dumper->Dump([$hash], [qw(foo)]));
|
1417
|
|
1418
|
close $fd;
|
1419
|
|
1420
|
return FALSE;
|
1421
|
}
|
1422
|
|
1423
|
sub getHash {
|
1424
|
my $file = shift;
|
1425
|
my $foo;
|
1426
|
my $buffer;
|
1427
|
|
1428
|
my $fd = new IO::File $file, O_RDONLY;
|
1429
|
|
1430
|
return undef if (!defined($fd));
|
1431
|
|
1432
|
while (my $line = <$fd>) {
|
1433
|
$buffer .= $line;
|
1434
|
}
|
1435
|
|
1436
|
close $fd;
|
1437
|
|
1438
|
eval $buffer;
|
1439
|
|
1440
|
return $foo;
|
1441
|
}
|
1442
|
|
1443
|
sub to_hex {
|
1444
|
my $string = shift;
|
1445
|
my @array = map(ord, split(//, $string));
|
1446
|
|
1447
|
return \@array;
|
1448
|
}
|
1449
|
|
1450
|
sub hex_ascii {
|
1451
|
my $section_a = shift;
|
1452
|
my $hex;
|
1453
|
my $ascii;
|
1454
|
my @section_a;
|
1455
|
my $buf;
|
1456
|
|
1457
|
$buf = "\n";
|
1458
|
|
1459
|
for (my $i = 0; $i <= $#{$section_a}; $i++) {
|
1460
|
$hex = $ascii = undef;
|
1461
|
my $i_t = sprintf("%04x", $i);
|
1462
|
for (my $x = 0; $x < 16; $x++) {
|
1463
|
if ($i > $#{$section_a}) {
|
1464
|
$hex .= ' ';
|
1465
|
$ascii .= ' ';
|
1466
|
} else {
|
1467
|
my $char = $$section_a[$i++];
|
1468
|
$hex .= ' '.sprintf("%02x", $char);
|
1469
|
if (($char >= 32) && ($char < 127)) {
|
1470
|
$ascii .= chr($char);
|
1471
|
} else {
|
1472
|
$ascii .= '.';
|
1473
|
}
|
1474
|
}
|
1475
|
|
1476
|
$hex .= ' ' if ($x == 7);
|
1477
|
}
|
1478
|
|
1479
|
$i--;
|
1480
|
|
1481
|
$buf .= "$i_t: $hex $ascii\n";
|
1482
|
}
|
1483
|
|
1484
|
$buf .= "\n";
|
1485
|
|
1486
|
return $buf;
|
1487
|
}
|
1488
|
|
1489
|
sub preparefile {
|
1490
|
my $xmlfile_s = shift;
|
1491
|
open(XMLFILE,">".$xmlfile_s) or die "could not open file $xmlfile_s: $!\n";
|
1492
|
print XMLFILE "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
|
1493
|
print XMLFILE "<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n\n";
|
1494
|
print XMLFILE "<tv source-info-name=\"EIT\" generator-info-name=\"geteit\" generator-info-url=\"mailto:me\@mine.com\">\n";
|
1495
|
return;
|
1496
|
}
|
1497
|
|
1498
|
sub closefile {
|
1499
|
print XMLFILE "</tv>\n";
|
1500
|
close XMLFILE;
|
1501
|
}
|
1502
|
|
1503
|
sub realDate {
|
1504
|
my $seconds = shift;
|
1505
|
my ($second, $minute, $hour, $day, $month, $year) = localtime($seconds);
|
1506
|
|
1507
|
return sprintf("%02i%02i%02i%02i%02i%02i",
|
1508
|
($year + 1900), ($month + 1), $day, $hour, $minute, $second);
|
1509
|
}
|
1510
|
|