1
|
#!/usr/bin/perl
|
2
|
# Perl version of AutoBouquets E2 28.2E by LraiZer for www.ukcvs.org
|
3
|
# modified to integrate with TVHeadend
|
4
|
# by Jonathan Kempson - [email protected]
|
5
|
|
6
|
use 5.010;
|
7
|
use strict;
|
8
|
use warnings;
|
9
|
|
10
|
use Data::Dumper;
|
11
|
use LWP::Simple qw(get);
|
12
|
use JSON;
|
13
|
use URI::Escape;
|
14
|
use List::Util qw(first);
|
15
|
use LWP::Simple qw($ua head);
|
16
|
|
17
|
# Required regional data/region here (use SD version if HD channel verification required)
|
18
|
my $data_sd = "4097";
|
19
|
my $region_sd = "07";
|
20
|
|
21
|
# HD data/region here to be verified. Set to "" to skip HD channel verification
|
22
|
my $data_hd = "";
|
23
|
my $region_hd = "";
|
24
|
|
25
|
# TVH configuration
|
26
|
my $tag_channels = 1;
|
27
|
my $tvh_user = "someuser";
|
28
|
my $tvh_pass = "somepassword";
|
29
|
my $tvh_ip = "someip";
|
30
|
my $tvh_proto = "http";
|
31
|
my $tvh_port = "9981";
|
32
|
my $tvh_tag = "TV channels";
|
33
|
my $icons_enabled = 1;
|
34
|
|
35
|
# Change 'on' => 1 to 'on' => 0 if you don't want that channel group
|
36
|
my %chan_tags = (
|
37
|
'Entertainment' => { 'num' => 101, 'on' => 1 },
|
38
|
'Lifestyle and Culture' => { 'num' => 240, 'on' => 1 },
|
39
|
'Movies' => { 'num' => 301, 'on' => 1 },
|
40
|
'Music' => { 'num' => 350, 'on' => 1 },
|
41
|
'Sports' => { 'num' => 401, 'on' => 1 },
|
42
|
'News' => { 'num' => 501, 'on' => 1 },
|
43
|
'Documentaries' => { 'num' => 520, 'on' => 1 },
|
44
|
'Religious' => { 'num' => 580, 'on' => 1 },
|
45
|
'Kids' => { 'num' => 601, 'on' => 1 },
|
46
|
'Shopping' => { 'num' => 640, 'on' => 1 },
|
47
|
'Sky Box Office' => { 'num' => 700, 'on' => 1 },
|
48
|
'International' => { 'num' => 780, 'on' => 1 },
|
49
|
'Gaming and Dating' => { 'num' => 861, 'on' => 1 },
|
50
|
'Specialist' => { 'num' => 881, 'on' => 1 },
|
51
|
'Adult' => { 'num' => 900, 'on' => 1 },
|
52
|
'Other' => { 'num' => 950, 'on' => 1 },
|
53
|
'Radio' => { 'num' => 3101, 'on' => 0 },
|
54
|
);
|
55
|
|
56
|
# Manual add channel, number:SID, for channels that may not be in your bouquet
|
57
|
my @sid_adds = ("131:10155");
|
58
|
|
59
|
say "Started";
|
60
|
my $tvh_url = $tvh_proto . "\:\/\/" . $tvh_user . "\:" . $tvh_pass . "\@" . $tvh_ip . "\:" . $tvh_port;
|
61
|
|
62
|
say "Get Channel Tags...";
|
63
|
my %chan_ids = tvhTagHash( $tvh_tag, \%chan_tags );
|
64
|
say "Done";
|
65
|
|
66
|
#my $f_dvb = "/tmp/abm_dvb.txt";
|
67
|
#my @dvb = readFile($f_dvb);
|
68
|
|
69
|
# Run dvbsnoop
|
70
|
my @dvb = `dvbsnoop -nph -n 500 0x11`;
|
71
|
if ( scalar(@dvb) <= 1 ) { die "No data received. You must have root access?"; }
|
72
|
|
73
|
# Get SDT
|
74
|
say "Get SDT...";
|
75
|
my @sdt = serviceDescriptionTable( \@dvb );
|
76
|
say "Done";
|
77
|
|
78
|
|
79
|
# Get service lists
|
80
|
say "Get services";
|
81
|
my @services_sd = populateServices($data_sd, $region_sd, @dvb, @sdt);
|
82
|
@services_sd = sort( uniqArray(@services_sd) );
|
83
|
say "Done";
|
84
|
|
85
|
my @merged_services;
|
86
|
|
87
|
if (($data_hd ne "") && ($region_hd ne "")) {
|
88
|
my @services_hd = populateServices($data_hd, $region_hd, @dvb, @sdt);
|
89
|
@services_hd = sort( uniqArray(@services_hd) );
|
90
|
my @services_hd_checked;
|
91
|
$ua->timeout(10);
|
92
|
my @tvh_services = tvhQuery("mpegts/service/grid?dir=ASC&limit=100000&sort=sid&start=0");
|
93
|
foreach my $s_hd (@services_hd) {
|
94
|
my $dupe = 0;
|
95
|
foreach my $s_sd (@services_sd) {
|
96
|
if ($s_hd eq $s_sd) { $dupe = 1; }
|
97
|
}
|
98
|
|
99
|
if ($dupe == 0) {
|
100
|
my @conv = split( " ", $s_hd );
|
101
|
my $serv_type = tvhServiceType( hex( $conv[2] ), \@tvh_services );
|
102
|
|
103
|
# dvb_servicetype seems to be HD channels? Use to ignore SD channels returned in HD bouquet.
|
104
|
if ($serv_type == 25) {
|
105
|
my $chan_num = hex( $conv[0] );
|
106
|
my $chan_uuid = tvhServiceID( hex( $conv[2] ), \@tvh_services );
|
107
|
my $chan_name = lookupService( "$conv[2]", \@sdt );
|
108
|
my $url = "$tvh_url/stream/service/$chan_uuid";
|
109
|
print "Testing channel $chan_num $chan_name...";
|
110
|
if (head($url)) {
|
111
|
push (@services_hd_checked, $s_hd);
|
112
|
print "ok\n";
|
113
|
} else {
|
114
|
print "fail\n";
|
115
|
}
|
116
|
}
|
117
|
}
|
118
|
|
119
|
}
|
120
|
foreach my $s_sd (@services_sd) {
|
121
|
my @conv = split( " ", $s_sd );
|
122
|
my $chan_num = hex( $conv[0] );
|
123
|
my $replace = $s_sd;
|
124
|
foreach my $s_hd (@services_hd_checked) {
|
125
|
my @conv_hd = split( " ", $s_hd );
|
126
|
my $chan_num_hd = hex( $conv_hd[0] );
|
127
|
if ($chan_num eq $chan_num_hd) { $replace = $s_hd; }
|
128
|
}
|
129
|
push (@merged_services, $replace);
|
130
|
}
|
131
|
} else {
|
132
|
@merged_services = @services_sd;
|
133
|
}
|
134
|
|
135
|
foreach my $add (@sid_adds) {
|
136
|
my @arr = split(":",$add);
|
137
|
$arr[0] = sprintf( "%04x", $arr[0] );
|
138
|
$arr[1] = sprintf( "%04x", $arr[1] );
|
139
|
push @merged_services, "$arr[0] 0000 $arr[1] 0000";
|
140
|
}
|
141
|
@merged_services = sort( uniqArray(@merged_services) );
|
142
|
|
143
|
# Populate channels in TVH
|
144
|
say "Populate channels in TVH...";
|
145
|
updateTVH( \@merged_services, $tvh_tag, $tag_channels );
|
146
|
say "Done";
|
147
|
|
148
|
exit;
|
149
|
|
150
|
################################################################################
|
151
|
|
152
|
sub populateServices {
|
153
|
my ( $data, $region, $dvb, $sdt ) = @_;
|
154
|
my (@svcs, @sections);
|
155
|
|
156
|
$region = sprintf( "%02x", $region );
|
157
|
|
158
|
say "Process regional services first...";
|
159
|
processServices( $data, $region, \@dvb, \@svcs, \@sdt );
|
160
|
say "Done";
|
161
|
|
162
|
say "Everything else...";
|
163
|
|
164
|
if ( ( $region ne "33" ) && ( $region ne "32" ) ) {
|
165
|
|
166
|
# Use different base services if region is Irish
|
167
|
processServices( "4104", "21", \@dvb, \@svcs, \@sdt );
|
168
|
}
|
169
|
else {
|
170
|
processServices( "4101", "01", \@dvb, \@svcs, \@sdt );
|
171
|
}
|
172
|
|
173
|
say "Done";
|
174
|
return @svcs;
|
175
|
}
|
176
|
|
177
|
sub updateTVH {
|
178
|
my ( $services, $tvh_tag, $tag_channels ) = @_;
|
179
|
my @tvh_tags = tvhQuery("channeltag/list?enum=true");
|
180
|
|
181
|
my $tag_id = tvhTagID($tvh_tag);
|
182
|
|
183
|
# Get all services from tvh
|
184
|
my @tvh_services = tvhQuery("mpegts/service/grid?dir=ASC&limit=100000&sort=sid&start=0");
|
185
|
my @tvh_channels = tvhQuery("channel/grid?dir=ASC&limit=100000&sort=number&start=0");
|
186
|
|
187
|
my @chan_list;
|
188
|
my @sid_dupes;
|
189
|
|
190
|
foreach my $line (@$services) {
|
191
|
my @conv = split( " ", $line );
|
192
|
|
193
|
my $chan_num = hex( $conv[0] );
|
194
|
my $chan_name = lookupService( "$conv[2]", \@sdt );
|
195
|
my $chan_epg = hex( $conv[1] );
|
196
|
my $chan_logo = "http://tv.sky.com/logo/0/0/skychb" . $chan_epg . ".png";
|
197
|
|
198
|
|
199
|
my ( @j_sid, @j_tag );
|
200
|
my $chan_uuid = tvhServiceID( hex( $conv[2] ), \@tvh_services );
|
201
|
push @j_sid, $chan_uuid;
|
202
|
|
203
|
# Duplicate channels with different channel numbers seem to cause problems with EPG in XBMC (perhaps others)?
|
204
|
# EPG sometimes isn't imported for channels which are assigned multiple channel numbers.
|
205
|
# Skip channel if it has been seen earlier.
|
206
|
my $skip = 0;
|
207
|
if ( first { $_ eq $chan_uuid } @sid_dupes ) {
|
208
|
$skip = 1;
|
209
|
}
|
210
|
push @sid_dupes, $chan_uuid;
|
211
|
|
212
|
# Ignore useless channels 65535
|
213
|
if ( $chan_num == 65535 ) { $skip = 1; }
|
214
|
|
215
|
push @j_tag, $tag_id;
|
216
|
|
217
|
# Add tag info if enabled
|
218
|
if ( $tag_channels == 1 ) {
|
219
|
if ( $chan_tags{ chanToTag($chan_num) }->{on} == 1 ) {
|
220
|
push @j_tag, $chan_ids{ chanToTag($chan_num) };
|
221
|
}
|
222
|
else {
|
223
|
$skip = 1;
|
224
|
}
|
225
|
}
|
226
|
|
227
|
# Create hash of channel data to be added
|
228
|
if ( $skip == 0 ) {
|
229
|
my %chan_hash = (
|
230
|
'name' => $chan_name,
|
231
|
'number' => $chan_num,
|
232
|
'services' => \@j_sid,
|
233
|
'tags' => \@j_tag,
|
234
|
'dvr_pre_time' => '0',
|
235
|
'dvr_pst_time' => '0'
|
236
|
);
|
237
|
|
238
|
if ($icons_enabled !=0) {
|
239
|
$chan_hash{'icon'} = $chan_logo;
|
240
|
}
|
241
|
|
242
|
push @chan_list, $chan_num;
|
243
|
tvhUpdateChannel( \@tvh_channels, $tag_id, \%chan_hash );
|
244
|
}
|
245
|
}
|
246
|
|
247
|
# Remove orphan channels
|
248
|
foreach my $chan (@tvh_channels) {
|
249
|
my $tag_check = 0;
|
250
|
foreach my $tag ( @{ $chan->{tags} } ) {
|
251
|
if ( $tag eq $tag_id ) { $tag_check = 1; }
|
252
|
}
|
253
|
|
254
|
my $found = 0;
|
255
|
for my $line (@chan_list) {
|
256
|
if ( $chan->{number} eq $line ) { $found = 1; }
|
257
|
}
|
258
|
|
259
|
if ( ( $found == 0 ) && ( $tag_check == 1 ) ) {
|
260
|
say "Removing orphaned channel $chan->{number} $chan->{name}";
|
261
|
get( $tvh_url . "/api/idnode/delete?uuid=" . uri_escape( $chan->{uuid} ) );
|
262
|
}
|
263
|
}
|
264
|
|
265
|
}
|
266
|
|
267
|
sub tvhTagHash {
|
268
|
my ( $tvh_tag, $chan_tags ) = @_;
|
269
|
my @tvh_tags;
|
270
|
push @tvh_tags, $tvh_tag;
|
271
|
|
272
|
foreach my $ch ( sort keys %chan_tags ) {
|
273
|
push @tvh_tags, $ch;
|
274
|
}
|
275
|
|
276
|
my %chan_ids;
|
277
|
|
278
|
foreach my $ch (@tvh_tags) {
|
279
|
my $tag_id = tvhTagID($ch);
|
280
|
if ( !$tag_id ) {
|
281
|
$tag_id = tvhCreateTag($ch);
|
282
|
}
|
283
|
$chan_ids{$ch} = $tag_id;
|
284
|
}
|
285
|
return %chan_ids;
|
286
|
}
|
287
|
|
288
|
sub chanToTag {
|
289
|
my ( $chan, $chan_tags ) = @_;
|
290
|
my $tag = "";
|
291
|
foreach my $n ( sort { $chan_tags{$b}->{num} <=> $chan_tags{$a}->{num} } keys %chan_tags ) {
|
292
|
if ( $chan >= $chan_tags{$n}->{num} ) {
|
293
|
$tag = $n;
|
294
|
last;
|
295
|
}
|
296
|
}
|
297
|
return $tag;
|
298
|
}
|
299
|
|
300
|
sub tvhTagID {
|
301
|
my ($tvh_tag) = @_;
|
302
|
my @tvh_tags = tvhQuery("channeltag/list?enum=true");
|
303
|
my $tag_id;
|
304
|
|
305
|
foreach my $tag (@tvh_tags) {
|
306
|
if ( $tag->{val} eq $tvh_tag ) { $tag_id = $tag->{key}; }
|
307
|
}
|
308
|
|
309
|
return $tag_id;
|
310
|
}
|
311
|
|
312
|
sub tvhCreateTag {
|
313
|
my ($tag_name) = @_;
|
314
|
my $post = "{\"enabled\":true,\"name\":\"" . $tag_name . "\",\"internal\":false,\"icon\":\"\",\"titled_icon\":false,\"comment\":\"\"}";
|
315
|
say "Adding channel tag $tag_name";
|
316
|
get( $tvh_url."/api/channeltag/create?conf=".uri_escape($post));
|
317
|
return tvhTagID($tag_name);
|
318
|
}
|
319
|
|
320
|
sub tvhUpdateChannel {
|
321
|
my ( $tvh_channels, $tag_id, $post_json ) = @_;
|
322
|
my $matches = 0;
|
323
|
my $chan_exist = "";
|
324
|
|
325
|
for my $line (@$tvh_channels) {
|
326
|
my $tag_check = 0;
|
327
|
foreach my $tag ( @{ $line->{tags} } ) {
|
328
|
if ( $tag eq $tag_id ) { $tag_check = 1; }
|
329
|
}
|
330
|
|
331
|
if ( $tag_check == 1 ) {
|
332
|
if ( $line->{number} eq $post_json->{number} ) {
|
333
|
$matches = 1;
|
334
|
$chan_exist = $line->{uuid};
|
335
|
my $servs = join( "", sort( @{ $line->{services} } ) );
|
336
|
my $post_servs = join( "", sort( @{ $post_json->{services} } ) );
|
337
|
|
338
|
my $tags = join( "", sort( @{ $line->{tags} } ) );
|
339
|
my $post_tags = join( "", sort( @{ $post_json->{tags} } ) );
|
340
|
|
341
|
if ( $line->{name} ne $post_json->{name} ) { $matches = 0 }
|
342
|
|
343
|
if ($icons_enabled == 1) {
|
344
|
if ( $line->{icon} ne $post_json->{icon} ) { $matches = 0 }
|
345
|
}
|
346
|
|
347
|
if ( $servs ne $post_servs ) { $matches = 0 }
|
348
|
if ( $tags ne $post_tags ) { $matches = 0 }
|
349
|
last;
|
350
|
}
|
351
|
}
|
352
|
}
|
353
|
|
354
|
if ( $matches == 0 ) {
|
355
|
if ( $chan_exist ne "" ) {
|
356
|
say "Deleting old channel $post_json->{number}";
|
357
|
get( $tvh_url . "/api/idnode/delete?uuid=" . uri_escape($chan_exist) );
|
358
|
}
|
359
|
say "Adding channel $post_json->{number} $post_json->{name}";
|
360
|
get( $tvh_url . "/api/channel/create?conf=" . uri_escape( encode_json($post_json) ) );
|
361
|
}
|
362
|
}
|
363
|
|
364
|
sub tvhQuery {
|
365
|
my ($query) = @_;
|
366
|
my $url = $tvh_url . '/api/' . $query;
|
367
|
my $return = from_json( get($url) );
|
368
|
return @{ $return->{entries} };
|
369
|
}
|
370
|
|
371
|
sub tvhServiceID {
|
372
|
my ( $search, $decoded ) = @_;
|
373
|
my $uuid = "";
|
374
|
foreach my $line (@$decoded) {
|
375
|
if ( $line->{sid} eq $search ) { $uuid = $line->{uuid}; last; }
|
376
|
}
|
377
|
|
378
|
return $uuid;
|
379
|
}
|
380
|
|
381
|
sub tvhServiceType {
|
382
|
my ( $search, $decoded ) = @_;
|
383
|
my $uuid = "";
|
384
|
foreach my $line (@$decoded) {
|
385
|
if ( $line->{sid} eq $search ) { $uuid = $line->{dvb_servicetype}; last; }
|
386
|
}
|
387
|
|
388
|
return $uuid;
|
389
|
}
|
390
|
|
391
|
sub uniqArray {
|
392
|
return keys %{ { map { $_ => 1 } @_ } };
|
393
|
}
|
394
|
|
395
|
sub processServices {
|
396
|
my ( $data, $region, $dvb, $svcs, $sdt ) = @_;
|
397
|
my @sections = findSections( $data, \@dvb );
|
398
|
return streamData( $region, \@sections, $svcs, \@sdt );
|
399
|
}
|
400
|
|
401
|
sub streamData {
|
402
|
my ( $region, $sections, $conv_data, $sdt ) = @_;
|
403
|
my ( @data, $hex, $ts, $code, $pcode );
|
404
|
|
405
|
# If there is already data in services array then turn on duplicate checking
|
406
|
my $check_dupes;
|
407
|
if ( scalar(@$conv_data) > 0 ) {
|
408
|
$check_dupes = 1;
|
409
|
}
|
410
|
else {
|
411
|
$check_dupes = 0;
|
412
|
}
|
413
|
|
414
|
foreach my $line (@$sections) {
|
415
|
|
416
|
if ( $line =~ /^ Transport_stream_ID/ ) {
|
417
|
($ts) = $line =~ /\(.*x([^\)]+)/;
|
418
|
$ts = trim($ts);
|
419
|
$pcode = -1;
|
420
|
push( @data, $hex );
|
421
|
$hex = $ts;
|
422
|
}
|
423
|
|
424
|
if ( $line =~ /^ 00/ ) {
|
425
|
$line =~ s /(.{74}).*/${1}/s;
|
426
|
$line =~ s/\h+/ /g;
|
427
|
$line = trim($line);
|
428
|
|
429
|
$code = hex( substr( $line, 0, 4 ) );
|
430
|
$line = substr( $line, 5 );
|
431
|
|
432
|
if ( $code <= $pcode ) {
|
433
|
push( @data, $hex );
|
434
|
$hex = $ts;
|
435
|
}
|
436
|
$pcode = $code;
|
437
|
|
438
|
$hex = $hex . $line;
|
439
|
}
|
440
|
}
|
441
|
push( @data, $hex );
|
442
|
shift @data;
|
443
|
|
444
|
filterServices( \@data, $region );
|
445
|
|
446
|
for my $line (@data) {
|
447
|
my $count = 1;
|
448
|
my $number = 7;
|
449
|
my $serv_count = length($line) / 27;
|
450
|
my $code = substr( $line, 0, 4 );
|
451
|
while ( $count < $serv_count ) {
|
452
|
my @s = split( " ", substr( $line, $number, 27 ) );
|
453
|
my $out = $s[6] . $s[7] . " " . $s[4] . $s[5] . " " . $s[1] . $s[2] . " " . $code;
|
454
|
|
455
|
my $found = 0;
|
456
|
if ( $check_dupes eq 1 ) {
|
457
|
my $d = substr( $out, 0, 4 );
|
458
|
if ( grep( /^$d/i, @$conv_data ) ) { $found = 1; }
|
459
|
}
|
460
|
if ( $found == 0 ) {
|
461
|
push( @$conv_data, $out );
|
462
|
}
|
463
|
|
464
|
$number = $number + 27;
|
465
|
$count++;
|
466
|
}
|
467
|
}
|
468
|
return @$conv_data;
|
469
|
}
|
470
|
|
471
|
sub filterServices {
|
472
|
my ( $data, $region ) = @_;
|
473
|
my $i = 0;
|
474
|
do {
|
475
|
if ( ( substr( @$data[$i], 8, 2 ) ne $region ) && ( substr( @$data[$i], 5, 2 ) ne "ff" ) ) {
|
476
|
splice @$data, $i, 1;
|
477
|
}
|
478
|
else {
|
479
|
$i++;
|
480
|
}
|
481
|
} until ( $i == scalar(@$data) );
|
482
|
|
483
|
return @$data;
|
484
|
}
|
485
|
|
486
|
sub lookupService {
|
487
|
my $search = shift;
|
488
|
my $sdt = shift;
|
489
|
foreach my $line (@$sdt) {
|
490
|
if ( $line =~ /^$search/ ) {
|
491
|
$search = substr( $line, 10 );
|
492
|
last;
|
493
|
}
|
494
|
}
|
495
|
return $search;
|
496
|
}
|
497
|
|
498
|
sub findSections {
|
499
|
|
500
|
# Extract relevant sections from dvbsnoop array based on $data bouquet info
|
501
|
my ( $data, $dvb ) = @_;
|
502
|
|
503
|
my @sections_ref = sectionsRef( $data, \@dvb );
|
504
|
my @sections;
|
505
|
|
506
|
my $sec_first = ( @$dvb[ $sections_ref[0] + 18 ] =~ /Section_number: (.*) \(/ )[0];
|
507
|
my $sec_last = ( @$dvb[ $sections_ref[0] + 19 ] =~ /Section_number: (.*) \(/ )[0];
|
508
|
my $sec_cycle;
|
509
|
|
510
|
# Set loop stop point to prevent repeat data
|
511
|
if ( $sec_first == 0 ) {
|
512
|
$sec_cycle = $sec_last;
|
513
|
}
|
514
|
else {
|
515
|
$sec_cycle = $sec_first - 1;
|
516
|
}
|
517
|
|
518
|
foreach my $line (@sections_ref) {
|
519
|
my $strt = $line;
|
520
|
do {
|
521
|
$strt++;
|
522
|
push( @sections, @$dvb[$strt] );
|
523
|
} while !( @$dvb[$strt] =~ /CRC/ );
|
524
|
|
525
|
# sec_num = current sequence position
|
526
|
my $sec_num = ( @$dvb[ $line + 18 ] =~ /Section_number: (.*) \(/ )[0];
|
527
|
|
528
|
# If position in sequence matches stop point then exit
|
529
|
if ( $sec_num == $sec_cycle ) { last; }
|
530
|
}
|
531
|
return @sections;
|
532
|
}
|
533
|
|
534
|
sub sectionsRef {
|
535
|
|
536
|
# Extract positions of Bouquet_ID for $data in dvbsnoop array
|
537
|
my ( $data, $dvb ) = @_;
|
538
|
my $cnt = 0;
|
539
|
my @sections;
|
540
|
foreach my $line (@$dvb) {
|
541
|
if ( $line =~ /Bouquet_ID: $data/ ) {
|
542
|
push( @sections, ( $cnt - 14 ) . "\n" );
|
543
|
}
|
544
|
$cnt++;
|
545
|
}
|
546
|
return @sections;
|
547
|
}
|
548
|
|
549
|
sub serviceDescriptionTable {
|
550
|
|
551
|
# Finds all service data from the dvbsnoop array
|
552
|
my $dvb = shift;
|
553
|
|
554
|
# Match any line in this array
|
555
|
my $match = join( "|",
|
556
|
qr/^Transport_Stream_ID:/,
|
557
|
qr/^ Service_id:/,
|
558
|
qr/^ Free_CA_mode:/,
|
559
|
qr/^ service_provider_name:/,
|
560
|
qr/^ Service_name:/,
|
561
|
qr/^ 0000: /,
|
562
|
);
|
563
|
|
564
|
# Do not include matching line if a second check matches any values here (not sure why?)
|
565
|
my $no_match = join( "|", qr/^ 0000: 00/, qr/^ 0000: ff/, qr/^ 0000: 1d/, );
|
566
|
|
567
|
my @services;
|
568
|
foreach my $line (@$dvb) {
|
569
|
if ( ( $line =~ $match ) && !( $line =~ $no_match ) ) {
|
570
|
push( @services, $line );
|
571
|
}
|
572
|
}
|
573
|
return processSDT( \@services );
|
574
|
}
|
575
|
|
576
|
sub processSDT {
|
577
|
my $services = shift;
|
578
|
my @return;
|
579
|
my ( $tsid, $sid, $sn, $ca, $spn );
|
580
|
|
581
|
foreach my $line (@$services) {
|
582
|
if ( $line =~ /Transport_Stream_ID/ ) {
|
583
|
$tsid = get_hex($line);
|
584
|
}
|
585
|
|
586
|
if ( $line =~ /Service_id:/ ) {
|
587
|
$sid = get_hex($line);
|
588
|
$sn = "";
|
589
|
}
|
590
|
|
591
|
if ( $line =~ /Free_CA_mode:/ ) {
|
592
|
$ca = get_hex($line);
|
593
|
if ( $ca == 0 ) { $ca = "FTA"; }
|
594
|
else { $ca = "NDS"; }
|
595
|
}
|
596
|
if ( $line =~ /service_provider_name:/ ) {
|
597
|
$spn = get_txt($line);
|
598
|
}
|
599
|
|
600
|
if ( $line =~ /Service_name:/ ) {
|
601
|
$sn = get_txt($line);
|
602
|
|
603
|
#push @return, "$sid:$tsid#$ca:$spn:$sn";
|
604
|
push @return, "$sid:$tsid:$sn";
|
605
|
}
|
606
|
|
607
|
if ( $line =~ /0000:/ ) {
|
608
|
if ( length($sn) == 0 ) {
|
609
|
|
610
|
# Need a regexp here, this isn't nice :S
|
611
|
$sn = trim( substr( $line, 70 ) );
|
612
|
|
613
|
# push @return, "$sid:$tsid#$ca:BSkyB:$sn";
|
614
|
push @return, "$sid:$tsid:$sn";
|
615
|
}
|
616
|
}
|
617
|
}
|
618
|
|
619
|
@return = sort( uniqArray(@return) );
|
620
|
return @return;
|
621
|
}
|
622
|
|
623
|
sub get_txt {
|
624
|
if ( $_[0] =~ /"(.+?)"/ ) { return $1; }
|
625
|
}
|
626
|
|
627
|
sub get_hex {
|
628
|
my $return;
|
629
|
($return) = $_[0] =~ /\(.*x([^\)]+)/;
|
630
|
$return = trim($return);
|
631
|
return $return;
|
632
|
}
|
633
|
|
634
|
sub trim {
|
635
|
( my $s = $_[0] ) =~ s/^\s+|\s+$//g;
|
636
|
return $s;
|
637
|
}
|
638
|
|
639
|
sub readFile {
|
640
|
open( FH, $_[0] );
|
641
|
my @buf = <FH>;
|
642
|
close(FH);
|
643
|
return @buf;
|
644
|
}
|