mlbserver 2025.3.29 → 2025.4.1
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/index.js +76 -13
- package/package.json +1 -1
- package/session.js +169 -102
package/index.js
CHANGED
|
@@ -1041,7 +1041,7 @@ app.get('/gamechanger.m3u8', async function(req, res) {
|
|
|
1041
1041
|
|
|
1042
1042
|
for ( gamechanger_resolution in GAMECHANGER_RESOLUTIONS ) {
|
|
1043
1043
|
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'
|
|
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.m3u8?id=' + id + '&resolution=' + gamechanger_resolution + includeTeams + excludeTeams + content_protect + '\n'
|
|
1045
1045
|
break
|
|
1046
1046
|
}
|
|
1047
1047
|
}
|
|
@@ -1055,10 +1055,10 @@ app.get('/gamechanger.m3u8', async function(req, res) {
|
|
|
1055
1055
|
|
|
1056
1056
|
|
|
1057
1057
|
// Listen for gamechanger playlist requests
|
|
1058
|
-
app.get('/gamechangerplaylist', async function(req, res) {
|
|
1058
|
+
app.get('/gamechangerplaylist.m3u8', async function(req, res) {
|
|
1059
1059
|
if ( ! (await protect(req, res)) ) return
|
|
1060
1060
|
|
|
1061
|
-
session.requestlog('gamechangerplaylist', req, true)
|
|
1061
|
+
session.requestlog('gamechangerplaylist.m3u8', req, true)
|
|
1062
1062
|
|
|
1063
1063
|
let gamechangerAccess = new Date()
|
|
1064
1064
|
|
|
@@ -1562,11 +1562,11 @@ app.get('/', async function(req, res) {
|
|
|
1562
1562
|
|
|
1563
1563
|
let currentDate = new Date()
|
|
1564
1564
|
|
|
1565
|
+
let entitlements = await session.getEntitlements()
|
|
1565
1566
|
// MLB Network live stream for eligible USA subscribers
|
|
1566
1567
|
try {
|
|
1567
|
-
let entitlements = await session.getEntitlements()
|
|
1568
1568
|
if ( entitlements.includes('MLBN') || entitlements.includes('EXECMLB') || entitlements.includes('MLBTVMLBNADOBEPASS') ) {
|
|
1569
|
-
body += '<tr><td><span class="tooltip">MLB Network<span class="tooltiptext">MLB Network live stream is now available in the USA for paid MLBTV subscribers or as a paid add-on, in addition to authenticated TV subscribers. <a href="https://
|
|
1569
|
+
body += '<tr><td><span class="tooltip">MLB Network<span class="tooltiptext">MLB Network live stream is now available in the USA for paid MLBTV subscribers or as a paid add-on, in addition to authenticated TV subscribers. <a href="https://support.mlb.com/s/article/MLB-Network-Streaming-FAQ">See here for more information</a>.</span></span></td><td>'
|
|
1570
1570
|
let querystring = '?event=MLBN'
|
|
1571
1571
|
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1572
1572
|
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
@@ -1589,6 +1589,58 @@ app.get('/', async function(req, res) {
|
|
|
1589
1589
|
session.debuglog('MLB Network detect error : ' + e.message)
|
|
1590
1590
|
}
|
|
1591
1591
|
|
|
1592
|
+
// SNLA live stream for entitled subscribers
|
|
1593
|
+
try {
|
|
1594
|
+
if ( entitlements.includes('SNLA_119') ) {
|
|
1595
|
+
body += '<tr><td><span class="tooltip">SportsNet LA<span class="tooltiptext">SNLA 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></td><td>'
|
|
1596
|
+
let querystring = '?event=SNLA'
|
|
1597
|
+
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1598
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
1599
|
+
if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
|
|
1600
|
+
if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
|
|
1601
|
+
}
|
|
1602
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1603
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1604
|
+
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1605
|
+
} else if ( linkType == VALID_LINK_TYPES[4] ) {
|
|
1606
|
+
querystring += '&filename=' + gameDate + ' SNLA'
|
|
1607
|
+
}
|
|
1608
|
+
querystring += content_protect_b
|
|
1609
|
+
multiviewquerystring += content_protect_b
|
|
1610
|
+
body += '<a href="' + thislink + querystring + '">SNLA</a>'
|
|
1611
|
+
body += '<input type="checkbox" value="http://127.0.0.1:' + session.data.port + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1612
|
+
body += '</td></tr>' + "\n"
|
|
1613
|
+
} // end entitlements check
|
|
1614
|
+
} catch (e) {
|
|
1615
|
+
session.debuglog('SNLA detect error : ' + e.message)
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// SNY live stream for entitled subscribers
|
|
1619
|
+
try {
|
|
1620
|
+
if ( entitlements.includes('SNY_121') ) {
|
|
1621
|
+
body += '<tr><td><span class="tooltip">SNY<span class="tooltiptext">SNY live stream for entitled subscribers. <a href="https://support.mlb.com/s/article/SNY-In-Market-Offering">See here for more information</a>.</span></span></td><td>'
|
|
1622
|
+
let querystring = '?event=SNY'
|
|
1623
|
+
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1624
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
1625
|
+
if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
|
|
1626
|
+
if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
|
|
1627
|
+
}
|
|
1628
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1629
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1630
|
+
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1631
|
+
} else if ( linkType == VALID_LINK_TYPES[4] ) {
|
|
1632
|
+
querystring += '&filename=' + gameDate + ' SNY'
|
|
1633
|
+
}
|
|
1634
|
+
querystring += content_protect_b
|
|
1635
|
+
multiviewquerystring += content_protect_b
|
|
1636
|
+
body += '<a href="' + thislink + querystring + '">SNY</a>'
|
|
1637
|
+
body += '<input type="checkbox" value="http://127.0.0.1:' + session.data.port + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1638
|
+
body += '</td></tr>' + "\n"
|
|
1639
|
+
} // end entitlements check
|
|
1640
|
+
} catch (e) {
|
|
1641
|
+
session.debuglog('SNY detect error : ' + e.message)
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1592
1644
|
if ( (mediaType == 'MLBTV') && ((level_ids == levels['MLB']) || level_ids.startsWith(levels['MLB'] + ',')) ) {
|
|
1593
1645
|
// Recap Rundown beginning in 2023, disabled because it stopped working
|
|
1594
1646
|
/*if ( (gameDate <= yesterday) && (gameDate >= '2023-03-31') && cache_data.dates && cache_data.dates[0] && cache_data.dates[0].games && (cache_data.dates[0].games.length > 0) ) {
|
|
@@ -1618,12 +1670,13 @@ app.get('/', async function(req, res) {
|
|
|
1618
1670
|
//big_inning = await session.generateBigInningSchedule(gameDate)
|
|
1619
1671
|
}
|
|
1620
1672
|
if ( big_inning && big_inning.start ) {
|
|
1621
|
-
body += '<tr><td><span class="tooltip">' + new Date(big_inning.start).toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + ' - ' + new Date(big_inning.end).toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + '<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://
|
|
1673
|
+
body += '<tr><td><span class="tooltip">' + new Date(big_inning.start).toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + ' - ' + new Date(big_inning.end).toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + '<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>'
|
|
1622
1674
|
let compareStart = new Date(big_inning.start)
|
|
1623
1675
|
compareStart.setMinutes(compareStart.getMinutes()-10)
|
|
1624
1676
|
let compareEnd = new Date(big_inning.end)
|
|
1625
1677
|
compareEnd.setHours(compareEnd.getHours()+1)
|
|
1626
1678
|
if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
|
|
1679
|
+
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>'
|
|
1627
1680
|
let querystring = '?event=biginning'
|
|
1628
1681
|
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1629
1682
|
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
@@ -1659,15 +1712,15 @@ app.get('/', async function(req, res) {
|
|
|
1659
1712
|
compareEnd.setHours(compareEnd.getHours()+4)
|
|
1660
1713
|
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>'
|
|
1661
1714
|
if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
|
|
1662
|
-
let streamURL = server + '/gamechanger.m3u8'
|
|
1663
|
-
let multiviewquerystring = streamURL + '
|
|
1715
|
+
let streamURL = server + '/gamechanger.m3u8?'
|
|
1716
|
+
let multiviewquerystring = streamURL + 'resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + content_protect_b
|
|
1664
1717
|
streamURL += content_protect_a
|
|
1665
|
-
if ( resolution != VALID_RESOLUTIONS[0] ) streamURL += '
|
|
1718
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) streamURL += 'resolution=' + resolution + '&'
|
|
1666
1719
|
if ( linkType != VALID_LINK_TYPES[1] ) {
|
|
1667
|
-
streamURL = thislink + '?src=' + encodeURIComponent(streamURL) + '
|
|
1720
|
+
streamURL = thislink + '?src=' + encodeURIComponent(streamURL) + 'startFrom=' + VALID_START_FROM[1] + content_protect_b + '&'
|
|
1668
1721
|
}
|
|
1669
1722
|
if ( linkType == VALID_LINK_TYPES[4] ) {
|
|
1670
|
-
streamURL += '
|
|
1723
|
+
streamURL += 'filename=' + gameDate + ' Game Changer' + '&'
|
|
1671
1724
|
}
|
|
1672
1725
|
body += '<a href="' + streamURL + '">Game Changer</a>'
|
|
1673
1726
|
body += '<input type="checkbox" value="http://127.0.0.1:' + session.data.port + multiviewquerystring + '" onclick="addmultiview(this, [], excludeTeams)">'
|
|
@@ -2196,7 +2249,7 @@ app.get('/', async function(req, res) {
|
|
|
2196
2249
|
resolution = 'best'
|
|
2197
2250
|
}
|
|
2198
2251
|
|
|
2199
|
-
body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all live MLB broadcasts (
|
|
2252
|
+
body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all entitled live MLB broadcasts (games plus Big Inning, Game Changer, and Multiview, as well as MLB Network, SNLA, and/or SNY as appropriate). If favorite team(s) have been provided, it will also include affiliate games for those organizations. Channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + content_protect_b + '">channels.m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + content_protect_b + '">guide.xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2200
2253
|
|
|
2201
2254
|
let include_teams = 'ath,national'
|
|
2202
2255
|
if ( session.credentials.fav_teams.length > 0 ) {
|
|
@@ -2211,7 +2264,17 @@ app.get('/', async function(req, res) {
|
|
|
2211
2264
|
|
|
2212
2265
|
body += '<p><span class="tooltip">Include (or exclude) LIDOM<span class="tooltiptext">Dominican Winter League, aka Liga de Beisbol Dominicano. Live stream only, does not support starting from the beginning or certain innings, skip options, etc.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=lidom' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2213
2266
|
|
|
2214
|
-
|
|
2267
|
+
if ( entitlements.includes('MLBN') || entitlements.includes('EXECMLB') || entitlements.includes('MLBTVMLBNADOBEPASS') ) {
|
|
2268
|
+
body += '<p><span class="tooltip">Include (or exclude) MLB Network<span class="tooltiptext">MLB Network live stream is now available in the USA for paid MLBTV subscribers or as a paid add-on, in addition to authenticated TV subscribers. <a href="https://support.mlb.com/s/article/MLB-Network-Streaming-FAQ">See here for more information</a>.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=mlbn' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=mlbn' + content_protect_b + '">xml</a></p>' + "\n"
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
if ( entitlements.includes('SNLA_119') ) {
|
|
2272
|
+
body += '<p><span class="tooltip">Include (or exclude) SportsNet LA<span class="tooltiptext">SNLA 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=snla' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=snla' + content_protect_b + '">xml</a></p>' + "\n"
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
if ( entitlements.includes('SNY_121') ) {
|
|
2276
|
+
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"
|
|
2277
|
+
}
|
|
2215
2278
|
|
|
2216
2279
|
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"
|
|
2217
2280
|
|
package/package.json
CHANGED
package/session.js
CHANGED
|
@@ -38,12 +38,16 @@ const WINTER_LEAGUES = [AFL_ID, LIDOM_ID]
|
|
|
38
38
|
const BREAK_TYPES = ['Game Advisory', 'Pitching Substitution', 'Offensive Substitution', 'Defensive Sub', 'Defensive Switch', 'Runner Placed On Base']
|
|
39
39
|
// These are the events to keep, in addition to the last event of each at-bat, if we're skipping pitches
|
|
40
40
|
const ACTION_TYPES = ['Wild Pitch', 'Passed Ball', 'Stolen Base', 'Caught Stealing', 'Pickoff', 'Error', 'Out', 'Balk', 'Defensive Indiff', 'Other Advance']
|
|
41
|
+
// These are some idle events to skip
|
|
42
|
+
const IDLE_TYPES = ['Mound Visit', 'Batter Timeout', 'Pitcher Step Off', 'challenge']
|
|
41
43
|
const EVENT_START_PADDING = -3
|
|
42
|
-
const PITCH_END_PADDING =
|
|
44
|
+
const PITCH_END_PADDING = 2
|
|
43
45
|
const ACTION_END_PADDING = 7
|
|
44
46
|
const MINIMUM_BREAK_DURATION = 5
|
|
45
47
|
// extra padding for MLB events (2025)
|
|
46
48
|
const MLB_PADDING = 39
|
|
49
|
+
// extra Game Changer padding for MLB (2025)
|
|
50
|
+
const MLB_GAMECHANGER_PADDING = 20
|
|
47
51
|
|
|
48
52
|
const LI_TABLE = {
|
|
49
53
|
1: {
|
|
@@ -1269,7 +1273,7 @@ class sessionClass {
|
|
|
1269
1273
|
this.save_cache_data()
|
|
1270
1274
|
}
|
|
1271
1275
|
|
|
1272
|
-
cacheStreamURL(mediaId, streamURL, streamURLToken='', streamURLExpiration='') {
|
|
1276
|
+
cacheStreamURL(mediaId, streamURL, streamURLToken='', streamURLExpiration='', rawStreamURL='') {
|
|
1273
1277
|
this.createMediaCache(mediaId)
|
|
1274
1278
|
this.cache.media[mediaId].streamURL = streamURL
|
|
1275
1279
|
if (streamURLToken != '') {
|
|
@@ -1282,6 +1286,7 @@ class sessionClass {
|
|
|
1282
1286
|
} else {
|
|
1283
1287
|
this.cache.media[mediaId].streamURLExpiry = new Date(streamURLExpiration)
|
|
1284
1288
|
}
|
|
1289
|
+
this.cache.media[mediaId].rawStreamURL = rawStreamURL
|
|
1285
1290
|
this.save_cache_data()
|
|
1286
1291
|
}
|
|
1287
1292
|
|
|
@@ -1572,6 +1577,9 @@ class sessionClass {
|
|
|
1572
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()) ) {
|
|
1573
1578
|
this.debuglog('using cached streamURL and token')
|
|
1574
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
|
+
}
|
|
1575
1583
|
return streamInfo
|
|
1576
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()) ) {
|
|
1577
1585
|
this.log('mediaId recently blacked out, skipping')
|
|
@@ -1611,17 +1619,17 @@ class sessionClass {
|
|
|
1611
1619
|
var response = await this.httpPost(reqObj)
|
|
1612
1620
|
if ( response ) {
|
|
1613
1621
|
this.debuglog('getStreamURL response : ' + JSON.stringify(response))
|
|
1614
|
-
if ( response.data && response.data.initPlaybackSession && response.data.initPlaybackSession.playback && response.data.initPlaybackSession.playback.url
|
|
1622
|
+
if ( response.data && response.data.initPlaybackSession && response.data.initPlaybackSession.playback && response.data.initPlaybackSession.playback.url ) {
|
|
1615
1623
|
let rawStreamURL = response.data.initPlaybackSession.playback.url
|
|
1616
1624
|
this.debuglog('getStreamURL rawStreamURL : ' + rawStreamURL)
|
|
1617
1625
|
let streamURL = rawStreamURL.replace(/[\/]([A-Za-z0-9_]+)[\/]/, '/')
|
|
1618
|
-
|
|
1619
|
-
|
|
1626
|
+
let streamURLToken = response.data.initPlaybackSession.playback.token
|
|
1627
|
+
let streamURLExpiration = response.data.initPlaybackSession.playback.expiration
|
|
1620
1628
|
this.debuglog('getStreamURL streamURL : ' + streamURL)
|
|
1621
1629
|
this.debuglog('getStreamURL token : ' + streamURLToken)
|
|
1622
1630
|
this.debuglog('getStreamURL expiration : ' + streamURLExpiration)
|
|
1623
|
-
this.cacheStreamURL(mediaId, streamURL, streamURLToken, streamURLExpiration)
|
|
1624
|
-
let streamInfo = {streamURL: streamURL, streamURLToken: streamURLToken}
|
|
1631
|
+
this.cacheStreamURL(mediaId, streamURL, streamURLToken, streamURLExpiration, rawStreamURL)
|
|
1632
|
+
let streamInfo = {streamURL: streamURL, streamURLToken: streamURLToken, rawStreamURL: rawStreamURL}
|
|
1625
1633
|
return streamInfo
|
|
1626
1634
|
} else {
|
|
1627
1635
|
this.log('getStreamURL streamURL not found')
|
|
@@ -2588,9 +2596,10 @@ class sessionClass {
|
|
|
2588
2596
|
channels = this.sortObj(channels)
|
|
2589
2597
|
channels = Object.assign(channels, nationalChannels)
|
|
2590
2598
|
|
|
2599
|
+
|
|
2600
|
+
let entitlements = await this.getEntitlements()
|
|
2591
2601
|
// MLB Network live stream for eligible USA subscribers
|
|
2592
2602
|
try {
|
|
2593
|
-
let entitlements = await this.getEntitlements()
|
|
2594
2603
|
if ( (entitlements.includes('MLBN') || entitlements.includes('EXECMLB') || entitlements.includes('MLBTVMLBNADOBEPASS')) ) {
|
|
2595
2604
|
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
2596
2605
|
if ( (excludeTeams.length > 0) && excludeTeams.includes('MLBN') ) {
|
|
@@ -2620,6 +2629,70 @@ class sessionClass {
|
|
|
2620
2629
|
} catch (e) {
|
|
2621
2630
|
this.debuglog('getTVData MLB Network detect error : ' + e.message)
|
|
2622
2631
|
}
|
|
2632
|
+
|
|
2633
|
+
// SNLA live stream for entitled subscribers
|
|
2634
|
+
try {
|
|
2635
|
+
if ( (entitlements.includes('SNLA_119')) ) {
|
|
2636
|
+
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
2637
|
+
if ( (excludeTeams.length > 0) && excludeTeams.includes('SNLA') ) {
|
|
2638
|
+
// do nothing
|
|
2639
|
+
} else if ( (includeTeams.length == 0) || includeTeams.includes('SNLA') ) {
|
|
2640
|
+
this.debuglog('getTVData processing SNLA')
|
|
2641
|
+
let logo = 'https://img.mlbstatic.com/mlb-images/image/upload/t_w640/mlb/fnwk2k0kgn1j8r8vvx3d.png'
|
|
2642
|
+
let channelid = mediaType + '.SNLA'
|
|
2643
|
+
//if ( this.protection.content_protect ) logo += '&content_protect=' + this.protection.content_protect
|
|
2644
|
+
let stream = server + '/stream.m3u8?event=snla&mediaType=Video&resolution=' + resolution
|
|
2645
|
+
if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
|
|
2646
|
+
if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
|
|
2647
|
+
channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
|
|
2648
|
+
|
|
2649
|
+
let title = 'SportsNet LA'
|
|
2650
|
+
let description = 'Live stream of SNLA'
|
|
2651
|
+
|
|
2652
|
+
let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
|
|
2653
|
+
let stop = this.convertDateToXMLTV(new Date(cache_data.dates[cache_data.dates.length-1].date + ' 00:00:00'))
|
|
2654
|
+
|
|
2655
|
+
// SNLA guide XML
|
|
2656
|
+
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))
|
|
2657
|
+
this.debuglog('getTVData completed SNLA')
|
|
2658
|
+
} // end includeTeams check
|
|
2659
|
+
} // end mediaType check
|
|
2660
|
+
} // end entitlements check
|
|
2661
|
+
} catch (e) {
|
|
2662
|
+
this.debuglog('getTVData SNLA detect error : ' + e.message)
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
// SNY live stream for entitled subscribers
|
|
2666
|
+
try {
|
|
2667
|
+
if ( (entitlements.includes('SNY_121')) ) {
|
|
2668
|
+
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
2669
|
+
if ( (excludeTeams.length > 0) && excludeTeams.includes('SNY') ) {
|
|
2670
|
+
// do nothing
|
|
2671
|
+
} else if ( (includeTeams.length == 0) || includeTeams.includes('SNY') ) {
|
|
2672
|
+
this.debuglog('getTVData processing SNY')
|
|
2673
|
+
let logo = 'https://img.mlbstatic.com/mlb-images/image/upload/t_w640/mlb/le5jifzo6oylxtnuf0m1.png'
|
|
2674
|
+
let channelid = mediaType + '.SNY'
|
|
2675
|
+
//if ( this.protection.content_protect ) logo += '&content_protect=' + this.protection.content_protect
|
|
2676
|
+
let stream = server + '/stream.m3u8?event=sny&mediaType=Video&resolution=' + resolution
|
|
2677
|
+
if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
|
|
2678
|
+
if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
|
|
2679
|
+
channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
|
|
2680
|
+
|
|
2681
|
+
let title = 'SNY'
|
|
2682
|
+
let description = 'Live stream of SNY'
|
|
2683
|
+
|
|
2684
|
+
let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
|
|
2685
|
+
let stop = this.convertDateToXMLTV(new Date(cache_data.dates[cache_data.dates.length-1].date + ' 00:00:00'))
|
|
2686
|
+
|
|
2687
|
+
// SNLA guide XML
|
|
2688
|
+
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))
|
|
2689
|
+
this.debuglog('getTVData completed SNY')
|
|
2690
|
+
} // end includeTeams check
|
|
2691
|
+
} // end mediaType check
|
|
2692
|
+
} // end entitlements check
|
|
2693
|
+
} catch (e) {
|
|
2694
|
+
this.debuglog('getTVData SNY detect error : ' + e.message)
|
|
2695
|
+
}
|
|
2623
2696
|
|
|
2624
2697
|
// Big Inning
|
|
2625
2698
|
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
@@ -2638,6 +2711,7 @@ class sessionClass {
|
|
|
2638
2711
|
let title = 'MLB Big Inning'
|
|
2639
2712
|
let description = 'Live look-ins and big moments from around the league'
|
|
2640
2713
|
|
|
2714
|
+
// disabled Big Inning schedule scraping March 2025
|
|
2641
2715
|
for (var i = 0; i < cache_data.dates.length; i++) {
|
|
2642
2716
|
// Scraped Big Inning schedule
|
|
2643
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') ) {
|
|
@@ -2655,9 +2729,9 @@ class sessionClass {
|
|
|
2655
2729
|
let stop = this.convertDateToXMLTV(new Date(this.cache.bigInningSchedule[gameDate].end))
|
|
2656
2730
|
|
|
2657
2731
|
// Generated Big Inning schedule (disabled)
|
|
2658
|
-
|
|
2659
|
-
let start = this.convertDateToXMLTV(new Date(big_inning.start))
|
|
2660
|
-
let stop = this.convertDateToXMLTV(new Date(big_inning.end))
|
|
2732
|
+
//let big_inning = await this.generateBigInningSchedule(gameDate)
|
|
2733
|
+
//let start = this.convertDateToXMLTV(new Date(big_inning.start))
|
|
2734
|
+
//let stop = this.convertDateToXMLTV(new Date(big_inning.end))
|
|
2661
2735
|
|
|
2662
2736
|
// Big Inning calendar ICS
|
|
2663
2737
|
let prefix = 'Watch'
|
|
@@ -2670,6 +2744,12 @@ class sessionClass {
|
|
|
2670
2744
|
}
|
|
2671
2745
|
this.debuglog('getTVData completed Big Inning for date ' + cache_data.dates[i].date)
|
|
2672
2746
|
}
|
|
2747
|
+
|
|
2748
|
+
// generic Big Inning guide XML
|
|
2749
|
+
/*let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
|
|
2750
|
+
let stop = this.convertDateToXMLTV(new Date(cache_data.dates[cache_data.dates.length-1].date + ' 00:00:00'))
|
|
2751
|
+
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))*/
|
|
2752
|
+
|
|
2673
2753
|
this.debuglog('getTVData completed Big Inning')
|
|
2674
2754
|
}
|
|
2675
2755
|
}
|
|
@@ -3024,8 +3104,11 @@ class sessionClass {
|
|
|
3024
3104
|
event_end_padding = pitch_end_padding
|
|
3025
3105
|
}
|
|
3026
3106
|
let action_index
|
|
3027
|
-
// skip type 1 (breaks)
|
|
3028
|
-
if ((skip_type
|
|
3107
|
+
// skip type 1 (breaks) will look at all plays with an endTime
|
|
3108
|
+
if ((skip_type == 1) && cache_data.liveData.plays.allPlays[i].playEvents[j].endTime) {
|
|
3109
|
+
action_index = j
|
|
3110
|
+
// skip type 2 (idle time) will look at all non-idle plays with an endTime
|
|
3111
|
+
} else if ((skip_type == 2) && cache_data.liveData.plays.allPlays[i].playEvents[j].endTime && (!cache_data.liveData.plays.allPlays[i].playEvents[j].details || !cache_data.liveData.plays.allPlays[i].playEvents[j].details.description || !IDLE_TYPES.some(v => cache_data.liveData.plays.allPlays[i].playEvents[j].details.description.includes(v)))) {
|
|
3029
3112
|
action_index = j
|
|
3030
3113
|
} else if (skip_type == 3) {
|
|
3031
3114
|
// skip type 3 excludes non-action pitches (events that aren't last in the at-bat and don't fall under action types)
|
|
@@ -3116,17 +3199,15 @@ class sessionClass {
|
|
|
3116
3199
|
this.debuglog('getBigInningSchedule')
|
|
3117
3200
|
|
|
3118
3201
|
// temporarily disable Big Inning schedule checking until a new source URL is available
|
|
3119
|
-
this.cache.bigInningSchedule = {}
|
|
3120
|
-
return
|
|
3202
|
+
/*this.cache.bigInningSchedule = {}
|
|
3203
|
+
return*/
|
|
3121
3204
|
|
|
3122
3205
|
let currentDate = new Date()
|
|
3123
3206
|
if ( !this.cache || !this.cache.bigInningScheduleCacheExpiry || (currentDate > new Date(this.cache.bigInningScheduleCacheExpiry)) ) {
|
|
3124
3207
|
if ( !this.cache.bigInningSchedule ) this.cache.bigInningSchedule = {}
|
|
3125
3208
|
let reqObj = {
|
|
3126
|
-
|
|
3127
|
-
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',
|
|
3128
3210
|
headers: {
|
|
3129
|
-
'authority': 'www.mlb.com',
|
|
3130
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',
|
|
3131
3212
|
'accept-language': 'en-US,en;q=0.9',
|
|
3132
3213
|
'cache-control': 'no-cache',
|
|
@@ -3149,89 +3230,15 @@ class sessionClass {
|
|
|
3149
3230
|
if ( response ) {
|
|
3150
3231
|
// disabled because it's very big!
|
|
3151
3232
|
//this.debuglog(response)
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
let
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
let year
|
|
3162
|
-
let month
|
|
3163
|
-
let day
|
|
3164
|
-
let this_datestring
|
|
3165
|
-
let add_date = 0
|
|
3166
|
-
let d
|
|
3167
|
-
|
|
3168
|
-
for (var j=1; j<cols.length; j++) {
|
|
3169
|
-
// split on closing bracket to get column text at resulting array index 0
|
|
3170
|
-
let col = cols[j].split('<')
|
|
3171
|
-
switch(j){
|
|
3172
|
-
// first column is date
|
|
3173
|
-
case 1:
|
|
3174
|
-
// split date into array
|
|
3175
|
-
// old date format (January 1, 1970) (disabled)
|
|
3176
|
-
/*parts = col[0].split(' ')
|
|
3177
|
-
year = parts[2]
|
|
3178
|
-
// get month index, zero-based
|
|
3179
|
-
month = new Date(Date.parse(parts[0] +" 1, 2021")).getMonth()
|
|
3180
|
-
day = parts[1].substring(0,parts[1].length-3)*/
|
|
3181
|
-
// new date format (01/01/70)
|
|
3182
|
-
parts = col[0].split('/')
|
|
3183
|
-
year = parts[2]
|
|
3184
|
-
if ( year.length == 2 ) {
|
|
3185
|
-
year = '20' + parts[2]
|
|
3186
|
-
}
|
|
3187
|
-
// get month index, zero-based
|
|
3188
|
-
month = parseInt(parts[0]) - 1
|
|
3189
|
-
day = parts[1]
|
|
3190
|
-
this_datestring = new Date(year, month, day).toISOString().substring(0,10)
|
|
3191
|
-
this.cache.bigInningSchedule[this_datestring] = {}
|
|
3192
|
-
// increment month index (not zero-based)
|
|
3193
|
-
month += 1
|
|
3194
|
-
break
|
|
3195
|
-
// remaining columns are times
|
|
3196
|
-
default:
|
|
3197
|
-
let hour
|
|
3198
|
-
let minute = '00'
|
|
3199
|
-
let ampm
|
|
3200
|
-
// if time has colon, split into array on that to get hour and minute parts
|
|
3201
|
-
if ( col[0].indexOf(':') > 0 ) {
|
|
3202
|
-
parts = col[0].split(':')
|
|
3203
|
-
hour = parseInt(parts[0])
|
|
3204
|
-
minute = parts[1].substring(0,2)
|
|
3205
|
-
} else {
|
|
3206
|
-
hour = parseInt(col[0].substring(0,col[0].length-2))
|
|
3207
|
-
}
|
|
3208
|
-
ampm = col[0].substring(col[0].length-2,col[0].length)
|
|
3209
|
-
// convert hour to 24-hour format
|
|
3210
|
-
if ( (ampm == 'PM') || ((hour == 12) && (ampm == 'AM')) ) {
|
|
3211
|
-
hour += 12
|
|
3212
|
-
}
|
|
3213
|
-
// these times are EDT so add 4 for UTC
|
|
3214
|
-
hour += 4
|
|
3215
|
-
// if hour is beyond 23, note we will have to add 1 day
|
|
3216
|
-
if ( hour > 23 ) {
|
|
3217
|
-
add_date = 1
|
|
3218
|
-
hour -= 24
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
|
-
d = new Date(this_datestring + 'T' + hour.toString().padStart(2, '0') + ':' + minute.toString().padStart(2, '0') + ':00.000+00:00')
|
|
3222
|
-
d.setDate(d.getDate()+add_date)
|
|
3223
|
-
switch(j){
|
|
3224
|
-
// 2nd column is start time
|
|
3225
|
-
case 2:
|
|
3226
|
-
this.cache.bigInningSchedule[this_datestring].start = d
|
|
3227
|
-
break
|
|
3228
|
-
// 3rd column is end time
|
|
3229
|
-
case 3:
|
|
3230
|
-
this.cache.bigInningSchedule[this_datestring].end = d
|
|
3231
|
-
break
|
|
3232
|
-
}
|
|
3233
|
-
break
|
|
3234
|
-
}
|
|
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
|
|
3235
3242
|
}
|
|
3236
3243
|
}
|
|
3237
3244
|
this.debuglog(JSON.stringify(this.cache.bigInningSchedule))
|
|
@@ -3380,6 +3387,60 @@ class sessionClass {
|
|
|
3380
3387
|
}
|
|
3381
3388
|
}
|
|
3382
3389
|
|
|
3390
|
+
// Get linear channel stream URL
|
|
3391
|
+
async getLinearStreamURL(network) {
|
|
3392
|
+
try {
|
|
3393
|
+
this.debuglog('getLinearStreamURL')
|
|
3394
|
+
|
|
3395
|
+
let reqObj = {
|
|
3396
|
+
url: GRAPHQL_URL,
|
|
3397
|
+
simple: false,
|
|
3398
|
+
headers: {
|
|
3399
|
+
'accept': 'application/json, text/plain, */*',
|
|
3400
|
+
'accept-encoding': 'gzip, deflate, br',
|
|
3401
|
+
'accept-language': 'en-US,en;q=0.5',
|
|
3402
|
+
'authorization': 'Bearer ' + await this.getLoginToken() || this.halt('missing loginToken'),
|
|
3403
|
+
'connection': 'keep-alive',
|
|
3404
|
+
'content-type': 'application/json',
|
|
3405
|
+
'x-client-name': 'WEB',
|
|
3406
|
+
'x-client-version': '7.8.1',
|
|
3407
|
+
'origin': 'https://www.mlb.com',
|
|
3408
|
+
'referer': 'https://www.mlb.com/',
|
|
3409
|
+
'user-agent': USER_AGENT
|
|
3410
|
+
},
|
|
3411
|
+
body: {
|
|
3412
|
+
'operationName': 'contentCollections',
|
|
3413
|
+
'query': 'query contentCollections(\n $categories: [ContentGroupCategory!]\n $includeRestricted: Boolean = false\n $includeSpoilers: Boolean = false\n $limit: Int = 10,\n $skip: Int = 0\n ) {\n contentCollections(\n categories: $categories\n includeRestricted: $includeRestricted\n includeSpoilers: $includeSpoilers\n limit: $limit\n skip: $skip\n ) {\n title\n category\n contents {\n assetTrackingKey\n contentDate\n contentId\n contentRestrictions\n description\n duration\n language\n mediaId\n officialDate\n title\n mediaState {\n state\n mediaType\n }\n thumbnails {\n thumbnailType\n templateUrl\n thumbnailUrl\n }\n }\n }\n }',
|
|
3414
|
+
'variables': {
|
|
3415
|
+
'categories': network,
|
|
3416
|
+
'limit': '25'
|
|
3417
|
+
}
|
|
3418
|
+
},
|
|
3419
|
+
json: true,
|
|
3420
|
+
gzip: true
|
|
3421
|
+
}
|
|
3422
|
+
var response = await this.httpPost(reqObj)
|
|
3423
|
+
if ( response ) {
|
|
3424
|
+
this.debuglog('getLinearStreamURL response : ' + JSON.stringify(response))
|
|
3425
|
+
if ( response.data && response.data.contentCollections && (response.data.contentCollections.length > 0) && response.data.contentCollections[0].contents ) {
|
|
3426
|
+
for (var i=0; i<response.data.contentCollections[0].contents.length; i++) {
|
|
3427
|
+
try {
|
|
3428
|
+
let streamInfo = await this.getStreamURL(response.data.contentCollections[0].contents[i].mediaId)
|
|
3429
|
+
if ( streamInfo.rawStreamURL ) {
|
|
3430
|
+
return streamInfo.rawStreamURL
|
|
3431
|
+
}
|
|
3432
|
+
} catch(e) {
|
|
3433
|
+
this.debuglog('getLinearStreamURL getStreamURL error : ' + e.message)
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
this.log('getLinearStreamURL stream url not found')
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
} catch(e) {
|
|
3440
|
+
this.log('getLinearStreamURL error : ' + e.message)
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3383
3444
|
// Get Recap Rundown data
|
|
3384
3445
|
async getRecapRundownData(dateString) {
|
|
3385
3446
|
try {
|
|
@@ -3473,6 +3534,12 @@ class sessionClass {
|
|
|
3473
3534
|
playbackURL = await this.getRecapRundownURL(dateString)
|
|
3474
3535
|
} else if ( eventName.toUpperCase() == 'MLBN' ) {
|
|
3475
3536
|
playbackURL = 'https://falcon.mlbinfra.com/api/v1/linear/mlbn'
|
|
3537
|
+
} else if ( eventName.toUpperCase() == 'SNLA' ) {
|
|
3538
|
+
playbackURL = await this.getLinearStreamURL('SNLA_LIVE')
|
|
3539
|
+
return playbackURL
|
|
3540
|
+
} else if ( eventName.toUpperCase() == 'SNY' ) {
|
|
3541
|
+
playbackURL = await this.getLinearStreamURL('SNY_LIVE')
|
|
3542
|
+
return playbackURL
|
|
3476
3543
|
} else {
|
|
3477
3544
|
playbackURL = await this.getEventURL(eventName)
|
|
3478
3545
|
}
|
|
@@ -4014,7 +4081,7 @@ class sessionClass {
|
|
|
4014
4081
|
|
|
4015
4082
|
if ( !this.temp_cache.gamechanger[id].games ) this.temp_cache.gamechanger[id].games = []
|
|
4016
4083
|
this.temp_cache.gamechanger[id].games.push(best_games)
|
|
4017
|
-
let maxlength = (this.gamechanger_delay + 10) / 10
|
|
4084
|
+
let maxlength = (this.gamechanger_delay + 10 + MLB_GAMECHANGER_PADDING) / 10
|
|
4018
4085
|
while ( this.temp_cache.gamechanger[id].games.length > maxlength ) {
|
|
4019
4086
|
this.temp_cache.gamechanger[id].games.shift()
|
|
4020
4087
|
}
|