mlbserver 2025.3.30 → 2025.4.0-1.2

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/index.js +32 -29
  2. package/package.json +1 -1
  3. package/session.js +38 -110
package/index.js CHANGED
@@ -44,7 +44,7 @@ const VALID_SCAN_MODES = [ 'off', 'on' ]
44
44
 
45
45
  const SAMPLE_STREAM_URL = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
46
46
 
47
- const SECONDS_PER_SEGMENT = 5
47
+ const SECONDS_PER_SEGMENT = 4
48
48
 
49
49
  // for favorites: text, then background, based on https://teamcolors.jim-nielsen.com/
50
50
  const TEAM_COLORS = { 'ATH': ['003831', 'EFB21E'], 'ATL': ['13274F', 'CE1141'], 'AZ': ['E3D4AD', 'A71930'], 'BAL': ['000000', 'DF4601'], 'BOS': ['0D2B56', 'BD3039'], 'CHC': ['CC3433', '0E3386'], 'CWS': ['000000', 'C4CED4'], 'CIN': ['FFFFFF', 'C6011F'], 'CLE': ['002B5C', 'E31937'], 'COL': ['C4CED4', '333366'], 'DET': ['0C2C56', 'FFFFFF'], 'HOU': ['002D62', 'EB6E1F'], 'KC': ['C09A5B', '004687'], 'LAA': ['FFFFFF', 'BA0021'], 'LAD': ['FFFFFF', '005A9C'], 'MIA': ['0077C8', 'FF6600'], 'MIL': ['0A2351', 'B6922E'], 'MIN': ['D31145', '002B5C'], 'NYM': ['002D72', 'FF5910'], 'NYY': ['FFFFFF', '003087'], 'OAK': ['003831', 'EFB21E'], 'PHI': ['284898', 'E81828'], 'PIT': ['000000', 'FDB827'], 'STL': ['FEDB00', 'C41E3A'], 'SD': ['FEC325', '7F411C'], 'SF': ['000000', 'FD5A1E'], 'SEA': ['C4CED4', '005C5C'], 'TB': ['092C5C', '8FBCE6'], 'TEX': ['003278', 'C0111F'], 'TOR': ['FFFFFF', '134A8E'], 'WSH': ['AB0003', '11225B'] }
@@ -863,26 +863,31 @@ app.get('/playlist.m3u8', async function(req, res) {
863
863
  })
864
864
  .join('\n')+'\n'
865
865
 
866
+ let end_tag = '#EXT-X-ENDLIST'
866
867
  if ( pad != VALID_PAD[0] ) {
867
868
  let body_array = body.trim().split('\n')
868
- let last_segment_index = body_array.length-1
869
- if ( body_array[last_segment_index] == '#EXT-X-ENDLIST' ) {
869
+ let segment_index = body_array.length-1
870
+ if ( body_array[segment_index] == end_tag ) {
870
871
  session.debuglog('padding archive stream with extra segments')
871
- last_segment_index--
872
- while ( !body_array[last_segment_index].startsWith('#EXTINF:') ) {
873
- last_segment_index--
872
+ while ( body_array[segment_index].startsWith('#') ) {
873
+ segment_index--
874
874
  }
875
- last_segment_inf = body_array[last_segment_index]
876
- last_segment = body_array[last_segment_index+1]
877
- let pad_lines = '#EXT-X-DISCONTINUITY' + '\n' + '#EXTINF:' + SECONDS_PER_SEGMENT + '\n' + last_segment + '\n'
878
- session.debuglog(pad_lines)
875
+ let last_segment = body_array[segment_index]
876
+ body_array.pop()
879
877
  for (i=0; i<pad; i++) {
880
- body += pad_lines
878
+ body_array.push('#EXT-X-DISCONTINUITY')
879
+ body_array.push('#EXTINF:' + SECONDS_PER_SEGMENT)
880
+ body_array.push(last_segment)
881
881
  }
882
- body += '#EXT-X-ENDLIST' + '\n'
882
+ body_array.push(end_tag)
883
+ body = body_array.join('\n')
884
+ }
885
+ } else if ( force_vod != VALID_FORCE_VOD[0] ) {
886
+ let body_array = body.trim().split('\n')
887
+ if ( body_array[body_array.length-1] != end_tag ) {
888
+ body += end_tag + '\n'
883
889
  }
884
890
  }
885
- if ( force_vod != VALID_FORCE_VOD[0] ) body += '#EXT-X-ENDLIST' + '\n'
886
891
  session.debuglog(body)
887
892
  respond(response, res, Buffer.from(body))
888
893
  })
