mlbserver 2021.10.11 → 2022.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.js +259 -102
- package/package.json +1 -1
- package/session.js +351 -269
package/README.md
CHANGED
package/index.js
CHANGED
|
@@ -37,7 +37,7 @@ const DEFAULT_MULTIVIEW_AUDIO_TRACK = 'English'
|
|
|
37
37
|
const VALID_SKIP = [ 'off', 'breaks', 'pitches' ]
|
|
38
38
|
const VALID_FORCE_VOD = [ 'off', 'on' ]
|
|
39
39
|
|
|
40
|
-
const SAMPLE_STREAM_URL = 'https://
|
|
40
|
+
const SAMPLE_STREAM_URL = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
|
|
41
41
|
|
|
42
42
|
// Basic command line arguments, if specified:
|
|
43
43
|
// --port or -p (primary port to run on; defaults to 9999 if not specified)
|
|
@@ -72,7 +72,9 @@ var argv = minimist(process.argv, {
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
// Version
|
|
75
|
-
|
|
75
|
+
var version = require('./package').version
|
|
76
|
+
console.log('Version ' + version)
|
|
77
|
+
if (argv.version) return
|
|
76
78
|
|
|
77
79
|
// Declare a session, pass arguments to it
|
|
78
80
|
var session = new sessionClass(argv)
|
|
@@ -117,8 +119,8 @@ var appname = path.basename(__dirname)
|
|
|
117
119
|
// Multiview server variables
|
|
118
120
|
var hls_base = 'multiview'
|
|
119
121
|
var multiview_stream_name = 'master.m3u8'
|
|
120
|
-
if ( session.protection.content_protect ) multiview_stream_name += '?content_protect=' + session.protection.content_protect
|
|
121
122
|
var multiview_url_path = '/' + hls_base + '/' + multiview_stream_name
|
|
123
|
+
if ( session.protection.content_protect ) multiview_url_path += '?content_protect=' + session.protection.content_protect
|
|
122
124
|
session.setMultiviewStreamURLPath(multiview_url_path)
|
|
123
125
|
var ffmpeg_command
|
|
124
126
|
var ffmpeg_status = false
|
|
@@ -158,11 +160,12 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
158
160
|
let streamURL
|
|
159
161
|
let options = {}
|
|
160
162
|
let urlArray = req.url.split('?')
|
|
161
|
-
if ( (urlArray.length == 1) || ((session.data.scan_mode == 'on') && req.query.team) || (!req.query.team && !req.query.src && !req.query.highlight_src && !req.query.
|
|
163
|
+
if ( (urlArray.length == 1) || ((session.data.scan_mode == 'on') && req.query.team) || (!req.query.team && !req.query.src && !req.query.highlight_src && !req.query.event && !req.query.id && !req.query.mediaId && !req.query.contentId) ) {
|
|
162
164
|
// load a sample encrypted HLS stream
|
|
163
165
|
session.log('loading sample stream')
|
|
164
166
|
options.resolution = 'adaptive'
|
|
165
167
|
streamURL = SAMPLE_STREAM_URL
|
|
168
|
+
options.referer = 'https://hls-js.netlify.app/'
|
|
166
169
|
} else {
|
|
167
170
|
if ( req.query.resolution && (options.resolution == 'best') ) {
|
|
168
171
|
options.resolution = VALID_RESOLUTIONS[1]
|
|
@@ -181,8 +184,8 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
181
184
|
streamURL = req.query.src
|
|
182
185
|
} else if ( req.query.highlight_src ) {
|
|
183
186
|
streamURL = req.query.highlight_src
|
|
184
|
-
} else if ( req.query.
|
|
185
|
-
streamURL = await session.
|
|
187
|
+
} else if ( req.query.event ) {
|
|
188
|
+
streamURL = await session.getEventStreamURL(req.query.event.toUpperCase())
|
|
186
189
|
} else {
|
|
187
190
|
if ( req.query.contentId ) {
|
|
188
191
|
contentId = req.query.contentId
|
|
@@ -240,6 +243,11 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
245
|
|
|
246
|
+
if ( req.query.referer ) {
|
|
247
|
+
options.referer = req.query.referer
|
|
248
|
+
session.debuglog('sending referer : ' + options.referer)
|
|
249
|
+
}
|
|
250
|
+
|
|
243
251
|
getMasterPlaylist(streamURL, req, res, options)
|
|
244
252
|
} else {
|
|
245
253
|
session.log('failed to get streamURL : ' + req.url)
|
|
@@ -269,6 +277,11 @@ var getKey = function(url, headers, cb) {
|
|
|
269
277
|
})
|
|
270
278
|
}
|
|
271
279
|
|
|
280
|
+
function getOriginFromURL(url) {
|
|
281
|
+
let pathArray = url.split('/')
|
|
282
|
+
return pathArray[0] + '//' + pathArray[2]
|
|
283
|
+
}
|
|
284
|
+
|
|
272
285
|
// Default respond function, for adjusting content-length and updating CORS headers
|
|
273
286
|
var respond = function(proxy, res, body) {
|
|
274
287
|
delete proxy.headers['content-length']
|
|
@@ -307,6 +320,15 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
307
320
|
session.debuglog('getMasterPlaylist of streamURL : ' + streamURL)
|
|
308
321
|
var req = function () {
|
|
309
322
|
var headers = {}
|
|
323
|
+
var referer = false
|
|
324
|
+
var referer_parameter = ''
|
|
325
|
+
if ( options.referer ) {
|
|
326
|
+
referer = decodeURIComponent(options.referer)
|
|
327
|
+
headers.referer = referer
|
|
328
|
+
headers.origin = getOriginFromURL(referer)
|
|
329
|
+
session.debuglog('found stream referer : ' + referer)
|
|
330
|
+
referer_parameter = '&referer=' + encodeURIComponent(options.referer)
|
|
331
|
+
}
|
|
310
332
|
requestRetry(streamURL, headers, function(err, response) {
|
|
311
333
|
if (err) return res.error(err)
|
|
312
334
|
|
|
@@ -314,6 +336,26 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
314
336
|
|
|
315
337
|
var body = response.body.trim().split('\n')
|
|
316
338
|
|
|
339
|
+
// check if HLS
|
|
340
|
+
let hls_detected = false
|
|
341
|
+
for (var i=0; i<body.length; i++) {
|
|
342
|
+
if ( body[i] == '#EXTM3U' ) {
|
|
343
|
+
session.debuglog('hls detected')
|
|
344
|
+
hls_detected = true
|
|
345
|
+
break
|
|
346
|
+
} else if ( body[i] == '' ) {
|
|
347
|
+
session.debuglog('skipping blank lines at beginning of file')
|
|
348
|
+
continue
|
|
349
|
+
} else {
|
|
350
|
+
session.debuglog(body[i])
|
|
351
|
+
break
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if ( !hls_detected ) {
|
|
355
|
+
session.log('not a valid hls stream')
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
317
359
|
let resolution = options.resolution || VALID_RESOLUTIONS[0]
|
|
318
360
|
let audio_track = options.audio_track || VALID_AUDIO_TRACKS[0]
|
|
319
361
|
let audio_url = options.audio_url || ''
|
|
@@ -338,12 +380,13 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
338
380
|
var audio_track_matched = false
|
|
339
381
|
var frame_rate = '29.97'
|
|
340
382
|
if ( (resolution != 'adaptive') && (resolution != 'none') ) {
|
|
341
|
-
if ( resolution.
|
|
383
|
+
if ( resolution.endsWith('p60') ) {
|
|
342
384
|
frame_rate = '59.94'
|
|
385
|
+
resolution = resolution.slice(0, -3)
|
|
343
386
|
}
|
|
344
|
-
resolution = resolution.slice(0, 3)
|
|
345
387
|
}
|
|
346
388
|
|
|
389
|
+
var segment_playlist = false
|
|
347
390
|
var segment_found = false
|
|
348
391
|
|
|
349
392
|
body = body
|
|
@@ -352,12 +395,15 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
352
395
|
|
|
353
396
|
// Check if segment playlist instead of master
|
|
354
397
|
if ( line.startsWith('#EXTINF:') ) {
|
|
355
|
-
|
|
398
|
+
if ( !segment_playlist ) {
|
|
399
|
+
session.debuglog('segment playlist instead of master')
|
|
400
|
+
segment_playlist = true
|
|
401
|
+
}
|
|
356
402
|
segment_found = true
|
|
357
403
|
return line
|
|
358
404
|
} else if ( segment_found ) {
|
|
359
405
|
segment_found = false
|
|
360
|
-
return 'ts?url='+encodeURIComponent(url.resolve(streamURL, line.trim())) + content_protect
|
|
406
|
+
return 'ts?url='+encodeURIComponent(url.resolve(streamURL, line.trim())) + content_protect + referer_parameter
|
|
361
407
|
}
|
|
362
408
|
|
|
363
409
|
// Omit keyframe tracks
|
|
@@ -375,7 +421,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
375
421
|
if ( audio_track_matched ) return
|
|
376
422
|
if ( audio_url != '' ) {
|
|
377
423
|
audio_track_matched = true
|
|
378
|
-
return '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="' + audio_url + content_protect + '"'
|
|
424
|
+
return '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="' + audio_url + content_protect + referer_parameter + '"'
|
|
379
425
|
}
|
|
380
426
|
if ( audio_track == 'none') return
|
|
381
427
|
if ( (resolution == 'none') && (line.indexOf(',URI=') < 0) ) return
|
|
@@ -397,7 +443,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
397
443
|
if ( inning_number != VALID_INNING_NUMBER[0] ) newurl += '&inning_number=' + inning_number
|
|
398
444
|
if ( skip != VALID_SKIP[0] ) newurl += '&skip=' + skip
|
|
399
445
|
if ( contentId ) newurl += '&contentId=' + contentId
|
|
400
|
-
newurl += content_protect
|
|
446
|
+
newurl += content_protect + referer_parameter
|
|
401
447
|
if ( resolution == 'none' ) {
|
|
402
448
|
audio_track_matched = true
|
|
403
449
|
return line.replace(parsed[0],'') + "\n" + '#EXT-X-STREAM-INF:BANDWIDTH=50000,CODECS="mp4a.40.2",AUDIO="aac"' + "\n" + newurl
|
|
@@ -443,7 +489,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
443
489
|
if ( inning_number != VALID_INNING_NUMBER[0] ) newurl += '&inning_number=' + inning_number
|
|
444
490
|
if ( skip != VALID_SKIP[0] ) newurl += '&skip=' + skip
|
|
445
491
|
if ( contentId ) newurl += '&contentId=' + contentId
|
|
446
|
-
newurl += content_protect
|
|
492
|
+
newurl += content_protect + referer_parameter
|
|
447
493
|
return 'playlist?url='+newurl
|
|
448
494
|
}
|
|
449
495
|
})
|
|
@@ -477,6 +523,14 @@ app.get('/playlist', async function(req, res) {
|
|
|
477
523
|
var u = req.query.url
|
|
478
524
|
session.debuglog('playlist url : ' + u)
|
|
479
525
|
|
|
526
|
+
var referer = false
|
|
527
|
+
var referer_parameter = ''
|
|
528
|
+
if ( req.query.referer ) {
|
|
529
|
+
referer = decodeURIComponent(req.query.referer)
|
|
530
|
+
session.debuglog('found playlist referer : ' + referer)
|
|
531
|
+
referer_parameter = '&referer=' + encodeURIComponent(req.query.referer)
|
|
532
|
+
}
|
|
533
|
+
|
|
480
534
|
var force_vod = req.query.force_vod || VALID_FORCE_VOD[0]
|
|
481
535
|
var inning_half = req.query.inning_half || VALID_INNING_HALF[0]
|
|
482
536
|
var inning_number = req.query.inning_number || VALID_INNING_NUMBER[0]
|
|
@@ -485,12 +539,37 @@ app.get('/playlist', async function(req, res) {
|
|
|
485
539
|
|
|
486
540
|
var req = function () {
|
|
487
541
|
var headers = {}
|
|
542
|
+
if ( referer ) {
|
|
543
|
+
headers.referer = referer
|
|
544
|
+
headers.origin = getOriginFromURL(referer)
|
|
545
|
+
}
|
|
488
546
|
requestRetry(u, headers, function(err, response) {
|
|
489
547
|
if (err) return res.error(err)
|
|
490
548
|
|
|
491
549
|
//session.debuglog(response.body)
|
|
492
550
|
|
|
493
551
|
var body = response.body.trim().split('\n')
|
|
552
|
+
|
|
553
|
+
// check if HLS
|
|
554
|
+
let hls_detected = false
|
|
555
|
+
for (var i=0; i<body.length; i++) {
|
|
556
|
+
if ( body[i] == '#EXTM3U' ) {
|
|
557
|
+
session.debuglog('hls detected')
|
|
558
|
+
hls_detected = true
|
|
559
|
+
break
|
|
560
|
+
} else if ( body[i] == '' ) {
|
|
561
|
+
session.debuglog('skipping blank lines at beginning of file')
|
|
562
|
+
continue
|
|
563
|
+
} else {
|
|
564
|
+
session.debuglog(body[i])
|
|
565
|
+
break
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if ( !hls_detected ) {
|
|
569
|
+
session.log('not a valid hls stream')
|
|
570
|
+
return
|
|
571
|
+
}
|
|
572
|
+
|
|
494
573
|
var key
|
|
495
574
|
var iv
|
|
496
575
|
|
|
@@ -517,6 +596,9 @@ app.get('/playlist', async function(req, res) {
|
|
|
517
596
|
|
|
518
597
|
body = body
|
|
519
598
|
.map(function(line) {
|
|
599
|
+
// Skip blank lines
|
|
600
|
+
if (line.trim() == '') return null
|
|
601
|
+
|
|
520
602
|
if ( ((skip != 'off') || (inning_half != VALID_INNING_HALF[0]) || (inning_number != VALID_INNING_NUMBER[0])) && (typeof session.temp_cache[contentId] !== 'undefined') && (typeof session.temp_cache[contentId].inning_offsets !== 'undefined') ) {
|
|
521
603
|
if ( skip_next ) {
|
|
522
604
|
skip_next = false
|
|
@@ -579,10 +661,17 @@ app.get('/playlist', async function(req, res) {
|
|
|
579
661
|
}
|
|
580
662
|
|
|
581
663
|
if (line.indexOf('-KEY:METHOD=AES-128') > 0) {
|
|
664
|
+
session.debuglog('key line : ' + line)
|
|
582
665
|
var parsed = line.match(/URI="([^"]+)"(?:,IV=(.+))?$/)
|
|
583
666
|
if ( parsed ) {
|
|
584
667
|
if ( parsed[1].substr(0,4) == 'http' ) key = parsed[1]
|
|
585
668
|
else key = url.resolve(u, parsed[1])
|
|
669
|
+
session.debuglog('found key : ' + key)
|
|
670
|
+
if ( key.startsWith('data:;base64,') ) {
|
|
671
|
+
let newparsed = key.split(',')
|
|
672
|
+
key = newparsed[1]
|
|
673
|
+
session.debuglog('found new key : ' + key)
|
|
674
|
+
}
|
|
586
675
|
if (parsed[2]) iv = parsed[2].slice(2).toLowerCase()
|
|
587
676
|
}
|
|
588
677
|
return null
|
|
@@ -590,8 +679,8 @@ app.get('/playlist', async function(req, res) {
|
|
|
590
679
|
|
|
591
680
|
if (line[0] === '#') return line
|
|
592
681
|
|
|
593
|
-
if ( key ) return 'ts?url='+encodeURIComponent(url.resolve(u, line.trim()))+'&key='+encodeURIComponent(key)+'&iv='+encodeURIComponent(iv) + content_protect
|
|
594
|
-
else return 'ts?url='+encodeURIComponent(url.resolve(u, line.trim())) + content_protect
|
|
682
|
+
if ( key ) return 'ts?url='+encodeURIComponent(url.resolve(u, line.trim()))+'&key='+encodeURIComponent(key)+'&iv='+encodeURIComponent(iv) + content_protect + referer_parameter
|
|
683
|
+
else return 'ts?url='+encodeURIComponent(url.resolve(u, line.trim())) + content_protect + referer_parameter
|
|
595
684
|
})
|
|
596
685
|
.filter(function(line) {
|
|
597
686
|
return line
|
|
@@ -625,23 +714,42 @@ app.get('/ts', async function(req, res) {
|
|
|
625
714
|
|
|
626
715
|
var headers = {encoding:null}
|
|
627
716
|
|
|
717
|
+
if ( req.query.referer ) {
|
|
718
|
+
session.debuglog('found segment referer : ' + req.query.referer)
|
|
719
|
+
referer = decodeURIComponent(req.query.referer)
|
|
720
|
+
headers.referer = referer
|
|
721
|
+
headers.origin = getOriginFromURL(referer)
|
|
722
|
+
}
|
|
723
|
+
|
|
628
724
|
requestRetry(u, headers, function(err, response) {
|
|
629
725
|
if (err) return res.error(err)
|
|
630
726
|
if (!req.query.key) return respond(response, res, response.body)
|
|
631
727
|
|
|
632
728
|
//var ku = url.resolve(manifest, req.query.key)
|
|
633
729
|
var ku = req.query.key
|
|
634
|
-
|
|
635
|
-
if (err) return res.error(err)
|
|
636
|
-
|
|
730
|
+
if ( ku.substr(0,4) != 'http' ) {
|
|
637
731
|
var iv = Buffer.from(req.query.iv, 'hex')
|
|
638
732
|
session.debuglog('iv : 0x'+req.query.iv)
|
|
639
733
|
|
|
734
|
+
let key = Buffer.from(ku, "base64")
|
|
735
|
+
|
|
640
736
|
var dc = crypto.createDecipheriv('aes-128-cbc', key, iv)
|
|
641
737
|
var buffer = Buffer.concat([dc.update(response.body), dc.final()])
|
|
642
738
|
|
|
643
739
|
respond(response, res, buffer)
|
|
644
|
-
}
|
|
740
|
+
} else {
|
|
741
|
+
getKey(ku, headers, function(err, key) {
|
|
742
|
+
if (err) return res.error(err)
|
|
743
|
+
|
|
744
|
+
var iv = Buffer.from(req.query.iv, 'hex')
|
|
745
|
+
session.debuglog('iv : 0x'+req.query.iv)
|
|
746
|
+
|
|
747
|
+
var dc = crypto.createDecipheriv('aes-128-cbc', key, iv)
|
|
748
|
+
var buffer = Buffer.concat([dc.update(response.body), dc.final()])
|
|
749
|
+
|
|
750
|
+
respond(response, res, buffer)
|
|
751
|
+
})
|
|
752
|
+
}
|
|
645
753
|
})
|
|
646
754
|
})
|
|
647
755
|
|
|
@@ -872,6 +980,8 @@ app.get('/', async function(req, res) {
|
|
|
872
980
|
}
|
|
873
981
|
body += '</p>' + "\n"
|
|
874
982
|
|
|
983
|
+
body += '<p><span class="tooltip tinytext">* indicates a free game<span class="tooltiptext">Free games are available to anyone with an account, no subscription necessary. Blackouts still apply.</span></span></p>' + "\n"
|
|
984
|
+
|
|
875
985
|
body += "<table>" + "\n"
|
|
876
986
|
|
|
877
987
|
// Rename some parameters before display links
|
|
@@ -903,7 +1013,7 @@ app.get('/', async function(req, res) {
|
|
|
903
1013
|
let compareEnd = new Date(big_inning.end)
|
|
904
1014
|
compareEnd.setHours(compareEnd.getHours()+1)
|
|
905
1015
|
if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
|
|
906
|
-
let querystring = '?
|
|
1016
|
+
let querystring = '?event=biginning'
|
|
907
1017
|
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
908
1018
|
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
909
1019
|
querystring += content_protect_b
|
|
@@ -921,6 +1031,10 @@ app.get('/', async function(req, res) {
|
|
|
921
1031
|
|
|
922
1032
|
let awayteam = cache_data.dates[0].games[j].teams['away'].team.abbreviation
|
|
923
1033
|
let hometeam = cache_data.dates[0].games[j].teams['home'].team.abbreviation
|
|
1034
|
+
if ( cache_data.dates[0].games[j].teams['home'].team.sport.name == 'Winter Leagues' ) {
|
|
1035
|
+
awayteam = cache_data.dates[0].games[j].teams['away'].team.name
|
|
1036
|
+
hometeam = cache_data.dates[0].games[j].teams['home'].team.name
|
|
1037
|
+
}
|
|
924
1038
|
|
|
925
1039
|
let teams = awayteam + " @ " + hometeam
|
|
926
1040
|
let pitchers = ""
|
|
@@ -996,6 +1110,9 @@ app.get('/', async function(req, res) {
|
|
|
996
1110
|
} else if ( cache_data.dates[0].games[j].gameUtils.isDelayed ) {
|
|
997
1111
|
state += "<br/>" + detailedState
|
|
998
1112
|
}
|
|
1113
|
+
if ( cache_data.dates[0].games[j].teams['home'].team.sport.name == 'Winter Leagues' ) {
|
|
1114
|
+
state += "<br/>" + cache_data.dates[0].games[j].teams['home'].team.league.name
|
|
1115
|
+
}
|
|
999
1116
|
|
|
1000
1117
|
if ( cache_data.dates[0].games[j].doubleHeader != 'N' ) {
|
|
1001
1118
|
state += "<br/>Game " + cache_data.dates[0].games[j].gameNumber
|
|
@@ -1049,103 +1166,139 @@ app.get('/', async function(req, res) {
|
|
|
1049
1166
|
|
|
1050
1167
|
body += "<tr><td>" + teams + pitchers + state + "</td>"
|
|
1051
1168
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
} else {
|
|
1169
|
+
// Check if Winter League game first
|
|
1170
|
+
if ( cache_data.dates[0].games[j].teams['home'].team.sport.name == 'Winter Leagues' ) {
|
|
1055
1171
|
body += "<td>"
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1172
|
+
if ( cache_data.dates[0].games[j].broadcasts ) {
|
|
1173
|
+
for (var k = 0; k < cache_data.dates[0].games[j].broadcasts.length; k++) {
|
|
1174
|
+
if ( (mediaType == 'MLBTV') && (cache_data.dates[0].games[j].broadcasts[k].name == 'MLB.TV') ) {
|
|
1175
|
+
// Check if game should be live
|
|
1176
|
+
let currentTime = new Date()
|
|
1177
|
+
let startTime = new Date(cache_data.dates[0].games[j].gameDate)
|
|
1178
|
+
startTime.setMinutes(startTime.getMinutes()-30)
|
|
1179
|
+
let endTime = new Date(cache_data.dates[0].games[j].gameDate)
|
|
1180
|
+
endTime.setHours(endTime.getHours()+4)
|
|
1181
|
+
if ( (currentTime >= startTime) && (currentTime < endTime) ) {
|
|
1182
|
+
let querystring
|
|
1183
|
+
querystring = '?event=' + cache_data.dates[0].games[j].teams['home'].team.clubName.toLowerCase()
|
|
1184
|
+
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + '&audio_track=' + DEFAULT_MULTIVIEW_AUDIO_TRACK
|
|
1185
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1186
|
+
if ( audio_track != VALID_AUDIO_TRACKS[0] ) querystring += '&audio_track=' + encodeURIComponent(audio_track)
|
|
1187
|
+
querystring += content_protect_b
|
|
1188
|
+
multiviewquerystring += content_protect_b
|
|
1189
|
+
body += '<a href="' + thislink + querystring + '">' + cache_data.dates[0].games[j].broadcasts[k].name + '</a>'
|
|
1190
|
+
body += '<input type="checkbox" value="' + server + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1191
|
+
} else {
|
|
1192
|
+
body += cache_data.dates[0].games[j].broadcasts[k].name
|
|
1064
1193
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1194
|
+
break
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
body += "</td>"
|
|
1199
|
+
} else {
|
|
1200
|
+
// Begin MLB games
|
|
1201
|
+
if ( ((typeof cache_data.dates[0].games[j].content.media) == 'undefined') || ((typeof cache_data.dates[0].games[j].content.media.epg) == 'undefined') ) {
|
|
1202
|
+
body += "<td></td>"
|
|
1203
|
+
} else {
|
|
1204
|
+
body += "<td>"
|
|
1205
|
+
for (var k = 0; k < cache_data.dates[0].games[j].content.media.epg.length; k++) {
|
|
1206
|
+
let epgTitle = cache_data.dates[0].games[j].content.media.epg[k].title
|
|
1207
|
+
if ( epgTitle == mediaType ) {
|
|
1208
|
+
let lastStation
|
|
1209
|
+
for (var x = 0; x < cache_data.dates[0].games[j].content.media.epg[k].items.length; x++) {
|
|
1210
|
+
// check that pay TV authentication isn't required
|
|
1211
|
+
if ( (mediaType == 'MLBTV') && ((cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState != 'MEDIA_ARCHIVE') || !cache_data.dates[0].games[j].gameUtils.isFinal) && (cache_data.dates[0].games[j].content.media.epg[k].items[x].foxAuthRequired || cache_data.dates[0].games[j].content.media.epg[k].items[x].tbsAuthRequired || cache_data.dates[0].games[j].content.media.epg[k].items[x].espnAuthRequired || cache_data.dates[0].games[j].content.media.epg[k].items[x].fs1AuthRequired || cache_data.dates[0].games[j].content.media.epg[k].items[x].mlbnAuthRequired) ) {
|
|
1212
|
+
continue
|
|
1213
|
+
}
|
|
1214
|
+
if ( ((typeof cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaFeedType) == 'undefined') || (cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaFeedType.indexOf('IN_MARKET_') == -1) ) {
|
|
1215
|
+
if ( ((typeof cache_data.dates[0].games[j].content.media.epg[k].items[x].language) == 'undefined') || (cache_data.dates[0].games[j].content.media.epg[k].items[x].language == language) ) {
|
|
1216
|
+
let teamabbr
|
|
1217
|
+
if ( (((typeof cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaFeedType) != 'undefined') && (cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaFeedType == 'NATIONAL')) || ((mediaType == 'MLBTV') && cache_data.dates[0].games[j].gameUtils.isPostSeason) ) {
|
|
1218
|
+
teamabbr = 'NATIONAL'
|
|
1082
1219
|
} else {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
if ( linkType == 'embed' ) {
|
|
1087
|
-
if ( startFrom != 'Beginning' ) querystring += '&startFrom=' + startFrom
|
|
1220
|
+
teamabbr = hometeam
|
|
1221
|
+
if ( cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType] == 'AWAY' ) {
|
|
1222
|
+
teamabbr = awayteam
|
|
1088
1223
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1224
|
+
}
|
|
1225
|
+
let station = cache_data.dates[0].games[j].content.media.epg[k].items[x].callLetters
|
|
1226
|
+
if ( cache_data.dates[0].games[j].content.media.freeGame ) {
|
|
1227
|
+
station += '*'
|
|
1228
|
+
}
|
|
1229
|
+
if ( (cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON') || (cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ARCHIVE') || cache_data.dates[0].games[j].gameUtils.isFinal ) {
|
|
1230
|
+
game_started = true
|
|
1231
|
+
let mediaId = cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaId
|
|
1232
|
+
if ( (mediaType == 'MLBTV') && session.cache.media && session.cache.media[mediaId] && session.cache.media[mediaId].blackout && session.cache.media[mediaId].blackoutExpiry && (new Date(session.cache.media[mediaId].blackoutExpiry) > new Date()) ) {
|
|
1233
|
+
body += teamabbr + ': <s>' + station + '</s>'
|
|
1234
|
+
} else {
|
|
1235
|
+
let querystring
|
|
1236
|
+
querystring = '?mediaId=' + mediaId
|
|
1237
|
+
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + '&audio_track=' + DEFAULT_MULTIVIEW_AUDIO_TRACK
|
|
1238
|
+
if ( linkType == 'embed' ) {
|
|
1239
|
+
if ( startFrom != 'Beginning' ) querystring += '&startFrom=' + startFrom
|
|
1097
1240
|
}
|
|
1098
|
-
if (
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1241
|
+
if ( mediaType == 'MLBTV' ) {
|
|
1242
|
+
if ( inning_half != VALID_INNING_HALF[0] ) querystring += '&inning_half=' + inning_half
|
|
1243
|
+
if ( inning_number != '' ) querystring += '&inning_number=' + relative_inning
|
|
1244
|
+
if ( skip != 'off' ) querystring += '&skip=' + skip
|
|
1245
|
+
if ( skip_adjust != '0' ) querystring += '&skip_adjust=' + skip_adjust
|
|
1246
|
+
if ( (inning_half != VALID_INNING_HALF[0]) || (inning_number != VALID_INNING_NUMBER[0]) || (skip != VALID_SKIP[0]) ) {
|
|
1247
|
+
let contentId = cache_data.dates[0].games[j].content.media.epg[k].items[x].contentId
|
|
1248
|
+
querystring += '&contentId=' + contentId
|
|
1249
|
+
}
|
|
1250
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1251
|
+
if ( audio_track != VALID_AUDIO_TRACKS[0] ) querystring += '&audio_track=' + encodeURIComponent(audio_track)
|
|
1252
|
+
// audio_url is disabled here, now used in multiview instead
|
|
1253
|
+
//if ( audio_url != '' ) querystring += '&audio_url=' + encodeURIComponent(audio_url)
|
|
1106
1254
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
body += teamabbr + ': <a href="' + thislink + querystring + '">' + station + '</a>'
|
|
1111
|
-
if ( mediaType == 'MLBTV' ) {
|
|
1112
|
-
body += '<input type="checkbox" value="' + server + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1113
|
-
}
|
|
1114
|
-
if ( resumeStatus ) {
|
|
1115
|
-
if ( resumeStatus == 'live' ) {
|
|
1116
|
-
if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ARCHIVE' ) {
|
|
1117
|
-
body += '(1)'
|
|
1118
|
-
} else if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON' ) {
|
|
1119
|
-
body += '(2)'
|
|
1255
|
+
if ( linkType == 'stream' ) {
|
|
1256
|
+
if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON' ) {
|
|
1257
|
+
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1120
1258
|
}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1259
|
+
}
|
|
1260
|
+
querystring += content_protect_b
|
|
1261
|
+
multiviewquerystring += content_protect_b
|
|
1262
|
+
body += teamabbr + ': <a href="' + thislink + querystring + '">' + station + '</a>'
|
|
1263
|
+
if ( mediaType == 'MLBTV' ) {
|
|
1264
|
+
body += '<input type="checkbox" value="' + server + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1265
|
+
}
|
|
1266
|
+
if ( resumeStatus ) {
|
|
1267
|
+
if ( resumeStatus == 'live' ) {
|
|
1268
|
+
if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ARCHIVE' ) {
|
|
1269
|
+
body += '(1)'
|
|
1270
|
+
} else if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON' ) {
|
|
1271
|
+
body += '(2)'
|
|
1272
|
+
}
|
|
1124
1273
|
} else {
|
|
1125
|
-
|
|
1126
|
-
|
|
1274
|
+
if ( station == lastStation ) {
|
|
1275
|
+
body += '(2)'
|
|
1276
|
+
} else {
|
|
1277
|
+
body += '(1)'
|
|
1278
|
+
lastStation = station
|
|
1279
|
+
}
|
|
1127
1280
|
}
|
|
1128
1281
|
}
|
|
1282
|
+
body += ', '
|
|
1129
1283
|
}
|
|
1130
|
-
|
|
1284
|
+
} else {
|
|
1285
|
+
body += teamabbr + ': ' + station + ', '
|
|
1131
1286
|
}
|
|
1132
|
-
} else {
|
|
1133
|
-
body += teamabbr + ': ' + station + ', '
|
|
1134
1287
|
}
|
|
1135
1288
|
}
|
|
1136
1289
|
}
|
|
1290
|
+
break
|
|
1137
1291
|
}
|
|
1138
|
-
break
|
|
1139
1292
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1293
|
+
if ( body.substr(-2) == ', ' ) {
|
|
1294
|
+
body = body.slice(0, -2)
|
|
1295
|
+
}
|
|
1296
|
+
if ( (mediaType == 'MLBTV') && (game_started) ) {
|
|
1297
|
+
body += '<br/><a href="javascript:showhighlights(\'' + cache_data.dates[0].games[j].gamePk + '\',\'' + gameDate + '\')">Highlights</a>'
|
|
1298
|
+
}
|
|
1299
|
+
if ( body.substr(-2) == ', ' ) {
|
|
1300
|
+
body = body.slice(0, -2)
|
|
1301
|
+
}
|
|
1149
1302
|
}
|
|
1150
1303
|
body += "</td>"
|
|
1151
1304
|
body += "</tr>" + "\n"
|
|
@@ -1237,7 +1390,9 @@ app.get('/', async function(req, res) {
|
|
|
1237
1390
|
|
|
1238
1391
|
body += '<p><span class="tooltip">Exclude a team + national<span class="tooltiptext">This is useful for excluding games you may be blacked out from. Excluding a team will exclude every game involving that team. National refers to USA national TV broadcasts.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&excludeTeams=ari,national' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&excludeTeams=ari,national' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1239
1392
|
|
|
1240
|
-
body += '<p><span class="tooltip">Include (or exclude)
|
|
1393
|
+
body += '<p><span class="tooltip">Include (or exclude) LIDOM<span class="tooltiptext">Dominican Winter League, aka Liga de Beisbol Dominicano. Live stream only, does not support starting from the beginning or certain innings, skip options, etc.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=lidom' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1394
|
+
|
|
1395
|
+
body += '<p><span class="tooltip">Include (or exclude) Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. Live stream only, does not support starting from the beginning.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=biginning' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1241
1396
|
|
|
1242
1397
|
body += '<p><span class="tooltip">Include (or exclude) Multiview<span class="tooltiptext">Requires starting and stopping the multiview stream from the web interface.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=multiview' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1243
1398
|
|
|
@@ -1280,6 +1435,9 @@ app.get('/', async function(req, res) {
|
|
|
1280
1435
|
|
|
1281
1436
|
body += '<p><span class="tooltip">Bookmarklets for MLB.com<span class="tooltiptext">If you watch at MLB.com, drag these bookmarklets to your bookmarks toolbar and use them to hide parts of the interface.</span></span>: <a href="javascript:(function(){let x=document.querySelector(\'#mlbtv-stats-panel\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}})();">Boxscore</a> | <a href="javascript:(function(){let x=document.querySelector(\'.mlbtv-header-container\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">Scoreboard</a> | <a href="javascript:(function(){let x=document.querySelector(\'.mlbtv-container--footer\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">Linescore</a> | <a href="javascript:(function(){let x=document.querySelector(\'#mlbtv-stats-panel\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}x=document.querySelector(\'.mlbtv-header-container\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}x=document.querySelector(\'.mlbtv-container--footer\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">All</a></p>' + "\n"
|
|
1282
1437
|
|
|
1438
|
+
// Print version
|
|
1439
|
+
body += '<p class="tinytext">Version ' + version + '</p>' + "\n"
|
|
1440
|
+
|
|
1283
1441
|
// Datepicker functions
|
|
1284
1442
|
body += '<script>var datePicker=document.getElementById("gameDate");function changeDate(e){date=datePicker.value;reload()}function removeDate(e){datePicker.removeEventListener("change",changeDate,false);datePicker.addEventListener("blur",changeDate,false);if(e.keyCode===13){date=datePicker.value;reload()}}datePicker.addEventListener("change",changeDate,false);datePicker.addEventListener("keypress",removeDate,false)</script>' + "\n"
|
|
1285
1443
|
|
|
@@ -1749,7 +1907,6 @@ function start_multiview_stream(streams, sync, dvr, faster, audio_url, audio_url
|
|
|
1749
1907
|
ffmpeg_command.addOutputOption('-c:v', 'copy')
|
|
1750
1908
|
}
|
|
1751
1909
|
ffmpeg_command.addOutputOption('-c:a', 'aac')
|
|
1752
|
-
.addOutputOption('-strict', 'experimental')
|
|
1753
1910
|
.addOutputOption('-sn')
|
|
1754
1911
|
.addOutputOption('-t', '6:00:00')
|
|
1755
1912
|
.addOutputOption('-f', 'hls')
|