mlbserver 2022.5.13 → 2022.5.30
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 +94 -69
- package/package.json +1 -1
- package/session.js +2 -2
package/README.md
CHANGED
package/index.js
CHANGED
|
@@ -38,9 +38,12 @@ const DEFAULT_MULTIVIEW_AUDIO_TRACK = 'English'
|
|
|
38
38
|
const VALID_SKIP = [ 'off', 'breaks', 'idle time', 'pitches' ]
|
|
39
39
|
const VALID_PAD = [ 'off', 'on' ]
|
|
40
40
|
const VALID_FORCE_VOD = [ 'off', 'on' ]
|
|
41
|
+
const VALID_SCAN_MODES = [ 'off', 'on' ]
|
|
41
42
|
|
|
42
43
|
const SAMPLE_STREAM_URL = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
|
|
43
44
|
|
|
45
|
+
const SECONDS_PER_SEGMENT = 5
|
|
46
|
+
|
|
44
47
|
// Basic command line arguments, if specified:
|
|
45
48
|
// --port or -p (primary port to run on; defaults to 9999 if not specified)
|
|
46
49
|
// --debug or -d (false if not specified)
|
|
@@ -166,10 +169,10 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
166
169
|
let streamURL
|
|
167
170
|
let options = {}
|
|
168
171
|
let urlArray = req.url.split('?')
|
|
169
|
-
if ( (urlArray.length == 1) || ((session.data.scan_mode ==
|
|
172
|
+
if ( (urlArray.length == 1) || ((session.data.scan_mode == VALID_SCAN_MODES[1]) && 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) ) {
|
|
170
173
|
// load a sample encrypted HLS stream
|
|
171
174
|
session.log('loading sample stream')
|
|
172
|
-
options.resolution =
|
|
175
|
+
options.resolution = VALID_RESOLUTIONS[0]
|
|
173
176
|
streamURL = SAMPLE_STREAM_URL
|
|
174
177
|
options.referer = 'https://hls-js.netlify.app/'
|
|
175
178
|
} else {
|
|
@@ -187,8 +190,8 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
187
190
|
options.skip = req.query.skip || VALID_SKIP[0]
|
|
188
191
|
options.pad = req.query.pad || VALID_PAD[0]
|
|
189
192
|
if ( options.pad != VALID_PAD[0] ) {
|
|
190
|
-
// if pad is selected, pick a random number of times to repeat the last segment
|
|
191
|
-
options.pad = Math.floor(Math.random() *
|
|
193
|
+
// if pad is selected, pick a random number of times to repeat the last segment (between 1-3 hours)
|
|
194
|
+
options.pad = Math.floor(Math.random() * (7200 / SECONDS_PER_SEGMENT)) + (3600 / SECONDS_PER_SEGMENT)
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
if ( req.query.src ) {
|
|
@@ -231,7 +234,7 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
231
234
|
session.debuglog('using streamURL : ' + streamURL)
|
|
232
235
|
|
|
233
236
|
if ( streamURL.indexOf('master_radio_') > 0 ) {
|
|
234
|
-
options.resolution =
|
|
237
|
+
options.resolution = VALID_RESOLUTIONS[0]
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
if ( req.query.audio_url && (req.query.audio_url != '') ) {
|
|
@@ -384,7 +387,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
384
387
|
var video_track_matched = false
|
|
385
388
|
var audio_track_matched = false
|
|
386
389
|
var frame_rate = '29.97'
|
|
387
|
-
if ( (resolution !=
|
|
390
|
+
if ( (resolution != VALID_RESOLUTIONS[0]) && (resolution != VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) ) {
|
|
388
391
|
if ( resolution.endsWith('p60') ) {
|
|
389
392
|
frame_rate = '59.94'
|
|
390
393
|
resolution = resolution.slice(0, -3)
|
|
@@ -419,7 +422,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
419
422
|
}
|
|
420
423
|
|
|
421
424
|
// Omit captions track when no video is specified
|
|
422
|
-
if ( (resolution ==
|
|
425
|
+
if ( (resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) && (line.indexOf('#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,') === 0) ) {
|
|
423
426
|
return
|
|
424
427
|
}
|
|
425
428
|
|
|
@@ -430,13 +433,13 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
430
433
|
audio_track_matched = true
|
|
431
434
|
return '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="' + audio_url + content_protect + referer_parameter + '"'
|
|
432
435
|
}
|
|
433
|
-
if ( audio_track ==
|
|
434
|
-
if ( (resolution ==
|
|
435
|
-
if ( (audio_track !=
|
|
436
|
+
if ( audio_track == VALID_AUDIO_TRACKS[VALID_AUDIO_TRACKS.length-1]) return
|
|
437
|
+
if ( (resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) && (line.indexOf(',URI=') < 0) ) return
|
|
438
|
+
if ( (audio_track != VALID_AUDIO_TRACKS[0]) && ((line.indexOf('NAME="'+audio_track+'"') > 0) || (line.indexOf('NAME="'+audio_track.substring(0,audio_track.length-1)+'"') > 0)) ) {
|
|
436
439
|
audio_track_matched = true
|
|
437
440
|
line = line.replace('AUTOSELECT=NO','AUTOSELECT=YES')
|
|
438
441
|
if ( line.indexOf(',DEFAULT=YES') < 0 ) line = line.replace('AUTOSELECT=YES','AUTOSELECT=YES,DEFAULT=YES')
|
|
439
|
-
} else if ( (audio_track !=
|
|
442
|
+
} else if ( (audio_track != VALID_AUDIO_TRACKS[0]) && ((line.indexOf('NAME="'+audio_track+'"') === -1) || (line.indexOf('NAME="'+audio_track.substring(0,audio_track.length-1)+'"') === -1)) ) {
|
|
440
443
|
return
|
|
441
444
|
}
|
|
442
445
|
if (line.indexOf(',URI=') > 0) {
|
|
@@ -452,7 +455,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
452
455
|
if ( pad != VALID_PAD[0] ) newurl += '&pad=' + pad
|
|
453
456
|
if ( contentId ) newurl += '&contentId=' + contentId
|
|
454
457
|
newurl += content_protect + referer_parameter
|
|
455
|
-
if ( resolution ==
|
|
458
|
+
if ( resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1] ) {
|
|
456
459
|
audio_track_matched = true
|
|
457
460
|
return line.replace(parsed[0],'') + "\n" + '#EXT-X-STREAM-INF:BANDWIDTH=50000,CODECS="mp4a.40.2",AUDIO="aac"' + "\n" + newurl
|
|
458
461
|
}
|
|
@@ -464,10 +467,10 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
464
467
|
|
|
465
468
|
// Parse video tracks to only include matching one, if specified
|
|
466
469
|
if (line.indexOf('#EXT-X-STREAM-INF:BANDWIDTH=') === 0) {
|
|
467
|
-
if ( resolution ==
|
|
470
|
+
if ( resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1] ) {
|
|
468
471
|
return
|
|
469
472
|
} else {
|
|
470
|
-
if ( resolution ===
|
|
473
|
+
if ( resolution === VALID_RESOLUTIONS[0] ) {
|
|
471
474
|
return line
|
|
472
475
|
} else {
|
|
473
476
|
if (line.indexOf(resolution+',FRAME-RATE='+frame_rate) > 0) {
|
|
@@ -489,7 +492,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
489
492
|
return line
|
|
490
493
|
}
|
|
491
494
|
|
|
492
|
-
if ( (resolution ===
|
|
495
|
+
if ( (resolution === VALID_RESOLUTIONS[0]) || (video_track_matched) ) {
|
|
493
496
|
video_track_matched = false
|
|
494
497
|
newurl = encodeURIComponent(url.resolve(streamURL, line.trim()))
|
|
495
498
|
if ( force_vod != VALID_FORCE_VOD[0] ) newurl += '&force_vod=on'
|
|
@@ -667,11 +670,17 @@ app.get('/playlist', async function(req, res) {
|
|
|
667
670
|
if ( body_array[last_segment_index] == '#EXT-X-ENDLIST' ) {
|
|
668
671
|
session.debuglog('padding archive stream with extra segments')
|
|
669
672
|
last_segment_index--
|
|
670
|
-
|
|
673
|
+
while ( !body_array[last_segment_index].startsWith('#EXTINF:' + SECONDS_PER_SEGMENT) ) {
|
|
674
|
+
last_segment_index--
|
|
675
|
+
}
|
|
676
|
+
last_segment_inf = body_array[last_segment_index]
|
|
677
|
+
last_segment_ts = body_array[last_segment_index+1]
|
|
678
|
+
let pad_lines = '#EXT-X-DISCONTINUITY' + '\n' + last_segment_inf + '\n' + last_segment_ts + '\n'
|
|
671
679
|
session.debuglog(pad_lines)
|
|
672
680
|
for (i=0; i<pad; i++) {
|
|
673
681
|
body += pad_lines
|
|
674
682
|
}
|
|
683
|
+
body += '#EXT-X-ENDLIST' + '\n'
|
|
675
684
|
}
|
|
676
685
|
}
|
|
677
686
|
if ( force_vod != VALID_FORCE_VOD[0] ) body += '#EXT-X-ENDLIST' + '\n'
|
|
@@ -860,11 +869,13 @@ app.get('/', async function(req, res) {
|
|
|
860
869
|
session.setScanMode(req.query.scan_mode)
|
|
861
870
|
}
|
|
862
871
|
|
|
872
|
+
var content_protect = ''
|
|
863
873
|
var content_protect_a = ''
|
|
864
874
|
var content_protect_b = ''
|
|
865
875
|
if ( session.protection.content_protect ) {
|
|
866
|
-
|
|
867
|
-
|
|
876
|
+
content_protect = session.protection.content_protect
|
|
877
|
+
content_protect_a = '?content_protect=' + content_protect
|
|
878
|
+
content_protect_b = '&content_protect=' + content_protect
|
|
868
879
|
}
|
|
869
880
|
|
|
870
881
|
var body = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta http-equiv="Content-type" content="text/html;charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><title>' + appname + '</title><link rel="icon" href="favicon.svg' + content_protect_a + '"><style type="text/css">input[type=text],input[type=button]{-webkit-appearance:none;-webkit-border-radius:0}body{width:480px;color:lightgray;background-color:black;font-family:Arial,Helvetica,sans-serif;-webkit-text-size-adjust:none}a{color:darkgray}button{color:lightgray;background-color:black}button.default{color:black;background-color:lightgray}table{width:100%;pad}table,th,td{border:1px solid darkgray;border-collapse:collapse}th,td{padding:5px}.tinytext,textarea,input[type="number"]{font-size:.8em}textarea{width:380px}'
|
|
@@ -879,13 +890,13 @@ app.get('/', async function(req, res) {
|
|
|
879
890
|
body += '</style><script type="text/javascript">' + "\n";
|
|
880
891
|
|
|
881
892
|
// Define option variables in page
|
|
882
|
-
body += 'var date="' + gameDate + '";var mediaType="' + mediaType + '";var resolution="' + resolution + '";var audio_track="' + audio_track + '";var force_vod="' + force_vod + '";var inning_half="' + inning_half + '";var inning_number="' + inning_number + '";var skip="' + skip + '";var pad="' + pad + '";var linkType="' + linkType + '";var startFrom="' + startFrom + '";var scores="' + scores + '";var controls="' + controls + '";var scan_mode="' + scan_mode + '";' + "\n"
|
|
893
|
+
body += 'var date="' + gameDate + '";var mediaType="' + mediaType + '";var resolution="' + resolution + '";var audio_track="' + audio_track + '";var force_vod="' + force_vod + '";var inning_half="' + inning_half + '";var inning_number="' + inning_number + '";var skip="' + skip + '";var pad="' + pad + '";var linkType="' + linkType + '";var startFrom="' + startFrom + '";var scores="' + scores + '";var controls="' + controls + '";var scan_mode="' + scan_mode + '";var content_protect="' + content_protect + '";' + "\n"
|
|
883
894
|
// audio_url is disabled here, now used in multiview instead
|
|
884
895
|
//body += 'var audio_url="' + audio_url + '";' + "\n"
|
|
885
896
|
|
|
886
897
|
// Reload function, called after options change
|
|
887
898
|
// audio_url is disabled here, now used in multiview instead
|
|
888
|
-
body += 'var defaultDate="' + session.liveDate() + '";var curDate=new Date();var utcHours=curDate.getUTCHours();if ((utcHours >= ' + todayUTCHours + ') && (utcHours < ' + YESTERDAY_UTC_HOURS + ')){defaultDate="' + session.yesterdayDate() + '"}function reload(){var newurl="/?";if (date != defaultDate){var urldate=date;if (date == "' + session.liveDate() + '"){urldate="today"}else if (date == "' + session.yesterdayDate() + '"){urldate="yesterday"}newurl+="date="+urldate+"&"}if (mediaType != "' + VALID_MEDIA_TYPES[0] + '"){newurl+="mediaType="+mediaType+"&"}if (mediaType=="Video"){if (resolution != "' + VALID_RESOLUTIONS[0] + '"){newurl+="resolution="+resolution+"&"}if (audio_track != "' + VALID_AUDIO_TRACKS[0] + '"){newurl+="audio_track="+encodeURIComponent(audio_track)+"&"}else if (resolution == "none"){newurl+="audio_track="+encodeURIComponent("' + VALID_AUDIO_TRACKS[2] + '")+"&"}/*if (audio_url != ""){newurl+="audio_url="+encodeURIComponent(audio_url)+"&"}*/if (inning_half != "' + VALID_INNING_HALF[0] + '"){newurl+="inning_half="+inning_half+"&"}if (inning_number != "' + VALID_INNING_NUMBER[0] + '"){newurl+="inning_number="+inning_number+"&"}if (skip != "' + VALID_SKIP[0] + '"){newurl+="skip="+skip+"&";}}if (pad != "' + VALID_PAD[0] + '"){newurl+="pad="+pad+"&";}if (linkType != "' + VALID_LINK_TYPES[0] + '"){newurl+="linkType="+linkType+"&"}if (linkType=="
|
|
899
|
+
body += 'var defaultDate="' + session.liveDate() + '";var curDate=new Date();var utcHours=curDate.getUTCHours();if ((utcHours >= ' + todayUTCHours + ') && (utcHours < ' + YESTERDAY_UTC_HOURS + ')){defaultDate="' + session.yesterdayDate() + '"}function reload(){var newurl="/?";if (date != defaultDate){var urldate=date;if (date == "' + session.liveDate() + '"){urldate="today"}else if (date == "' + session.yesterdayDate() + '"){urldate="yesterday"}newurl+="date="+urldate+"&"}if (mediaType != "' + VALID_MEDIA_TYPES[0] + '"){newurl+="mediaType="+mediaType+"&"}if (mediaType=="Video"){if (resolution != "' + VALID_RESOLUTIONS[0] + '"){newurl+="resolution="+resolution+"&"}if (audio_track != "' + VALID_AUDIO_TRACKS[0] + '"){newurl+="audio_track="+encodeURIComponent(audio_track)+"&"}else if (resolution == "none"){newurl+="audio_track="+encodeURIComponent("' + VALID_AUDIO_TRACKS[2] + '")+"&"}/*if (audio_url != ""){newurl+="audio_url="+encodeURIComponent(audio_url)+"&"}*/if (inning_half != "' + VALID_INNING_HALF[0] + '"){newurl+="inning_half="+inning_half+"&"}if (inning_number != "' + VALID_INNING_NUMBER[0] + '"){newurl+="inning_number="+inning_number+"&"}if (skip != "' + VALID_SKIP[0] + '"){newurl+="skip="+skip+"&";}}if (pad != "' + VALID_PAD[0] + '"){newurl+="pad="+pad+"&";}if (linkType != "' + VALID_LINK_TYPES[0] + '"){newurl+="linkType="+linkType+"&"}if (linkType=="' + VALID_LINK_TYPES[0] + '"){if (startFrom != "' + VALID_START_FROM[0] + '"){newurl+="startFrom="+startFrom+"&"}if (controls != "' + VALID_CONTROLS[0] + '"){newurl+="controls="+controls+"&"}}if (linkType=="Stream"){if (force_vod != "' + VALID_FORCE_VOD[0] + '"){newurl+="force_vod="+force_vod+"&"}}if (scores != "' + VALID_SCORES[0] + '"){newurl+="scores="+scores+"&"}if (scan_mode != "' + session.data.scan_mode + '"){newurl+="scan_mode="+scan_mode+"&"}if (content_protect != ""){newurl+="content_protect="+content_protect+"&"}window.location=newurl.substring(0,newurl.length-1)}' + "\n"
|
|
889
900
|
|
|
890
901
|
// Ajax function for multiview and highlights
|
|
891
902
|
body += 'function makeGETRequest(url, callback){var request=new XMLHttpRequest();request.onreadystatechange=function(){if (request.readyState==4 && request.status==200){callback(request.responseText)}};request.open("GET", url);request.send();}' + "\n"
|
|
@@ -907,7 +918,7 @@ app.get('/', async function(req, res) {
|
|
|
907
918
|
body += '<p><span class="tooltip">Date<span class="tooltiptext">"today" lasts until ' + todayUTCHours + ' AM EST. Home page will default to yesterday between ' + todayUTCHours + ' AM - ' + (YESTERDAY_UTC_HOURS - 4) + ' AM EST.</span></span>: <input type="date" id="gameDate" value="' + gameDate + '"/> '
|
|
908
919
|
for (var i = 0; i < VALID_DATES.length; i++) {
|
|
909
920
|
body += '<button '
|
|
910
|
-
if ( ((VALID_DATES[i] ==
|
|
921
|
+
if ( ((VALID_DATES[i] == VALID_DATES[0]) && (gameDate == session.liveDate())) || ((VALID_DATES[i] == VALID_DATES[1]) && (gameDate == session.yesterdayDate())) ) body += 'class="default" '
|
|
911
922
|
body += 'onclick="date=\'' + VALID_DATES[i] + '\';reload()">' + VALID_DATES[i] + '</button> '
|
|
912
923
|
}
|
|
913
924
|
body += '</p>' + "\n" + '<p><span class="tinytext">Updated ' + session.getCacheUpdatedDate(gameDate) + '</span></p>' + "\n"
|
|
@@ -929,8 +940,8 @@ app.get('/', async function(req, res) {
|
|
|
929
940
|
body += '</p>' + "\n"
|
|
930
941
|
|
|
931
942
|
body += '<p>'
|
|
932
|
-
if ( linkType ==
|
|
933
|
-
body += '<
|
|
943
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
944
|
+
body += '<span class="tooltip">Video Controls<span class="tooltiptext">Choose whether to show or hide controls on the embedded video page. Helpful to avoid timeline spoilers.</span></span>: '
|
|
934
945
|
for (var i = 0; i < VALID_CONTROLS.length; i++) {
|
|
935
946
|
body += '<button '
|
|
936
947
|
if ( controls == VALID_CONTROLS[i] ) body += 'class="default" '
|
|
@@ -945,12 +956,12 @@ app.get('/', async function(req, res) {
|
|
|
945
956
|
body += 'onclick="startFrom=\'' + VALID_START_FROM[i] + '\';reload()">' + VALID_START_FROM[i] + '</button> '
|
|
946
957
|
}
|
|
947
958
|
body += "\n"
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if ( mediaType == 'Video' ) {
|
|
951
|
-
if ( linkType == 'Embed' ) {
|
|
959
|
+
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
952
960
|
body += 'or '
|
|
953
961
|
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
954
965
|
body += '<span class="tooltip">Inning<span class="tooltiptext">For video streams only: choose the inning to start with (and the score to display, if applicable). Inning number is relative -- for example, selecting inning 7 here will show inning 7 for scheduled 9 inning games, but inning 5 for scheduled 7 inning games, for example. If an inning number is specified, seeking to an earlier point will not be possible. Inning 0 (zero) should be the broadcast start time, if available. Default is the beginning of the stream. To use with radio, set the video track to "None".</span></span>: '
|
|
955
966
|
body += '<select id="inning_half" onchange="inning_half=this.value;reload()">'
|
|
956
967
|
for (var i = 0; i < VALID_INNING_HALF.length; i++) {
|
|
@@ -986,21 +997,21 @@ app.get('/', async function(req, res) {
|
|
|
986
997
|
// Rename some parameters before display links
|
|
987
998
|
var mediaFeedType = 'mediaFeedType'
|
|
988
999
|
var language = 'en'
|
|
989
|
-
if ( mediaType ==
|
|
1000
|
+
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
990
1001
|
mediaType = 'MLBTV'
|
|
991
|
-
} else if ( mediaType ==
|
|
992
|
-
mediaType =
|
|
1002
|
+
} else if ( mediaType == VALID_MEDIA_TYPES[2] ) {
|
|
1003
|
+
mediaType = VALID_MEDIA_TYPES[1]
|
|
993
1004
|
language = 'es'
|
|
994
1005
|
}
|
|
995
|
-
if ( mediaType ==
|
|
1006
|
+
if ( mediaType == VALID_MEDIA_TYPES[1] ) {
|
|
996
1007
|
mediaFeedType = 'type'
|
|
997
1008
|
}
|
|
998
|
-
|
|
999
|
-
let link = linkType + '.html'
|
|
1000
|
-
if ( linkType ==
|
|
1001
|
-
link = linkType + '.m3u8'
|
|
1009
|
+
|
|
1010
|
+
let link = linkType.toLowerCase() + '.html'
|
|
1011
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1012
|
+
link = linkType.toLowerCase() + '.m3u8'
|
|
1002
1013
|
} else {
|
|
1003
|
-
force_vod =
|
|
1014
|
+
force_vod = VALID_FORCE_VOD[0]
|
|
1004
1015
|
}
|
|
1005
1016
|
var thislink = '/' + link
|
|
1006
1017
|
|
|
@@ -1014,12 +1025,12 @@ app.get('/', async function(req, res) {
|
|
|
1014
1025
|
if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
|
|
1015
1026
|
let querystring = '?event=biginning'
|
|
1016
1027
|
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1017
|
-
if ( linkType ==
|
|
1028
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
1018
1029
|
if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
|
|
1019
1030
|
if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
|
|
1020
1031
|
}
|
|
1021
1032
|
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1022
|
-
if ( linkType ==
|
|
1033
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1023
1034
|
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1024
1035
|
}
|
|
1025
1036
|
querystring += content_protect_b
|
|
@@ -1062,7 +1073,7 @@ app.get('/', async function(req, res) {
|
|
|
1062
1073
|
}
|
|
1063
1074
|
var relative_inning = (inning_number - (9 - scheduledInnings))
|
|
1064
1075
|
relative_inning = relative_inning < 0 ? 0 : relative_inning
|
|
1065
|
-
if ( (scores ==
|
|
1076
|
+
if ( (scores == VALID_SCORES[1]) && (cache_data.dates[0].games[j].gameUtils.isLive || cache_data.dates[0].games[j].gameUtils.isFinal) && !cache_data.dates[0].games[j].gameUtils.isCancelled && !cache_data.dates[0].games[j].gameUtils.isPostponed ) {
|
|
1066
1077
|
let awayscore = ''
|
|
1067
1078
|
let homescore = ''
|
|
1068
1079
|
if ( (inning_number != VALID_INNING_NUMBER[0]) && cache_data.dates[0].games[j].linescore && cache_data.dates[0].games[j].linescore.innings ) {
|
|
@@ -1244,7 +1255,7 @@ app.get('/', async function(req, res) {
|
|
|
1244
1255
|
let querystring
|
|
1245
1256
|
querystring = '?mediaId=' + mediaId
|
|
1246
1257
|
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + '&audio_track=' + DEFAULT_MULTIVIEW_AUDIO_TRACK
|
|
1247
|
-
if ( linkType ==
|
|
1258
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
1248
1259
|
if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
|
|
1249
1260
|
if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
|
|
1250
1261
|
}
|
|
@@ -1262,7 +1273,7 @@ app.get('/', async function(req, res) {
|
|
|
1262
1273
|
//if ( audio_url != '' ) querystring += '&audio_url=' + encodeURIComponent(audio_url)
|
|
1263
1274
|
}
|
|
1264
1275
|
if ( pad != VALID_PAD[0] ) querystring += '&pad=' + pad
|
|
1265
|
-
if ( linkType ==
|
|
1276
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1266
1277
|
if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON' ) {
|
|
1267
1278
|
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1268
1279
|
}
|
|
@@ -1333,10 +1344,10 @@ app.get('/', async function(req, res) {
|
|
|
1333
1344
|
|
|
1334
1345
|
// Rename parameter back before displaying further links
|
|
1335
1346
|
if ( mediaType == 'MLBTV' ) {
|
|
1336
|
-
mediaType =
|
|
1347
|
+
mediaType = VALID_MEDIA_TYPES[0]
|
|
1337
1348
|
}
|
|
1338
1349
|
|
|
1339
|
-
if ( mediaType ==
|
|
1350
|
+
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
1340
1351
|
body += '<p><span class="tooltip">Video<span class="tooltiptext">For video streams only: you can manually specifiy a video track (resolution) to use. Adaptive will let your client choose. 720p60 is the best quality. 540p is default for multiview (see below).<br/><br/>None will allow to remove the video tracks, if you just want to listen to the audio while using the "start at inning" or "skip breaks" options enabled.</span></span>: '
|
|
1341
1352
|
for (var i = 0; i < VALID_RESOLUTIONS.length; i++) {
|
|
1342
1353
|
body += '<button '
|
|
@@ -1376,8 +1387,8 @@ app.get('/', async function(req, res) {
|
|
|
1376
1387
|
}
|
|
1377
1388
|
body += '</p>' + "\n"
|
|
1378
1389
|
|
|
1379
|
-
if ( mediaType ==
|
|
1380
|
-
body += '<table><tr><td><table><tr><td>1</td><td>2</tr><tr><td>3</td><td>4</td></tr></table><td><span class="tooltip">Multiview / Alternate Audio / Sync<span class="tooltiptext">For video streams only: create a new live stream combining 1-4 separate video streams, using the layout shown at left (if more than 1 video stream is selected). Check the boxes next to feeds above to add/remove them, then click "Start" when ready, "Stop" when done watching, or "Restart" to stop and start with the currently selected streams. May take up to 15 seconds after starting before it is ready to play.<br/><br/>No video scaling is performed: defaults to 540p video for each stream, which can combine to make one 1080p stream. Audio defaults to English (TV) audio. If you specify a different audio track instead, you can use the box after each URL below to adjust the sync in seconds (use positive values if audio is early and the audio stream needs to be padded with silence at the beginning to line up with the video; negative values if audio is late, and audio needs to be trimmed from the beginning.)<br/><br/>TIP #1: You can enter just 1 video stream here, at any resolution, to take advantage of the audio sync or alternate audio features without using multiview -- a single video stream will not be re-encoded and will be presented at its full resolution.<br/><br/>TIP #2: You can also manually enter streams from other sources like <a href="https://www.npmjs.com/package/milbserver" target="_blank">milbserver</a> in the boxes below.<br/><br/>WARNING #1: if the mlbserver process dies or restarts while multiview is active, the ffmpeg encoding process will be orphaned and must be killed manually.<br/><br/>WARNING #2: If you did not specify a hardware encoder for ffmpeg on the command line, this will use your server CPU for encoding. Either way, your system may not be able to keep up with processing 4 video streams at once. Try fewer streams if you have perisistent trouble.</span></span>: <a id="startmultiview" href="" onclick="startmultiview(this);return false">Start'
|
|
1390
|
+
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
1391
|
+
body += '<table><tr><td><table><tr><td>1</td><td>2</tr><tr><td>3</td><td>4</td></tr></table><td><span class="tooltip">Multiview / Alternate Audio / Sync<span class="tooltiptext">For video streams only: create a new live stream combining 1-4 separate video streams, using the layout shown at left (if more than 1 video stream is selected). Check the boxes next to feeds above to add/remove them, then click "Start" when ready, "Stop" when done watching, or "Restart" to stop and start with the currently selected streams. May take up to 15 seconds after starting before it is ready to play.<br/><br/>No video scaling is performed: defaults to 540p video for each stream, which can combine to make one 1080p stream. Audio defaults to English (TV) audio. If you specify a different audio track instead, you can use the box after each URL below to adjust the sync in seconds (use positive values if audio is early and the audio stream needs to be padded with silence at the beginning to line up with the video; negative values if audio is late, and audio needs to be trimmed from the beginning.)<br/><br/>TIP #1: You can enter just 1 video stream here, at any resolution, to take advantage of the audio sync or alternate audio features without using multiview -- a single video stream will not be re-encoded and will be presented at its full resolution.<br/><br/>TIP #2: You can also manually enter streams from other sources like <a href="https://www.npmjs.com/package/milbserver" target="_blank">milbserver</a> in the boxes below. Make sure any manually entered streams have the desired resolution.<br/><br/>WARNING #1: if the mlbserver process dies or restarts while multiview is active, the ffmpeg encoding process will be orphaned and must be killed manually.<br/><br/>WARNING #2: If you did not specify a hardware encoder for ffmpeg on the command line, this will use your server CPU for encoding. Either way, your system may not be able to keep up with processing 4 video streams at once. Try fewer streams if you have perisistent trouble.</span></span>: <a id="startmultiview" href="" onclick="startmultiview(this);return false">Start'
|
|
1381
1392
|
if ( ffmpeg_status ) body += 'ed'
|
|
1382
1393
|
body += '</a> | <a id="stopmultiview" href="" onclick="stopmultiview(this);return false">Stop'
|
|
1383
1394
|
if ( !ffmpeg_status ) body += 'ped'
|
|
@@ -1396,7 +1407,7 @@ app.get('/', async function(req, res) {
|
|
|
1396
1407
|
body += '</td></tr></table><br/>' + "\n"
|
|
1397
1408
|
}
|
|
1398
1409
|
|
|
1399
|
-
if ( (linkType ==
|
|
1410
|
+
if ( (linkType == VALID_LINK_TYPES[1]) && (gameDate == session.liveDate()) ) {
|
|
1400
1411
|
body += '<p><span class="tooltip">Force VOD<span class="tooltiptext">For streams only: if your client does not support seeking in mlbserver live streams, turning this on will make the stream look like a VOD stream instead, allowing the client to start at the beginning and allowing the user to seek within it. You will need to reload the stream to watch/view past the current time, though.</span></span>: '
|
|
1401
1412
|
for (var i = 0; i < VALID_FORCE_VOD.length; i++) {
|
|
1402
1413
|
body += '<button '
|
|
@@ -1411,17 +1422,16 @@ app.get('/', async function(req, res) {
|
|
|
1411
1422
|
body += '<p><span class="tooltip">Live Channel Playlist and XMLTV Guide<span class="tooltiptext">Allows you to generate a M3U playlist of channels, and an XML file of guide listings for those channels, to import into TV/DVR/PVR software like Tvheadend or Jellyfin.<br/><br/>NOTE: May be helpful to specify a resolution above.</span></span>:</p>' + "\n"
|
|
1412
1423
|
|
|
1413
1424
|
body += '<p><span class="tooltip">Scan Mode<span class="tooltiptext">During setup, some TV/DVR/PVR software will attempt to load all stream URLs. Turning Scan Mode ON will return a sample stream for all stream requests, thus satisfying that software without overloading mlbserver or excluding streams which aren\'t currently live. Once the channels are set up, turning Scan Mode OFF will restore normal stream behavior.<br/><br/>WARNING: Be sure your TV/DVR/PVR software doesn\'t periodically scan all channels automatically or you might overload mlbserver.</span></span>: '
|
|
1414
|
-
|
|
1415
|
-
for (var i = 0; i < options.length; i++) {
|
|
1425
|
+
for (var i = 0; i < VALID_SCAN_MODES.length; i++) {
|
|
1416
1426
|
body += '<button '
|
|
1417
|
-
if ( scan_mode ==
|
|
1418
|
-
body += 'onclick="scan_mode=\'' +
|
|
1427
|
+
if ( scan_mode == VALID_SCAN_MODES[i] ) body += 'class="default" '
|
|
1428
|
+
body += 'onclick="scan_mode=\'' + VALID_SCAN_MODES[i] + '\';reload()">' + VALID_SCAN_MODES[i] + '</button> '
|
|
1419
1429
|
}
|
|
1420
1430
|
body += ' <span class="tinytext">(ON plays sample for all stream requests)</span></p>' + "\n"
|
|
1421
1431
|
|
|
1422
1432
|
body += '<p>All: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1423
1433
|
|
|
1424
|
-
body += '<p><span class="tooltip">By team<span class="tooltiptext">Including a team will include that team\'s broadcasts, not their opponent\'s broadcasts or national TV broadcasts.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=ari' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=ari' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1434
|
+
body += '<p><span class="tooltip">By team<span class="tooltiptext">Including a team will include that team\'s broadcasts, not their opponent\'s broadcasts or national TV broadcasts.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=ari,atl' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=ari,atl' + content_protect_b + '">guide.xml</a></p>' + "\n"
|
|
1425
1435
|
|
|
1426
1436
|
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 <a href="https://www.mlb.com/live-stream-games/national-blackout">USA national TV broadcasts</a>.</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"
|
|
1427
1437
|
|
|
@@ -1463,8 +1473,13 @@ app.get('/', async function(req, res) {
|
|
|
1463
1473
|
}
|
|
1464
1474
|
body += '</p></td></tr></table><br/>' + "\n"
|
|
1465
1475
|
|
|
1466
|
-
let
|
|
1467
|
-
|
|
1476
|
+
let local_url = '' // default to embedded player
|
|
1477
|
+
let urlArray = req.url.split('?')
|
|
1478
|
+
if ( (urlArray.length == 2) ) {
|
|
1479
|
+
local_url += '?' + urlArray[1]
|
|
1480
|
+
}
|
|
1481
|
+
let media_center_link = '/live-stream-games/' + gameDate.replace(/-/g,'/') + local_url
|
|
1482
|
+
body += '<p><span class="tooltip">Media Center View<span class="tooltiptext">Allows you to use the MLB Media Center page format for nagivation.</span></span>: <a href="' + media_center_link + '" target="_blank">Link</a></p>' + "\n"
|
|
1468
1483
|
|
|
1469
1484
|
body += '<p><span class="tooltip">Sample video<span class="tooltiptext">A sample stream. Useful for testing and troubleshooting.</span></span>: <a href="/embed.html' + content_protect_a + '">Embed</a> | <a href="/stream.m3u8' + content_protect_a + '">Stream</a> | <a href="/chromecast.html' + content_protect_a + '">Chromecast</a> | <a href="/advanced.html' + content_protect_a + '">Advanced</a></p>' + "\n"
|
|
1470
1485
|
|
|
@@ -1514,7 +1529,7 @@ app.get('/live-stream-games*', async function(req, res) {
|
|
|
1514
1529
|
session.debuglog('schedule request : ' + req.url)
|
|
1515
1530
|
|
|
1516
1531
|
// check for a linkType parameter in the url
|
|
1517
|
-
let linkType =
|
|
1532
|
+
let linkType = VALID_LINK_TYPES[0]
|
|
1518
1533
|
if ( req.query.linkType ) {
|
|
1519
1534
|
linkType = req.query.linkType
|
|
1520
1535
|
session.setLinkType(linkType)
|
|
@@ -1522,15 +1537,17 @@ app.get('/live-stream-games*', async function(req, res) {
|
|
|
1522
1537
|
|
|
1523
1538
|
// use the link type to determine the local url to use
|
|
1524
1539
|
var local_url = '/embed.html' // default to embedded player
|
|
1525
|
-
|
|
1526
|
-
if ( linkType == 'stream' ) { // direct stream
|
|
1540
|
+
if ( linkType == VALID_LINK_TYPES[1] ) { // direct stream
|
|
1527
1541
|
local_url = '/stream.m3u8'
|
|
1528
|
-
if ( session.protection.content_protect ) content_protect = '&content_protect=' + session.protection.content_protect
|
|
1529
1542
|
} else { // other
|
|
1530
1543
|
local_url = '/' + linkType + '.html'
|
|
1531
1544
|
}
|
|
1545
|
+
let urlArray = req.url.split('?')
|
|
1546
|
+
if ( (urlArray.length == 2) ) {
|
|
1547
|
+
local_url += '?' + urlArray[1]
|
|
1548
|
+
}
|
|
1532
1549
|
|
|
1533
|
-
// remove our
|
|
1550
|
+
// remove our local parameters, if specified, from the url we will fetch remotely
|
|
1534
1551
|
var remote_url = url.parse(req.url).pathname
|
|
1535
1552
|
|
|
1536
1553
|
let reqObj = {
|
|
@@ -1546,7 +1563,12 @@ app.get('/live-stream-games*', async function(req, res) {
|
|
|
1546
1563
|
var body = await session.httpGet(reqObj)
|
|
1547
1564
|
|
|
1548
1565
|
// a regex substitution to change existing links to local urls
|
|
1549
|
-
body = body.replace(/https:\/\/www.mlb.com\/tv\/g\d+\/[v]([a-zA-Z0-9-]+)/g,local_url+"
|
|
1566
|
+
body = body.replace(/https:\/\/www.mlb.com\/tv\/g\d+\/[v]([a-zA-Z0-9-]+)/g,local_url+"&contentId=$1")
|
|
1567
|
+
|
|
1568
|
+
// a regex substitution to remove unsupported filter menus
|
|
1569
|
+
if ( session.protection.content_protect ) {
|
|
1570
|
+
body = body.replace(/<div\n id="date-container"[\S\s]+><\/span>\n <\/div>/g,'')
|
|
1571
|
+
}
|
|
1550
1572
|
|
|
1551
1573
|
// hide popup to accept cookies
|
|
1552
1574
|
body = body.replace(/www.googletagmanager.com/g,'0.0.0.0')
|
|
@@ -1560,8 +1582,6 @@ app.get('/embed.html', async function(req, res) {
|
|
|
1560
1582
|
|
|
1561
1583
|
session.log('embed.html request : ' + req.url)
|
|
1562
1584
|
|
|
1563
|
-
delete req.headers.host
|
|
1564
|
-
|
|
1565
1585
|
let startFrom = VALID_START_FROM[0]
|
|
1566
1586
|
if ( req.query.startFrom ) {
|
|
1567
1587
|
startFrom = req.query.startFrom
|
|
@@ -1582,8 +1602,13 @@ app.get('/embed.html', async function(req, res) {
|
|
|
1582
1602
|
}
|
|
1583
1603
|
session.debuglog('embed src : ' + video_url)
|
|
1584
1604
|
|
|
1605
|
+
let content_protect = ''
|
|
1606
|
+
if ( session.protection.content_protect ) {
|
|
1607
|
+
content_protect = '?content_protect=' + session.protection.content_protect
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1585
1610
|
// Adapted from https://hls-js.netlify.app/demo/basic-usage.html
|
|
1586
|
-
var body = '<html><head><meta charset="UTF-8"><meta http-equiv="Content-type" content="text/html;charset=UTF-8"><title>' + appname + ' player</title><link rel="icon" href="favicon.svg"><style type="text/css">input[type=text],input[type=button]{-webkit-appearance:none;-webkit-border-radius:0}body{background-color:black;color:lightgrey;font-family:Arial,Helvetica,sans-serif}video{width:100% !important;height:auto !important;max-width:1280px}input[type=number]::-webkit-inner-spin-button{opacity:1}button{color:lightgray;background-color:black}button.default{color:black;background-color:lightgray}</style><script>function goBack(){var prevPage=window.location.href;window.history.go(-1);setTimeout(function(){if(window.location.href==prevPage){window.location.href="/"}}, 500)}function toggleAudio(x){var elements=document.getElementsByClassName("audioButton");for(var i=0;i<elements.length;i++){elements[i].className="audioButton"}document.getElementById("audioButton"+x).className+=" default";hls.audioTrack=x}function changeTime(x){video.currentTime+=x}function changeRate(x){let newRate=Math.round((Number(document.getElementById("playback_rate").value)+x)*10)/10;if((newRate<=document.getElementById("playback_rate").max) && (newRate>=document.getElementById("playback_rate").min)){document.getElementById("playback_rate").value=newRate.toFixed(1);video.defaultPlaybackRate=video.playbackRate=document.getElementById("playback_rate").value}}function myKeyPress(e){if(e.key=="ArrowRight"){changeTime(10)}else if(e.key=="ArrowLeft"){changeTime(-10)}else if(e.key=="ArrowUp"){changeRate(0.1)}else if(e.key=="ArrowDown"){changeRate(-0.1)}}</script></head><body onkeydown="myKeyPress(event)"><script src="https://hls-js.netlify.app/dist/hls.js"></script><video id="video"'
|
|
1611
|
+
var body = '<html><head><meta charset="UTF-8"><meta http-equiv="Content-type" content="text/html;charset=UTF-8"><title>' + appname + ' player</title><link rel="icon" href="favicon.svg"><style type="text/css">input[type=text],input[type=button]{-webkit-appearance:none;-webkit-border-radius:0}body{background-color:black;color:lightgrey;font-family:Arial,Helvetica,sans-serif}video{width:100% !important;height:auto !important;max-width:1280px}input[type=number]::-webkit-inner-spin-button{opacity:1}button{color:lightgray;background-color:black}button.default{color:black;background-color:lightgray}</style><script>function goBack(){var prevPage=window.location.href;window.history.go(-1);setTimeout(function(){if(window.location.href==prevPage){window.location.href="/' + content_protect + '"}}, 500)}function toggleAudio(x){var elements=document.getElementsByClassName("audioButton");for(var i=0;i<elements.length;i++){elements[i].className="audioButton"}document.getElementById("audioButton"+x).className+=" default";hls.audioTrack=x}function changeTime(x){video.currentTime+=x}function changeRate(x){let newRate=Math.round((Number(document.getElementById("playback_rate").value)+x)*10)/10;if((newRate<=document.getElementById("playback_rate").max) && (newRate>=document.getElementById("playback_rate").min)){document.getElementById("playback_rate").value=newRate.toFixed(1);video.defaultPlaybackRate=video.playbackRate=document.getElementById("playback_rate").value}}function myKeyPress(e){if(e.key=="ArrowRight"){changeTime(10)}else if(e.key=="ArrowLeft"){changeTime(-10)}else if(e.key=="ArrowUp"){changeRate(0.1)}else if(e.key=="ArrowDown"){changeRate(-0.1)}}</script></head><body onkeydown="myKeyPress(event)"><script src="https://hls-js.netlify.app/dist/hls.js"></script><video id="video"'
|
|
1587
1612
|
if ( controls == VALID_CONTROLS[0] ) {
|
|
1588
1613
|
body += ' controls'
|
|
1589
1614
|
}
|
|
@@ -1653,7 +1678,7 @@ app.get('/channels.m3u', async function(req, res) {
|
|
|
1653
1678
|
|
|
1654
1679
|
session.log('channels.m3u request : ' + req.url)
|
|
1655
1680
|
|
|
1656
|
-
let mediaType =
|
|
1681
|
+
let mediaType = VALID_MEDIA_TYPES[0]
|
|
1657
1682
|
if ( req.query.mediaType ) {
|
|
1658
1683
|
mediaType = req.query.mediaType
|
|
1659
1684
|
}
|
|
@@ -1696,7 +1721,7 @@ app.get('/guide.xml', async function(req, res) {
|
|
|
1696
1721
|
|
|
1697
1722
|
session.log('guide.xml request : ' + req.url)
|
|
1698
1723
|
|
|
1699
|
-
let mediaType =
|
|
1724
|
+
let mediaType = VALID_MEDIA_TYPES[0]
|
|
1700
1725
|
if ( req.query.mediaType ) {
|
|
1701
1726
|
mediaType = req.query.mediaType
|
|
1702
1727
|
}
|
|
@@ -1853,7 +1878,7 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
|
|
|
1853
1878
|
if ( stream_count > 1 ) {
|
|
1854
1879
|
complexFilter.push({
|
|
1855
1880
|
filter: 'setpts=PTS-STARTPTS',
|
|
1856
|
-
inputs: i+':v',
|
|
1881
|
+
inputs: i+':v:0',
|
|
1857
1882
|
outputs: 'v'+i
|
|
1858
1883
|
})
|
|
1859
1884
|
xstack_inputs.push('v'+i)
|
|
@@ -1904,7 +1929,7 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
|
|
|
1904
1929
|
// Filters: resampling preserve timestamps and padding allows the multiview stream to continue if one stream ends
|
|
1905
1930
|
audio_reencoded = []
|
|
1906
1931
|
for (var i=0; i<audio_present.length; i++) {
|
|
1907
|
-
let audio_input = audio_present[i] + ':
|
|
1932
|
+
let audio_input = audio_present[i] + ':m:language:en?'
|
|
1908
1933
|
let filter = ''
|
|
1909
1934
|
// Optionally apply sync adjustments
|
|
1910
1935
|
if ( sync[audio_present[i]] ) {
|
|
@@ -1946,8 +1971,8 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
|
|
|
1946
1971
|
}
|
|
1947
1972
|
}
|
|
1948
1973
|
|
|
1949
|
-
// Default to keep only
|
|
1950
|
-
var hls_list_size =
|
|
1974
|
+
// Default to keep only 1 minute of segments on disk, unless dvr is specified
|
|
1975
|
+
var hls_list_size = 60 / SECONDS_PER_SEGMENT
|
|
1951
1976
|
var delete_segments = 'delete_segments+'
|
|
1952
1977
|
if ( dvr ) {
|
|
1953
1978
|
hls_list_size = 0
|
|
@@ -1990,7 +2015,7 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
|
|
|
1990
2015
|
ffmpeg_command.addOutputOption('-sn')
|
|
1991
2016
|
.addOutputOption('-t', '6:00:00')
|
|
1992
2017
|
.addOutputOption('-f', 'hls')
|
|
1993
|
-
.addOutputOption('-hls_time',
|
|
2018
|
+
.addOutputOption('-hls_time', SECONDS_PER_SEGMENT)
|
|
1994
2019
|
.addOutputOption('-hls_list_size', hls_list_size)
|
|
1995
2020
|
.addOutputOption('-hls_allow_cache', '0')
|
|
1996
2021
|
.addOutputOption('-hls_flags', delete_segments + 'independent_segments+discont_start+program_date_time')
|
package/package.json
CHANGED
package/session.js
CHANGED
|
@@ -533,7 +533,7 @@ class sessionClass {
|
|
|
533
533
|
if ( playlistURL ) {
|
|
534
534
|
return playlistURL
|
|
535
535
|
} else {
|
|
536
|
-
|
|
536
|
+
this.log('Failed to find audio playlist URL from ' + url)
|
|
537
537
|
return ''
|
|
538
538
|
}
|
|
539
539
|
}
|
|
@@ -1143,7 +1143,7 @@ class sessionClass {
|
|
|
1143
1143
|
let cache_name = dateString
|
|
1144
1144
|
let url = 'https://bdfed.stitch.mlbinfra.com/bdfed/transform-mlb-scoreboard?stitch_env=prod&sortTemplate=2&sportId=1&sportId=17&startDate=' + dateString + '&endDate=' + dateString + '&gameType=E&&gameType=S&&gameType=R&&gameType=F&&gameType=D&&gameType=L&&gameType=W&&gameType=A&language=en&leagueId=104&leagueId=103&leagueId=131&contextTeamId='
|
|
1145
1145
|
if ( team ) {
|
|
1146
|
-
this.debuglog('getDayData for team ' +
|
|
1146
|
+
this.debuglog('getDayData for team ' + team + ' on date ' + dateString)
|
|
1147
1147
|
cache_name = team.toUpperCase() + dateString
|
|
1148
1148
|
url = 'http://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId=' + TEAM_IDS[team.toUpperCase()] + '&startDate=' + dateString + '&endDate=' + dateString + '&gameType=&gamePk=&hydrate=team,game(content(media(epg)))'
|
|
1149
1149
|
} else {
|