@@ -1041,7 +1046,7 @@ app.get('/gamechanger.m3u8', async function(req, res) {
1041
1046
 
1042
1047
  for ( gamechanger_resolution in GAMECHANGER_RESOLUTIONS ) {
1043
1048
  if ( resolution == gamechanger_resolution ) {
1044
- body += '#EXT-X-STREAM-INF:BANDWIDTH=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].bandwidth + '000,RESOLUTION=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].resolution + ',FRAME-RATE=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].frame_rate + ',CODECS="mp4a.40.2,avc1.' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].codec + '",CLOSED-CAPTIONS="cc",AUDIO="aac"' + '\n' + '/gamechangerplaylist?id=' + id + '&resolution=' + gamechanger_resolution + includeTeams + excludeTeams + content_protect + '\n'
1049
+ body += '#EXT-X-STREAM-INF:BANDWIDTH=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].bandwidth + '000,RESOLUTION=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].resolution + ',FRAME-RATE=' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].frame_rate + ',CODECS="mp4a.40.2,avc1.' + GAMECHANGER_RESOLUTIONS[gamechanger_resolution].codec + '",CLOSED-CAPTIONS="cc",AUDIO="aac"' + '\n' + '/gamechangerplaylist.m3u8?id=' + id + '&resolution=' + gamechanger_resolution + includeTeams + excludeTeams + content_protect + '\n'
1045
1050
  break
1046
1051
  }
1047
1052
  }
