mlbserver 2022.5.13 → 2022.5.26

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.js +77 -59
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mlbserver
2
2
 
3
- Current version 2022.05.13
3
+ Current version 2022.05.26
4
4
 
5
5
  Credit to https://github.com/tonycpsu/streamglob and https://github.com/mafintosh/hls-decryptor
6
6
 
package/index.js CHANGED
@@ -38,6 +38,7 @@ 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
 
@@ -166,10 +167,10 @@ app.get('/stream.m3u8', async function(req, res) {
166
167
  let streamURL
167
168
  let options = {}
168
169
  let urlArray = req.url.split('?')
169
- 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) ) {
170
+ 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
171
  // load a sample encrypted HLS stream
171
172
  session.log('loading sample stream')
172
- options.resolution = 'adaptive'
173
+ options.resolution = VALID_RESOLUTIONS[0]
173
174
  streamURL = SAMPLE_STREAM_URL
174
175
  options.referer = 'https://hls-js.netlify.app/'
175
176
  } else {
@@ -231,7 +232,7 @@ app.get('/stream.m3u8', async function(req, res) {
231
232
  session.debuglog('using streamURL : ' + streamURL)
232
233
 
233
234
  if ( streamURL.indexOf('master_radio_') > 0 ) {
234
- options.resolution = 'adaptive'
235
+ options.resolution = VALID_RESOLUTIONS[0]
235
236
  }
236
237
 
237
238
  if ( req.query.audio_url && (req.query.audio_url != '') ) {
@@ -384,7 +385,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
384
385
  var video_track_matched = false
385
386
  var audio_track_matched = false
386
387
  var frame_rate = '29.97'
387
- if ( (resolution != 'adaptive') && (resolution != 'none') ) {
388
+ if ( (resolution != VALID_RESOLUTIONS[0]) && (resolution != VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) ) {
388
389
  if ( resolution.endsWith('p60') ) {
389
390
  frame_rate = '59.94'
390
391
  resolution = resolution.slice(0, -3)
@@ -419,7 +420,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
419
420
  }
420
421
 
421
422
  // Omit captions track when no video is specified
422
- if ( (resolution == 'none') && (line.indexOf('#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,') === 0) ) {
423
+ if ( (resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) && (line.indexOf('#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,') === 0) ) {
423
424
  return
424
425
  }
425
426
 
@@ -430,13 +431,13 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
430
431
  audio_track_matched = true
431
432
  return '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="' + audio_url + content_protect + referer_parameter + '"'
432
433
  }
433
- if ( audio_track == 'none') return
434
- if ( (resolution == 'none') && (line.indexOf(',URI=') < 0) ) return
435
- if ( (audio_track != 'all') && ((line.indexOf('NAME="'+audio_track+'"') > 0) || (line.indexOf('NAME="'+audio_track.substring(0,audio_track.length-1)+'"') > 0)) ) {
434
+ if ( audio_track == VALID_AUDIO_TRACKS[VALID_AUDIO_TRACKS.length-1]) return
435
+ if ( (resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) && (line.indexOf(',URI=') < 0) ) return
436
+ 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
437
  audio_track_matched = true
437
438
  line = line.replace('AUTOSELECT=NO','AUTOSELECT=YES')
438
439
  if ( line.indexOf(',DEFAULT=YES') < 0 ) line = line.replace('AUTOSELECT=YES','AUTOSELECT=YES,DEFAULT=YES')
439
- } else if ( (audio_track != 'all') && ((line.indexOf('NAME="'+audio_track+'"') === -1) || (line.indexOf('NAME="'+audio_track.substring(0,audio_track.length-1)+'"') === -1)) ) {
440
+ } 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
441
  return
441
442
  }
442
443
  if (line.indexOf(',URI=') > 0) {
@@ -452,7 +453,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
452
453
  if ( pad != VALID_PAD[0] ) newurl += '&pad=' + pad
453
454
  if ( contentId ) newurl += '&contentId=' + contentId
454
455
  newurl += content_protect + referer_parameter
455
- if ( resolution == 'none' ) {
456
+ if ( resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1] ) {
456
457
  audio_track_matched = true
457
458
  return line.replace(parsed[0],'') + "\n" + '#EXT-X-STREAM-INF:BANDWIDTH=50000,CODECS="mp4a.40.2",AUDIO="aac"' + "\n" + newurl
458
459
  }
@@ -464,10 +465,10 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
464
465
 
465
466
  // Parse video tracks to only include matching one, if specified
466
467
  if (line.indexOf('#EXT-X-STREAM-INF:BANDWIDTH=') === 0) {
467
- if ( resolution == 'none' ) {
468
+ if ( resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1] ) {
468
469
  return
469
470
  } else {
470
- if ( resolution === 'adaptive' ) {
471
+ if ( resolution === VALID_RESOLUTIONS[0] ) {
471
472
  return line
472
473
  } else {
473
474
  if (line.indexOf(resolution+',FRAME-RATE='+frame_rate) > 0) {
@@ -489,7 +490,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
489
490
  return line
490
491
  }
491
492
 
492
- if ( (resolution === 'adaptive') || (video_track_matched) ) {
493
+ if ( (resolution === VALID_RESOLUTIONS[0]) || (video_track_matched) ) {
493
494
  video_track_matched = false
494
495
  newurl = encodeURIComponent(url.resolve(streamURL, line.trim()))
495
496
  if ( force_vod != VALID_FORCE_VOD[0] ) newurl += '&force_vod=on'
@@ -667,11 +668,17 @@ app.get('/playlist', async function(req, res) {
667
668
  if ( body_array[last_segment_index] == '#EXT-X-ENDLIST' ) {
668
669
  session.debuglog('padding archive stream with extra segments')
669
670
  last_segment_index--
670
- let pad_lines = '#EXT-X-DISCONTINUITY' + '\n' + body_array[last_segment_index-1] + '\n' + body_array[last_segment_index] + '\n'
671
+ while ( !body_array[last_segment_index].startsWith('#EXTINF:5') ) {
672
+ last_segment_index--
673
+ }
674
+ last_segment_inf = body_array[last_segment_index]
675
+ last_segment_ts = body_array[last_segment_index+1]
676
+ let pad_lines = '#EXT-X-DISCONTINUITY' + '\n' + last_segment_inf + '\n' + last_segment_ts + '\n'
671
677
  session.debuglog(pad_lines)
672
678
  for (i=0; i<pad; i++) {
673
679
  body += pad_lines
674
680
  }
681
+ body += '#EXT-X-ENDLIST' + '\n'
675
682
  }
676
683
  }
677
684
  if ( force_vod != VALID_FORCE_VOD[0] ) body += '#EXT-X-ENDLIST' + '\n'
@@ -860,11 +867,13 @@ app.get('/', async function(req, res) {
860
867
  session.setScanMode(req.query.scan_mode)
861
868
  }
862
869
 
870
+ var content_protect = ''
863
871
  var content_protect_a = ''
864
872
  var content_protect_b = ''
865
873
  if ( session.protection.content_protect ) {
866
- content_protect_a = '?content_protect=' + session.protection.content_protect
867
- content_protect_b = '&content_protect=' + session.protection.content_protect
874
+ content_protect = session.protection.content_protect
875
+ content_protect_a = '?content_protect=' + content_protect
876
+ content_protect_b = '&content_protect=' + content_protect
868
877
  }
869
878
 
870
879
  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 +888,13 @@ app.get('/', async function(req, res) {
879
888
  body += '</style><script type="text/javascript">' + "\n";
880
889
 
881
890
  // 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"
891
+ 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
892
  // audio_url is disabled here, now used in multiview instead
884
893
  //body += 'var audio_url="' + audio_url + '";' + "\n"
885
894
 
886
895
  // Reload function, called after options change
887
896
  // 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=="Embed"){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+"&"}window.location=newurl.substring(0,newurl.length-1)}' + "\n"
897
+ 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
898
 
890
899
  // Ajax function for multiview and highlights
891
900
  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 +916,7 @@ app.get('/', async function(req, res) {
907
916
  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
917
  for (var i = 0; i < VALID_DATES.length; i++) {
909
918
  body += '<button '
910
- if ( ((VALID_DATES[i] == 'today') && (gameDate == session.liveDate())) || ((VALID_DATES[i] == 'yesterday') && (gameDate == session.yesterdayDate())) ) body += 'class="default" '
919
+ if ( ((VALID_DATES[i] == VALID_DATES[0]) && (gameDate == session.liveDate())) || ((VALID_DATES[i] == VALID_DATES[1]) && (gameDate == session.yesterdayDate())) ) body += 'class="default" '
911
920
  body += 'onclick="date=\'' + VALID_DATES[i] + '\';reload()">' + VALID_DATES[i] + '</button> '
912
921
  }
913
922
  body += '</p>' + "\n" + '<p><span class="tinytext">Updated ' + session.getCacheUpdatedDate(gameDate) + '</span></p>' + "\n"
@@ -929,8 +938,8 @@ app.get('/', async function(req, res) {
929
938
  body += '</p>' + "\n"
930
939
 
931
940
  body += '<p>'
932
- if ( linkType == 'Embed' ) {
933
- body += '<p><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>: '
941
+ if ( linkType == VALID_LINK_TYPES[0] ) {
942
+ 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
943
  for (var i = 0; i < VALID_CONTROLS.length; i++) {
935
944
  body += '<button '
936
945
  if ( controls == VALID_CONTROLS[i] ) body += 'class="default" '
@@ -945,12 +954,12 @@ app.get('/', async function(req, res) {
945
954
  body += 'onclick="startFrom=\'' + VALID_START_FROM[i] + '\';reload()">' + VALID_START_FROM[i] + '</button> '
946
955
  }
947
956
  body += "\n"
948
- }
949
-
950
- if ( mediaType == 'Video' ) {
951
- if ( linkType == 'Embed' ) {
957
+ if ( mediaType == VALID_MEDIA_TYPES[0] ) {
952
958
  body += 'or '
953
959
  }
960
+ }
961
+
962
+ if ( mediaType == VALID_MEDIA_TYPES[0] ) {
954
963
  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
964
  body += '<select id="inning_half" onchange="inning_half=this.value;reload()">'
956
965
  for (var i = 0; i < VALID_INNING_HALF.length; i++) {
@@ -986,21 +995,21 @@ app.get('/', async function(req, res) {
986
995
  // Rename some parameters before display links
987
996
  var mediaFeedType = 'mediaFeedType'
988
997
  var language = 'en'
989
- if ( mediaType == 'Video' ) {
998
+ if ( mediaType == VALID_MEDIA_TYPES[0] ) {
990
999
  mediaType = 'MLBTV'
991
- } else if ( mediaType == 'Spanish' ) {
992
- mediaType = 'Audio'
1000
+ } else if ( mediaType == VALID_MEDIA_TYPES[2] ) {
1001
+ mediaType = VALID_MEDIA_TYPES[1]
993
1002
  language = 'es'
994
1003
  }
995
- if ( mediaType == 'Audio' ) {
1004
+ if ( mediaType == VALID_MEDIA_TYPES[1] ) {
996
1005
  mediaFeedType = 'type'
997
1006
  }
998
1007
  linkType = linkType.toLowerCase()
999
1008
  let link = linkType + '.html'
1000
- if ( linkType == 'stream' ) {
1009
+ if ( linkType == VALID_LINK_TYPES[1].toLowerCase() ) {
1001
1010
  link = linkType + '.m3u8'
1002
1011
  } else {
1003
- force_vod = 'off'
1012
+ force_vod = VALID_FORCE_VOD[0]
1004
1013
  }
1005
1014
  var thislink = '/' + link
1006
1015
 
@@ -1014,12 +1023,12 @@ app.get('/', async function(req, res) {
1014
1023
  if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
1015
1024
  let querystring = '?event=biginning'
1016
1025
  let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
1017
- if ( linkType == 'embed' ) {
1026
+ if ( linkType == VALID_LINK_TYPES[0] ) {
1018
1027
  if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
1019
1028
  if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
1020
1029
  }
1021
1030
  if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
1022
- if ( linkType == 'stream' ) {
1031
+ if ( linkType == VALID_LINK_TYPES[1] ) {
1023
1032
  if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
1024
1033
  }
1025
1034
  querystring += content_protect_b
@@ -1062,7 +1071,7 @@ app.get('/', async function(req, res) {
1062
1071
  }
1063
1072
  var relative_inning = (inning_number - (9 - scheduledInnings))
1064
1073
  relative_inning = relative_inning < 0 ? 0 : relative_inning
1065
- if ( (scores == 'Show') && (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 ) {
1074
+ 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
1075
  let awayscore = ''
1067
1076
  let homescore = ''
1068
1077
  if ( (inning_number != VALID_INNING_NUMBER[0]) && cache_data.dates[0].games[j].linescore && cache_data.dates[0].games[j].linescore.innings ) {
@@ -1244,7 +1253,7 @@ app.get('/', async function(req, res) {
1244
1253
  let querystring
1245
1254
  querystring = '?mediaId=' + mediaId
1246
1255
  let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + '&audio_track=' + DEFAULT_MULTIVIEW_AUDIO_TRACK
1247
- if ( linkType == 'embed' ) {
1256
+ if ( linkType == VALID_LINK_TYPES[0] ) {
1248
1257
  if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
1249
1258
  if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
1250
1259
  }
@@ -1262,7 +1271,7 @@ app.get('/', async function(req, res) {
1262
1271
  //if ( audio_url != '' ) querystring += '&audio_url=' + encodeURIComponent(audio_url)
1263
1272
  }
1264
1273
  if ( pad != VALID_PAD[0] ) querystring += '&pad=' + pad
1265
- if ( linkType == 'stream' ) {
1274
+ if ( linkType == VALID_LINK_TYPES[1] ) {
1266
1275
  if ( cache_data.dates[0].games[j].content.media.epg[k].items[x].mediaState == 'MEDIA_ON' ) {
1267
1276
  if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
1268
1277
  }
@@ -1333,10 +1342,10 @@ app.get('/', async function(req, res) {
1333
1342
 
1334
1343
  // Rename parameter back before displaying further links
1335
1344
  if ( mediaType == 'MLBTV' ) {
1336
- mediaType = 'Video'
1345
+ mediaType = VALID_MEDIA_TYPES[0]
1337
1346
  }
1338
1347
 
1339
- if ( mediaType == 'Video' ) {
1348
+ if ( mediaType == VALID_MEDIA_TYPES[0] ) {
1340
1349
  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
1350
  for (var i = 0; i < VALID_RESOLUTIONS.length; i++) {
1342
1351
  body += '<button '
@@ -1376,8 +1385,8 @@ app.get('/', async function(req, res) {
1376
1385
  }
1377
1386
  body += '</p>' + "\n"
1378
1387
 
1379
- if ( mediaType == 'Video' ) {
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'
1388
+ if ( mediaType == VALID_MEDIA_TYPES[0] ) {
1389
+ 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
1390
  if ( ffmpeg_status ) body += 'ed'
1382
1391
  body += '</a> | <a id="stopmultiview" href="" onclick="stopmultiview(this);return false">Stop'
1383
1392
  if ( !ffmpeg_status ) body += 'ped'
@@ -1396,7 +1405,7 @@ app.get('/', async function(req, res) {
1396
1405
  body += '</td></tr></table><br/>' + "\n"
1397
1406
  }
1398
1407
 
1399
- if ( (linkType == 'stream') && (gameDate == session.liveDate()) ) {
1408
+ if ( (linkType == VALID_LINK_TYPES[1]) && (gameDate == session.liveDate()) ) {
1400
1409
  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
1410
  for (var i = 0; i < VALID_FORCE_VOD.length; i++) {
1402
1411
  body += '<button '
@@ -1411,11 +1420,10 @@ app.get('/', async function(req, res) {
1411
1420
  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
1421
 
1413
1422
  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
- let options = ['off', 'on']
1415
- for (var i = 0; i < options.length; i++) {
1423
+ for (var i = 0; i < VALID_SCAN_MODES.length; i++) {
1416
1424
  body += '<button '
1417
- if ( scan_mode == options[i] ) body += 'class="default" '
1418
- body += 'onclick="scan_mode=\'' + options[i] + '\';reload()">' + options[i] + '</button> '
1425
+ if ( scan_mode == VALID_SCAN_MODES[i] ) body += 'class="default" '
1426
+ body += 'onclick="scan_mode=\'' + VALID_SCAN_MODES[i] + '\';reload()">' + VALID_SCAN_MODES[i] + '</button> '
1419
1427
  }
1420
1428
  body += ' <span class="tinytext">(ON plays sample for all stream requests)</span></p>' + "\n"
1421
1429
 
@@ -1463,8 +1471,13 @@ app.get('/', async function(req, res) {
1463
1471
  }
1464
1472
  body += '</p></td></tr></table><br/>' + "\n"
1465
1473
 
1466
- let media_center_link = '/live-stream-games/' + gameDate.replace(/-/g,'/') + '?linkType=' + linkType
1467
- body += '<p><span class="tooltip">Media Center View<span class="tooltiptext">Allows you to use the MLB Media Center page format for nagivation. However, only the "Link Type" option is supported.</span></span>: <a href="' + media_center_link + '" target="_blank">Link</a></p>' + "\n"
1474
+ let local_url = '' // default to embedded player
1475
+ let urlArray = req.url.split('?')
1476
+ if ( (urlArray.length == 2) ) {
1477
+ local_url += '?' + urlArray[1]
1478
+ }
1479
+ let media_center_link = '/live-stream-games/' + gameDate.replace(/-/g,'/') + local_url
1480
+ 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
1481
 
1469
1482
  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
1483
 
@@ -1514,7 +1527,7 @@ app.get('/live-stream-games*', async function(req, res) {
1514
1527
  session.debuglog('schedule request : ' + req.url)
1515
1528
 
1516
1529
  // check for a linkType parameter in the url
1517
- let linkType = 'embed'
1530
+ let linkType = VALID_LINK_TYPES[0]
1518
1531
  if ( req.query.linkType ) {
1519
1532
  linkType = req.query.linkType
1520
1533
  session.setLinkType(linkType)
@@ -1522,15 +1535,17 @@ app.get('/live-stream-games*', async function(req, res) {
1522
1535
 
1523
1536
  // use the link type to determine the local url to use
1524
1537
  var local_url = '/embed.html' // default to embedded player
1525
- var content_protect = ''
1526
- if ( linkType == 'stream' ) { // direct stream
1538
+ if ( linkType == VALID_LINK_TYPES[1] ) { // direct stream
1527
1539
  local_url = '/stream.m3u8'
1528
- if ( session.protection.content_protect ) content_protect = '&content_protect=' + session.protection.content_protect
1529
1540
  } else { // other
1530
1541
  local_url = '/' + linkType + '.html'
1531
1542
  }
1543
+ let urlArray = req.url.split('?')
1544
+ if ( (urlArray.length == 2) ) {
1545
+ local_url += '?' + urlArray[1]
1546
+ }
1532
1547
 
1533
- // remove our linkType parameter, if specified, from the url we will fetch remotely
1548
+ // remove our local parameters, if specified, from the url we will fetch remotely
1534
1549
  var remote_url = url.parse(req.url).pathname
1535
1550
 
1536
1551
  let reqObj = {
@@ -1546,7 +1561,12 @@ app.get('/live-stream-games*', async function(req, res) {
1546
1561
  var body = await session.httpGet(reqObj)
1547
1562
 
1548
1563
  // 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+"?contentId=$1"+content_protect)
1564
+ body = body.replace(/https:\/\/www.mlb.com\/tv\/g\d+\/[v]([a-zA-Z0-9-]+)/g,local_url+"&contentId=$1")
1565
+
1566
+ // a regex substitution to remove unsupported filter menus
1567
+ if ( session.protection.content_protect ) {
1568
+ body = body.replace(/<div\n id="date-container"[\S\s]+><\/span>\n <\/div>/g,'')
1569
+ }
1550
1570
 
1551
1571
  // hide popup to accept cookies
1552
1572
  body = body.replace(/www.googletagmanager.com/g,'0.0.0.0')
@@ -1560,8 +1580,6 @@ app.get('/embed.html', async function(req, res) {
1560
1580
 
1561
1581
  session.log('embed.html request : ' + req.url)
1562
1582
 
1563
- delete req.headers.host
1564
-
1565
1583
  let startFrom = VALID_START_FROM[0]
1566
1584
  if ( req.query.startFrom ) {
1567
1585
  startFrom = req.query.startFrom
@@ -1653,7 +1671,7 @@ app.get('/channels.m3u', async function(req, res) {
1653
1671
 
1654
1672
  session.log('channels.m3u request : ' + req.url)
1655
1673
 
1656
- let mediaType = 'Video'
1674
+ let mediaType = VALID_MEDIA_TYPES[0]
1657
1675
  if ( req.query.mediaType ) {
1658
1676
  mediaType = req.query.mediaType
1659
1677
  }
@@ -1696,7 +1714,7 @@ app.get('/guide.xml', async function(req, res) {
1696
1714
 
1697
1715
  session.log('guide.xml request : ' + req.url)
1698
1716
 
1699
- let mediaType = 'Video'
1717
+ let mediaType = VALID_MEDIA_TYPES[0]
1700
1718
  if ( req.query.mediaType ) {
1701
1719
  mediaType = req.query.mediaType
1702
1720
  }
@@ -1853,7 +1871,7 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
1853
1871
  if ( stream_count > 1 ) {
1854
1872
  complexFilter.push({
1855
1873
  filter: 'setpts=PTS-STARTPTS',
1856
- inputs: i+':v',
1874
+ inputs: i+':v:0',
1857
1875
  outputs: 'v'+i
1858
1876
  })
1859
1877
  xstack_inputs.push('v'+i)
@@ -1904,7 +1922,7 @@ function start_multiview_stream(streams, sync, dvr, faster, reencode, audio_url,
1904
1922
  // Filters: resampling preserve timestamps and padding allows the multiview stream to continue if one stream ends
1905
1923
  audio_reencoded = []
1906
1924
  for (var i=0; i<audio_present.length; i++) {
1907
- let audio_input = audio_present[i] + ':a:0'
1925
+ let audio_input = audio_present[i] + ':m:language:en?'
1908
1926
  let filter = ''
1909
1927
  // Optionally apply sync adjustments
1910
1928
  if ( sync[audio_present[i]] ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlbserver",
3
- "version": "2022.05.13",
3
+ "version": "2022.05.26",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",