@@ -1055,10 +1060,10 @@ app.get('/gamechanger.m3u8', async function(req, res) {
1055
1060
 
1056
1061
 
1057
1062
  // Listen for gamechanger playlist requests
1058
- app.get('/gamechangerplaylist', async function(req, res) {
1063
+ app.get('/gamechangerplaylist.m3u8', async function(req, res) {
1059
1064
  if ( ! (await protect(req, res)) ) return
1060
1065
 
1061
- session.requestlog('gamechangerplaylist', req, true)
1066
+ session.requestlog('gamechangerplaylist.m3u8', req, true)
1062
1067
 
1063
1068
  let gamechangerAccess = new Date()
1064
1069
 
@@ -1661,8 +1666,7 @@ app.get('/', async function(req, res) {
1661
1666
  }
1662
1667
 
1663
1668
  // Big Inning
1664
- // disabled Big Inning schedule scraping (March 2025)
1665
- /*var big_inning
1669
+ var big_inning
1666
1670
  if ( cache_data.dates && cache_data.dates[0] && (cache_data.dates[0].date >= today) && cache_data.dates[0].games && (cache_data.dates[0].games.length > 1) && cache_data.dates[0].games[0] && (cache_data.dates[0].games[0].seriesDescription == 'Regular Season') ) {
1667
1671
  // Scraped Big Inning schedule
1668
1672
  big_inning = await session.getBigInningSchedule(gameDate)
@@ -1676,7 +1680,7 @@ app.get('/', async function(req, res) {
1676
1680
  compareStart.setMinutes(compareStart.getMinutes()-10)
1677
1681
  let compareEnd = new Date(big_inning.end)
1678
1682
  compareEnd.setHours(compareEnd.getHours()+1)
1679
- if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {*/
1683
+ if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
1680
1684
  body += '<tr><td><span class="tooltip">Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://support.mlb.com/s/article/What-Is-MLB-Big-Inning">See here for more information</a>.</span></span></td><td>'
1681
1685
  let querystring = '?event=biginning'
1682
1686
  let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
@@ -1694,11 +1698,11 @@ app.get('/', async function(req, res) {
1694
1698
  multiviewquerystring += content_protect_b
1695
1699
  body += '<a href="' + thislink + querystring + '">Big Inning</a>'
1696
1700
  body += '<input type="checkbox" value="http://127.0.0.1:' + session.data.port + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
1697
- /*} else {
1701
+ } else {
1698
1702
  body += 'Big Inning'
1699
- }*/
1703
+ }
1700
1704
  body += '</td></tr>' + "\n"
1701
- //}
1705
+ }
1702
1706
 
1703
1707
  // Game Changer
1704
1708
  if ( (gameDate >= today) && cache_data.dates && cache_data.dates[0] && cache_data.dates[0].games && (cache_data.dates[0].games.length > 1) ) {
@@ -1713,15 +1717,15 @@ app.get('/', async function(req, res) {
1713
1717
  compareEnd.setHours(compareEnd.getHours()+4)
1714
1718
  body += '<tr><td><span class="tooltip">' + compareStart.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + ' - ' + compareEnd.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + '<span class="tooltiptext">The game changer stream will automatically switch between the highest leverage active live non-blackout games, and should be available whenever there are such games available. Does not support adaptive bitrate switching, will default to 720p60 resolution if not specified.</span></span></td><td>'
1715
1719
  if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
1716
- let streamURL = server + '/gamechanger.m3u8'
1717
- let multiviewquerystring = streamURL + '?resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + content_protect_b
1720
+ let streamURL = server + '/gamechanger.m3u8?'
1721
+ let multiviewquerystring = streamURL + 'resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + content_protect_b
1718
1722
  streamURL += content_protect_a
1719
- if ( resolution != VALID_RESOLUTIONS[0] ) streamURL += '&resolution=' + resolution
1723
+ if ( resolution != VALID_RESOLUTIONS[0] ) streamURL += 'resolution=' + resolution + '&'
1720
1724
  if ( linkType != VALID_LINK_TYPES[1] ) {
1721
- streamURL = thislink + '?src=' + encodeURIComponent(streamURL) + '&startFrom=' + VALID_START_FROM[1] + content_protect_b
1725
+ streamURL = thislink + '?src=' + encodeURIComponent(streamURL) + 'startFrom=' + VALID_START_FROM[1] + content_protect_b + '&'
1722
1726
  }
1723
1727
  if ( linkType == VALID_LINK_TYPES[4] ) {
1724
- streamURL += '&filename=' + gameDate + ' Game Changer'
1728
+ streamURL += 'filename=' + gameDate + ' Game Changer' + '&'
1725
1729
  }
1726
1730
  body += '<a href="' + streamURL + '">Game Changer</a>'
1727
1731
  body += '<input type="checkbox" value="http://127.0.0.1:' + session.data.port + multiviewquerystring + '" onclick="addmultiview(this, [], excludeTeams)">'
@@ -2277,8 +2281,7 @@ app.get('/', async function(req, res) {
2277
2281
  body += '<p><span class="tooltip">Include (or exclude) SNY<span class="tooltiptext">SNY live stream for entitled subscribers. <a href="https://support.mlb.com/s/article/SNLA-Plus-Subscription-Packages">See here for more information</a>.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=sny' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=sny' + content_protect_b + '">xml</a></p>' + "\n"
2278
2282
  }
2279
2283
 
2280
- // disabled Big Inning schedule scraping (March 2025)
2281
- body += '<p><span class="tooltip">Include (or exclude) Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://www.mlb.com/live-stream-games/big-inning">See here for more information</a>.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=biginning' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">xml</a><!-- and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">ics</a>--></p>' + "\n"
2284
+ body += '<p><span class="tooltip">Include (or exclude) Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://www.mlb.com/live-stream-games/big-inning">See here for more information</a>.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=biginning' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">ics</a></p>' + "\n"
2282
2285
 
2283
2286
  let gamechanger_resolution = resolution
2284
2287
  if ( gamechanger_resolution == VALID_RESOLUTIONS[0] ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlbserver",
3
- "version": "2025.03.30",
3
+ "version": "2025.04.01.2",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
package/session.js CHANGED
@@ -46,6 +46,8 @@ const ACTION_END_PADDING = 7
46
46
  const MINIMUM_BREAK_DURATION = 5
47
47
  // extra padding for MLB events (2025)
48
48
  const MLB_PADDING = 39
49
+ // extra Game Changer padding for MLB (2025)
50
+ const MLB_GAMECHANGER_PADDING = 20
49
51
 
50
52
  const LI_TABLE = {
51
53
  1: {
@@ -1271,7 +1273,7 @@ class sessionClass {
1271
1273
  this.save_cache_data()
1272
1274
  }
1273
1275
 
1274
- cacheStreamURL(mediaId, streamURL, streamURLToken='', streamURLExpiration='') {
1276
+ cacheStreamURL(mediaId, streamURL, streamURLToken='', streamURLExpiration='', rawStreamURL='') {
1275
1277
  this.createMediaCache(mediaId)
1276
1278
  this.cache.media[mediaId].streamURL = streamURL
1277
1279
  if (streamURLToken != '') {
@@ -1284,6 +1286,7 @@ class sessionClass {
1284
1286
  } else {
1285
1287
  this.cache.media[mediaId].streamURLExpiry = new Date(streamURLExpiration)
1286
1288
  }
1289
+ this.cache.media[mediaId].rawStreamURL = rawStreamURL
1287
1290
  this.save_cache_data()
1288
1291
  }
1289
1292
 
@@ -1574,6 +1577,9 @@ class sessionClass {
1574
1577
  if ( this.cache.media && this.cache.media[mediaId] && this.cache.media[mediaId].streamURL && this.cache.media[mediaId].streamURLToken && this.cache.media[mediaId].streamURLExpiry && (Date.parse(this.cache.media[mediaId].streamURLExpiry) > new Date()) ) {
1575
1578
  this.debuglog('using cached streamURL and token')
1576
1579
  let streamInfo = {streamURL: this.cache.media[mediaId].streamURL, streamURLToken: this.cache.media[mediaId].streamURLToken}
1580
+ if ( this.cache.media[mediaId].rawStreamURL ) {
1581
+ streamInfo['rawStreamURL'] = this.cache.media[mediaId].rawStreamURL
1582
+ }
1577
1583
  return streamInfo
1578
1584
  } else if ( this.cache.media && this.cache.media[mediaId] && this.cache.media[mediaId].blackout && this.cache.media[mediaId].blackoutExpiry && (Date.parse(this.cache.media[mediaId].blackoutExpiry) > new Date()) ) {
1579
1585
  this.log('mediaId recently blacked out, skipping')
@@ -1616,19 +1622,15 @@ class sessionClass {
1616
1622
  if ( response.data && response.data.initPlaybackSession && response.data.initPlaybackSession.playback && response.data.initPlaybackSession.playback.url ) {
1617
1623
  let rawStreamURL = response.data.initPlaybackSession.playback.url
1618
1624
  this.debuglog('getStreamURL rawStreamURL : ' + rawStreamURL)
1619
- if ( response.data.initPlaybackSession.playback.token == null ) {
1620
- return rawStreamURL
1621
- } else if ( response.data.initPlaybackSession.playback.token && response.data.initPlaybackSession.playback.expiration ) {
1622
- let streamURL = rawStreamURL.replace(/[\/]([A-Za-z0-9_]+)[\/]/, '/')
1623
- this.debuglog('getStreamURL streamURL : ' + streamURL)
1624
- let streamURLToken = response.data.initPlaybackSession.playback.token
1625
- this.debuglog('getStreamURL token : ' + streamURLToken)
1626
- let streamURLExpiration = response.data.initPlaybackSession.playback.expiration
1627
- this.debuglog('getStreamURL expiration : ' + streamURLExpiration)
1628
- this.cacheStreamURL(mediaId, streamURL, streamURLToken, streamURLExpiration)
1629
- let streamInfo = {streamURL: streamURL, streamURLToken: streamURLToken}
1630
- return streamInfo
1631
- }
1625
+ let streamURL = rawStreamURL.replace(/[\/]([A-Za-z0-9_]+)[\/]/, '/')
1626
+ let streamURLToken = response.data.initPlaybackSession.playback.token
1627
+ let streamURLExpiration = response.data.initPlaybackSession.playback.expiration
1628
+ this.debuglog('getStreamURL streamURL : ' + streamURL)
1629
+ this.debuglog('getStreamURL token : ' + streamURLToken)
1630
+ this.debuglog('getStreamURL expiration : ' + streamURLExpiration)
1631
+ this.cacheStreamURL(mediaId, streamURL, streamURLToken, streamURLExpiration, rawStreamURL)
1632
+ let streamInfo = {streamURL: streamURL, streamURLToken: streamURLToken, rawStreamURL: rawStreamURL}
1633
+ return streamInfo
1632
1634
  } else {
1633
1635
  this.log('getStreamURL streamURL not found')
1634
1636
  return false
@@ -2710,7 +2712,7 @@ class sessionClass {
2710
2712
  let description = 'Live look-ins and big moments from around the league'
2711
2713
 
2712
2714
  // disabled Big Inning schedule scraping March 2025
2713
- /*for (var i = 0; i < cache_data.dates.length; i++) {
2715
+ for (var i = 0; i < cache_data.dates.length; i++) {
2714
2716
  // Scraped Big Inning schedule
2715
2717
  if ( (cache_data.dates[i].date >= today) && cache_data.dates[i].games && (cache_data.dates[i].games.length > 1) && cache_data.dates[i].games[0] && (cache_data.dates[i].games[0].seriesDescription == 'Regular Season') ) {
2716
2718
  await this.getBigInningSchedule()
@@ -2741,12 +2743,12 @@ class sessionClass {
2741
2743
  programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertDateToAirDate(new Date(this.cache.bigInningSchedule[gameDate].start)))
2742
2744
  }
2743
2745
  this.debuglog('getTVData completed Big Inning for date ' + cache_data.dates[i].date)
2744
- }*/
2746
+ }
2745
2747
 
2746
2748
  // generic Big Inning guide XML
2747
- let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
2749
+ /*let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
2748
2750
  let stop = this.convertDateToXMLTV(new Date(cache_data.dates[cache_data.dates.length-1].date + ' 00:00:00'))
2749
- programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))
2751
+ programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))*/
2750
2752
 
2751
2753
  this.debuglog('getTVData completed Big Inning')
2752
2754
  }
@@ -3197,17 +3199,15 @@ class sessionClass {
3197
3199
  this.debuglog('getBigInningSchedule')
3198
3200
 
3199
3201
  // temporarily disable Big Inning schedule checking until a new source URL is available
3200
- this.cache.bigInningSchedule = {}
3201
- return
3202
+ /*this.cache.bigInningSchedule = {}
3203
+ return*/
3202
3204
 
3203
3205
  let currentDate = new Date()
3204
3206
  if ( !this.cache || !this.cache.bigInningScheduleCacheExpiry || (currentDate > new Date(this.cache.bigInningScheduleCacheExpiry)) ) {
3205
3207
  if ( !this.cache.bigInningSchedule ) this.cache.bigInningSchedule = {}
3206
3208
  let reqObj = {
3207
- //url: 'https://www.mlb.com/live-stream-games/big-inning',
3208
- url: 'https://www.mlb.com/live-stream-games/help-center/subscription-access-big-inning',
3209
+ url: 'https://www.fubo.tv/welcome/channel/mlb-big-inning',
3209
3210
  headers: {
3210
- 'authority': 'www.mlb.com',
3211
3211
  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
3212
3212
  'accept-language': 'en-US,en;q=0.9',
3213
3213
  'cache-control': 'no-cache',
@@ -3230,89 +3230,15 @@ class sessionClass {
3230
3230
  if ( response ) {
3231
3231
  // disabled because it's very big!
3232
3232
  //this.debuglog(response)
3233
- // break HTML into array based on table rows
3234
- var rows = response.split('<tr>')
3235
- // start iterating at 2 (after header row)
3236
- for (var i=2; i<rows.length; i++) {
3237
- // split HTML row into array with columns
3238
- let cols = rows[i].split('<td>')
3239
-
3240
- // define some variables that persist for each row
3241
- let parts
3242
- let year
3243
- let month
3244
- let day
3245
- let this_datestring
3246
- let add_date = 0
3247
- let d
3248
-
3249
- for (var j=1; j<cols.length; j++) {
3250
- // split on closing bracket to get column text at resulting array index 0
3251
- let col = cols[j].split('<')
3252
- switch(j){
3253
- // first column is date
3254
- case 1:
3255
- // split date into array
3256
- // old date format (January 1, 1970) (disabled)
3257
- /*parts = col[0].split(' ')
3258
- year = parts[2]
3259
- // get month index, zero-based
3260
- month = new Date(Date.parse(parts[0] +" 1, 2021")).getMonth()
3261
- day = parts[1].substring(0,parts[1].length-3)*/
3262
- // new date format (01/01/70)
3263
- parts = col[0].split('/')
3264
- year = parts[2]
3265
- if ( year.length == 2 ) {
3266
- year = '20' + parts[2]
3267
- }
3268
- // get month index, zero-based
3269
- month = parseInt(parts[0]) - 1
3270
- day = parts[1]
3271
- this_datestring = new Date(year, month, day).toISOString().substring(0,10)
3272
- this.cache.bigInningSchedule[this_datestring] = {}
3273
- // increment month index (not zero-based)
3274
- month += 1
3275
- break
3276
- // remaining columns are times
3277
- default:
3278
- let hour
3279
- let minute = '00'
3280
- let ampm
3281
- // if time has colon, split into array on that to get hour and minute parts
3282
- if ( col[0].indexOf(':') > 0 ) {
3283
- parts = col[0].split(':')
3284
- hour = parseInt(parts[0])
3285
- minute = parts[1].substring(0,2)
3286
- } else {
3287
- hour = parseInt(col[0].substring(0,col[0].length-2))
3288
- }
3289
- ampm = col[0].substring(col[0].length-2,col[0].length)
3290
- // convert hour to 24-hour format
3291
- if ( (ampm == 'PM') || ((hour == 12) && (ampm == 'AM')) ) {
3292
- hour += 12
3293
- }
3294
- // these times are EDT so add 4 for UTC
3295
- hour += 4
3296
- // if hour is beyond 23, note we will have to add 1 day
3297
- if ( hour > 23 ) {
3298
- add_date = 1
3299
- hour -= 24
3300
- }
3301
-
3302
- d = new Date(this_datestring + 'T' + hour.toString().padStart(2, '0') + ':' + minute.toString().padStart(2, '0') + ':00.000+00:00')
3303
- d.setDate(d.getDate()+add_date)
3304
- switch(j){
3305
- // 2nd column is start time
3306
- case 2:
3307
- this.cache.bigInningSchedule[this_datestring].start = d
3308
- break
3309
- // 3rd column is end time
3310
- case 3:
3311
- this.cache.bigInningSchedule[this_datestring].end = d
3312
- break
3313
- }
3314
- break
3315
- }
3233
+
3234
+ let obj = JSON.parse(response.replace(/\\"/g, '"').match('(?<="channelPrograms":{"live":)(.+?(,"totalPages":1}))')[0])
3235
+ for (var i=0; i < obj.data.length; i++) {
3236
+ let est_date = new Date(obj.data[i].airings[0].start).toLocaleString("en-US", {timeZone: 'America/New_York'})
3237
+ let date_array = est_date.split(',')[0].split('/')
3238
+ let this_datestring = date_array[2] + '-' + date_array[0].padStart(2, '0') + '-' + date_array[1].padStart(2, '0')
3239
+ this.cache.bigInningSchedule[this_datestring] = {
3240
+ start: obj.data[i].airings[0].start,
3241
+ end: obj.data[i].airings[0].end
3316
3242
  }
3317
3243
  }
3318
3244
  this.debuglog(JSON.stringify(this.cache.bigInningSchedule))
@@ -3499,9 +3425,9 @@ class sessionClass {
3499
3425
  if ( response.data && response.data.contentCollections && (response.data.contentCollections.length > 0) && response.data.contentCollections[0].contents ) {
3500
3426
  for (var i=0; i<response.data.contentCollections[0].contents.length; i++) {
3501
3427
  try {
3502
- let streamURL = this.getStreamURL(response.data.contentCollections[0].contents[i].mediaId)
3503
- if ( streamURL ) {
3504
- return streamURL
3428
+ let streamInfo = await this.getStreamURL(response.data.contentCollections[0].contents[i].mediaId)
3429
+ if ( streamInfo.rawStreamURL ) {
3430
+ return streamInfo.rawStreamURL
3505
3431
  }
3506
3432
  } catch(e) {
3507
3433
  this.debuglog('getLinearStreamURL getStreamURL error : ' + e.message)
@@ -3610,8 +3536,10 @@ class sessionClass {
3610
3536
  playbackURL = 'https://falcon.mlbinfra.com/api/v1/linear/mlbn'
3611
3537
  } else if ( eventName.toUpperCase() == 'SNLA' ) {
3612
3538
  playbackURL = await this.getLinearStreamURL('SNLA_LIVE')
3539
+ return playbackURL
3613
3540
  } else if ( eventName.toUpperCase() == 'SNY' ) {
3614
3541
  playbackURL = await this.getLinearStreamURL('SNY_LIVE')
3542
+ return playbackURL
3615
3543
  } else {
3616
3544
  playbackURL = await this.getEventURL(eventName)
3617
3545
  }
@@ -4153,7 +4081,7 @@ class sessionClass {
4153
4081
 
4154
4082
  if ( !this.temp_cache.gamechanger[id].games ) this.temp_cache.gamechanger[id].games = []
4155
4083
  this.temp_cache.gamechanger[id].games.push(best_games)
4156
- let maxlength = (this.gamechanger_delay + 10) / 10
4084
+ let maxlength = (this.gamechanger_delay + 10 + MLB_GAMECHANGER_PADDING) / 10
4157
4085
  while ( this.temp_cache.gamechanger[id].games.length > maxlength ) {
4158
4086
  this.temp_cache.gamechanger[id].games.shift()
4159
4087
  }