mlbserver 2023.5.25 → 2023.8.27

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 (4) hide show
  1. package/README.md +1 -1
  2. package/index.js +85 -38
  3. package/package.json +1 -1
  4. package/session.js +296 -134
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mlbserver
2
2
 
3
- Current version 2023.05.25
3
+ Current version 2023.08.27
4
4
 
5
5
  Credit to https://github.com/tonycpsu/streamglob and https://github.com/mafintosh/hls-decryptor
6
6
 
package/index.js CHANGED
@@ -46,8 +46,6 @@ const SAMPLE_STREAM_URL = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
46
46
 
47
47
  const SECONDS_PER_SEGMENT = 5
48
48
 
49
- const AFFILIATE_TEAM_IDS = { 'ARI': '419,516,2310,5368', 'ATL': '430,432,478,431', 'BAL': '418,568,548,488', 'BOS': '428,414,533,546', 'CHC': '521,451,550,553', 'CIN': '450,459,498,416', 'CLE': '445,402,437,481', 'COL': '259,486,342,538', 'CWS': '247,580,487,494', 'DET': '512,570,582,106', 'HOU': '482,5434,573,3712', 'KC': '1350,3705,541,565', 'LAA': '401,559,460,561', 'LAD': '260,238,456,526', 'MIA': '479,564,554,4124', 'MIL': '249,572,556,5015', 'MIN': '492,509,1960,3898', 'NYM': '453,507,552,505', 'NYY': '1956,587,531,537', 'OAK': '237,499,400,524', 'PHI': '427,522,1410,566', 'PIT': '3390,484,452,477', 'SD': '103,510,584,4904', 'SEA': '403,515,529,574', 'SF': '105,461,476,3410', 'STL': '279,235,440,443', 'TB': '2498,233,234,421', 'TEX': '102,485,540,448', 'TOR': '424,435,463,422', 'WSH': '436,534,547,426' }
50
-
51
49
  // for favorites: text, then background, based on https://teamcolors.jim-nielsen.com/
52
50
  const TEAM_COLORS = {'ARI': ['E3D4AD', 'A71930'], 'ATL': ['13274F', 'CE1141'], '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']}
53
51
 
@@ -315,13 +313,19 @@ app.get('/stream.m3u8', async function(req, res) {
315
313
  }
316
314
  } else if ( req.query.team ) {
317
315
  let mediaType = req.query.mediaType || VALID_MEDIA_TYPES[0]
318
- let mediaInfo = await session.getMediaId(decodeURIComponent(req.query.team), mediaType, req.query.date, req.query.game, includeBlackouts)
316
+ let level = req.query.level || 'MLB'
317
+ let mediaInfo = await session.getMediaId(decodeURIComponent(req.query.team), decodeURIComponent(level), mediaType, req.query.date, req.query.game, includeBlackouts)
319
318
  if ( mediaInfo ) {
320
- mediaId = mediaInfo.mediaId
321
- contentId = mediaInfo.contentId
322
- if ( mediaInfo.alternateAudioTracks ) {
323
- for (const [key, value] of Object.entries(mediaInfo.alternateAudioTracks)) {
324
- options.alternate_audio_tracks[key] = value
319
+
320
+ if ( mediaInfo.gamePk ) {
321
+ streamURL = await session.getEventStreamURL(false, mediaInfo.gamePk)
322
+ } else {
323
+ mediaId = mediaInfo.mediaId
324
+ contentId = mediaInfo.contentId
325
+ if ( mediaInfo.alternateAudioTracks ) {
326
+ for (const [key, value] of Object.entries(mediaInfo.alternateAudioTracks)) {
327
+ options.alternate_audio_tracks[key] = value
328
+ }
325
329
  }
326
330
  }
327
331
  } else {
@@ -329,13 +333,15 @@ app.get('/stream.m3u8', async function(req, res) {
329
333
  }
330
334
  }
331
335
 
332
- if ( !mediaId ) {
333
- session.log('failed to get mediaId : ' + req.url)
334
- res.end('')
335
- return
336
- } else {
337
- session.debuglog('mediaId : ' + mediaId)
338
- streamURL = await session.getStreamURL(mediaId)
336
+ if ( !streamURL ) {
337
+ if ( !mediaId ) {
338
+ session.log('failed to get mediaId : ' + req.url)
339
+ res.end('')
340
+ return
341
+ } else {
342
+ session.debuglog('mediaId : ' + mediaId)
343
+ streamURL = await session.getStreamURL(mediaId)
344
+ }
339
345
  }
340
346
  }
341
347
  }
@@ -1264,25 +1270,23 @@ app.get('/', async function(req, res) {
1264
1270
  var team_ids = ''
1265
1271
  if ( req.query.org ) {
1266
1272
  org = decodeURIComponent(req.query.org)
1267
- if ( typeof AFFILIATE_TEAM_IDS[org] === 'undefined' ) {
1273
+ if ( typeof session.getAffiliateTeamIds(org) === 'undefined' ) {
1268
1274
  org = default_org
1269
1275
  } else {
1270
- team_ids += session.getTeamIds(org) + ',' + AFFILIATE_TEAM_IDS[org]
1276
+ team_ids += session.getTeamIds(org) + ',' + session.getAffiliateTeamIds(org)
1271
1277
  level = default_org
1272
1278
  }
1273
- } else if ( level_ids == '1' ) {
1279
+ } else if ( level_ids == levels['MLB'] ) {
1274
1280
  team_ids = session.getTeamIds()
1275
1281
  for (let i=0; i<session.credentials.fav_teams.length; i++) {
1276
1282
  if ( session.credentials.fav_teams[i] != '' ) {
1277
- if ( level_ids == '1' ) {
1278
- level_ids = levels['All']
1279
- }
1280
- team_ids += ',' + AFFILIATE_TEAM_IDS[session.credentials.fav_teams[i]]
1283
+ level_ids = levels['All']
1284
+ team_ids += ',' + session.getAffiliateTeamIds(session.credentials.fav_teams[i])
1281
1285
  }
1282
1286
  }
1283
1287
  }
1284
1288
  let cache_name = gameDate
1285
- if ( level_ids != '1' ) {
1289
+ if ( level_ids != levels['MLB'] ) {
1286
1290
  cache_name += '.' + level_ids
1287
1291
  }
1288
1292
  if ( team_ids != '' ) {
@@ -1408,10 +1412,11 @@ app.get('/', async function(req, res) {
1408
1412
  body += ' or <span class="tooltip">Org<span class="tooltiptext">Major league parent organization</span></span>: '
1409
1413
  body += '<select id="org" onchange="level=\'' + default_org + '\';org=this.value;reload()">'
1410
1414
  body += '<option value="' + default_org + '">' + default_org + '</option>'
1411
- for (const [key, value] of Object.entries(AFFILIATE_TEAM_IDS)) {
1412
- body += '<option value="' + key + '"'
1413
- if ( org == key ) body += ' selected'
1414
- body += '>' + key + '</option> '
1415
+ var orgs = session.getOrgs()
1416
+ for (var i = 0; i < orgs.length; i++) {
1417
+ body += '<option value="' + orgs[i] + '"'
1418
+ if ( org == orgs[i] ) body += ' selected'
1419
+ body += '>' + orgs[i] + '</option> '
1415
1420
  }
1416
1421
  body += '</select></p>' + "\n"
1417
1422
 
@@ -1507,9 +1512,9 @@ app.get('/', async function(req, res) {
1507
1512
 
1508
1513
  let blackouts = {}
1509
1514
 
1510
- if ( (mediaType == 'MLBTV') && ((level_ids == '1') || level_ids.startsWith('1,')) ) {
1511
- // Recap Rundown beginning in 2023
1512
- 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) ) {
1515
+ if ( (mediaType == 'MLBTV') && ((level_ids == levels['MLB']) || level_ids.startsWith(levels['MLB'] + ',')) ) {
1516
+ // Recap Rundown beginning in 2023, disabled because it stopped working
1517
+ /*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) ) {
1513
1518
  body += '<tr><td><span class="tooltip">VOD<span class="tooltiptext">Recap Rundown plays all of a day\'s recaps in order.</span></span></td><td>'
1514
1519
  let dateArray = gameDate.split('-')
1515
1520
  let querystring = '?event=recaprundown' + parseInt(dateArray[1]).toString() + '-' + parseInt(dateArray[2]).toString() + '-' + dateArray[0].substring(2,4)
@@ -1520,7 +1525,7 @@ app.get('/', async function(req, res) {
1520
1525
  querystring += content_protect_b
1521
1526
  body += '<a href="' + thislink + querystring + '">Recap Rundown</a>'
1522
1527
  body += '</td></tr>' + "\n"
1523
- }
1528
+ }*/
1524
1529
 
1525
1530
  if ( (gameDate >= today) && cache_data.dates && cache_data.dates[0] && cache_data.dates[0].games && (cache_data.dates[0].games.length > 0) ) {
1526
1531
  blackouts = await session.get_blackout_games(cache_data.dates[0].games, true)
@@ -1599,15 +1604,32 @@ app.get('/', async function(req, res) {
1599
1604
  let game_started = false
1600
1605
 
1601
1606
  let awayteam = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1607
+ let awayteam_abbr
1602
1608
  if ( cache_data.dates[0].games[j].teams['away'].team.sport.name != 'Major League Baseball' ) {
1603
1609
  awayteam = cache_data.dates[0].games[j].teams['away'].team.shortName + ' (' + session.getParent(cache_data.dates[0].games[j].teams['away'].team.parentOrgName) + ')'
1610
+ awayteam_abbr = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1611
+ awayteam_level = session.getLevelNameFromSportId(cache_data.dates[0].games[j].teams['away'].team.sport.id)
1604
1612
  }
1605
1613
  let hometeam = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1614
+ let hometeam_abbr
1606
1615
  if ( cache_data.dates[0].games[j].teams['home'].team.sport.name != 'Major League Baseball' ) {
1607
1616
  hometeam = cache_data.dates[0].games[j].teams['home'].team.shortName + ' (' + session.getParent(cache_data.dates[0].games[j].teams['home'].team.parentOrgName) + ')'
1617
+ hometeam_abbr = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1618
+ hometeam_level = session.getLevelNameFromSportId(cache_data.dates[0].games[j].teams['home'].team.sport.id)
1608
1619
  }
1609
1620
 
1610
- let teams = awayteam + " @ " + hometeam
1621
+ let teams = ""
1622
+ if ( awayteam_abbr ) {
1623
+ teams += '<span class="tooltip">' + awayteam + '<span class="tooltiptext">Team Abbreviation: ' + awayteam_abbr + ', level ' + awayteam_level + '</span></span>'
1624
+ } else {
1625
+ teams += awayteam
1626
+ }
1627
+ teams += " @ "
1628
+ if ( hometeam_abbr ) {
1629
+ teams += '<span class="tooltip">' + hometeam + '<span class="tooltiptext">Team Abbreviation: ' + hometeam_abbr + ', level ' + hometeam_level + '</span></span>'
1630
+ } else {
1631
+ teams += hometeam
1632
+ }
1611
1633
  let pitchers = ""
1612
1634
  let state = "<br/>"
1613
1635
 
@@ -1768,7 +1790,7 @@ app.get('/', async function(req, res) {
1768
1790
  body += '><td>' + description + teams + pitchers + state + '</td>'
1769
1791
 
1770
1792
  // Check if Winter League / MiLB game first
1771
- if ( (cache_data.dates[0].games[j].teams['home'].team.sport.id != '1') && (mediaType == 'MLBTV') ) {
1793
+ if ( (cache_data.dates[0].games[j].teams['home'].team.sport.id != levels['MLB']) && (mediaType == 'MLBTV') ) {
1772
1794
  body += "<td>"
1773
1795
  if ( cache_data.dates[0].games[j].broadcasts ) {
1774
1796
  let broadcastName = 'N/A'
@@ -2088,13 +2110,13 @@ app.get('/', async function(req, res) {
2088
2110
  resolution = 'best'
2089
2111
  }
2090
2112
 
2091
- body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all live broadcasts. If a zip code has been provided, channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + content_protect_b + '">guide.xml</a></p>' + "\n"
2113
+ body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all live MLB broadcasts. If favorite team(s) have been provided, it will also include affiliate games for those organizations. If a zip code has been provided, channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + content_protect_b + '">guide.xml</a></p>' + "\n"
2092
2114
 
2093
2115
  let include_teams = 'ari,national'
2094
2116
  if ( session.credentials.fav_teams.length > 0 ) {
2095
2117
  include_teams = session.credentials.fav_teams.toString()
2096
2118
  }
2097
- body += '<p><span class="tooltip">By team<span class="tooltiptext">Including a team (by abbreviation, in a comma-separated list if more than 1) will include all of its broadcasts, or if that team is not broadcasting the game, it will include the national broadcast or opponent\'s broadcast if available. If a zip code has been provided, channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=' + include_teams + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + content_protect_b + '">guide.xml</a></p>' + "\n"
2119
+ body += '<p><span class="tooltip">By team<span class="tooltiptext">Including a team (MLB only, by abbreviation, in a comma-separated list if more than 1) will include all of its broadcasts, or if that team is not broadcasting the game, it will include the national broadcast or opponent\'s broadcast if available. It will also include affiliate games for those organizations. If a zip code has been provided, channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=' + include_teams + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + content_protect_b + '">guide.xml</a></p>' + "\n"
2098
2120
 
2099
2121
  body += '<p><span class="tooltip">Include blackouts<span class="tooltiptext">An optional parameter added to the URL will include channels/games subject to blackout (although you may not be able to play those games).</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">guide.xml</a></p>' + "\n"
2100
2122
 
@@ -2103,7 +2125,7 @@ app.get('/', async function(req, res) {
2103
2125
  exclude_teams = session.credentials.blackout_teams.toString()
2104
2126
  exclude_teams += ',national'
2105
2127
  }
2106
- body += '<p><span class="tooltip">Exclude a team + national<span class="tooltiptext">This is useful for excluding games you may be blacked out from, even if you have not provided a zip code. Excluding a team (by abbreviation, in a comma-separated list if more than 1) will exclude every game involving that team. National refers to <a href="https://www.mlb.com/live-stream-games/national-blackout">USA national TV broadcasts</a>.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&excludeTeams=' + exclude_teams + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">guide.xml</a></p>' + "\n"
2128
+ body += '<p><span class="tooltip">Exclude a team + national<span class="tooltiptext">This is useful for excluding games you may be blacked out from, even if you have not provided a zip code. Excluding a team (MLB only, by abbreviation, in a comma-separated list if more than 1) will exclude every game involving that team. National refers to <a href="https://www.mlb.com/live-stream-games/national-blackout">USA national TV broadcasts</a>.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&excludeTeams=' + exclude_teams + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">guide.xml</a></p>' + "\n"
2107
2129
 
2108
2130
  body += '<p><span class="tooltip">Include (or exclude) LIDOM<span class="tooltiptext">Dominican Winter League, aka Liga de Beisbol Dominicano. Live stream only, does not support starting from the beginning or certain innings, skip options, etc.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=lidom' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">guide.xml</a></p>' + "\n"
2109
2131
 
@@ -2121,6 +2143,10 @@ app.get('/', async function(req, res) {
2121
2143
  body += '<p><span class="tooltip">Free games only<span class="tooltiptext">Only includes games marked as free. Blackouts still apply. If a zip code has been provided, channels/games subject to blackout will be omitted by default.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=free' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=free' + content_protect_b + '">guide.xml</a></p>' + "\n"
2122
2144
  }
2123
2145
 
2146
+ body += '<p><span class="tooltip">Include affiliates by org<span class="tooltiptext">Including an organization (by MLB team abbreviation, in a comma-separated list if more than 1) will include all of its affiliate broadcasts, or if that affiliate is not broadcasting the game, it will include the opponent\'s broadcast if available. If this option is not specified, but favorite team(s) have been provided, affiliate games for those organizations will be included anyway.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeOrgs=ari,atl' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeOrgs=ari,atl' + content_protect_b + '">guide.xml</a></p>' + "\n"
2147
+
2148
+ body += '<p><span class="tooltip">Include by level<span class="tooltiptext">Including a level (AAA, AA, A+, or A, in a comma-separated list if more than 1) will include all of its broadcasts, and exclude all other levels.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeLevels=aaa,aa' + content_protect_b + '">channels.m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeLevels=aaa,aa' + content_protect_b + '">guide.xml</a></p>' + "\n"
2149
+
2124
2150
  body += '</td></tr></table><br/>' + "\n"
2125
2151
 
2126
2152
  body += '<table><tr><td>' + "\n"
@@ -2135,6 +2161,7 @@ app.get('/', async function(req, res) {
2135
2161
 
2136
2162
  let examples = [
2137
2163
  ['Team live video', '?team=' + example_team + '&resolution=best'],
2164
+ ['MiLB team live video', '?team=COL&level=AAA&resolution=best'],
2138
2165
  ['Team live radio', '?team=' + example_team + '&mediaType=Audio'],
2139
2166
  ['Catch-up/condensed', '?team=' + example_team + '&resolution=best&skip=pitches&date=today'],
2140
2167
  ['Condensed yesterday', '?team=' + example_team + '&resolution=best&skip=pitches&date=yesterday'],
@@ -2419,7 +2446,17 @@ app.get('/channels.m3u', async function(req, res) {
2419
2446
  includeBlackouts = req.query.includeBlackouts
2420
2447
  }
2421
2448
 
2422
- var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, server, includeBlackouts, resolution, pipe, startingChannelNumber)
2449
+ let includeLevels = []
2450
+ if ( req.query.includeLevels ) {
2451
+ includeLevels = decodeURIComponent(req.query.includeLevels.toUpperCase()).split(',')
2452
+ }
2453
+
2454
+ let includeOrgs = []
2455
+ if ( req.query.includeOrgs ) {
2456
+ includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
2457
+ }
2458
+
2459
+ var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution, pipe, startingChannelNumber)
2423
2460
 
2424
2461
  res.writeHead(200, {'Content-Type': 'audio/x-mpegurl'})
2425
2462
  res.end(body)
@@ -2450,9 +2487,19 @@ app.get('/guide.xml', async function(req, res) {
2450
2487
  includeBlackouts = req.query.includeBlackouts
2451
2488
  }
2452
2489
 
2490
+ let includeLevels = []
2491
+ if ( req.query.includeLevels ) {
2492
+ includeLevels = decodeURIComponent(req.query.includeLevels.toUpperCase()).split(',')
2493
+ }
2494
+
2495
+ let includeOrgs = []
2496
+ if ( req.query.includeOrgs ) {
2497
+ includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
2498
+ }
2499
+
2453
2500
  let server = 'http://' + req.headers.host
2454
2501
 
2455
- var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, server, includeBlackouts)
2502
+ var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts)
2456
2503
 
2457
2504
  res.end(body)
2458
2505
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlbserver",
3
- "version": "2023.05.25",
3
+ "version": "2023.08.27",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
package/session.js CHANGED
@@ -25,6 +25,8 @@ const TODAY_UTC_HOURS = 8 // UTC hours (EST + 4) into tomorrow to still use toda
25
25
 
26
26
  const TEAM_IDS = {'ARI':'109','ATL':'144','BAL':'110','BOS':'111','CHC':'112','CWS':'145','CIN':'113','CLE':'114','COL':'115','DET':'116','HOU':'117','KC':'118','LAA':'108','LAD':'119','MIA':'146','MIL':'158','MIN':'142','NYM':'121','NYY':'147','OAK':'133','PHI':'143','PIT':'134','STL':'138','SD':'135','SF':'137','SEA':'136','TB':'139','TEX':'140','TOR':'141','WSH':'120'}
27
27
 
28
+ const AFFILIATE_TEAM_IDS = { 'ARI': '419,516,2310,5368', 'ATL': '430,432,478,431', 'BAL': '418,568,548,488', 'BOS': '428,414,533,546', 'CHC': '521,451,550,553', 'CIN': '450,459,498,416', 'CLE': '445,402,437,481', 'COL': '259,486,342,538', 'CWS': '247,580,487,494', 'DET': '512,570,582,106', 'HOU': '482,5434,573,3712', 'KC': '1350,3705,541,565', 'LAA': '401,559,460,561', 'LAD': '260,238,456,526', 'MIA': '479,564,554,4124', 'MIL': '249,572,556,5015', 'MIN': '492,509,1960,3898', 'NYM': '453,507,552,505', 'NYY': '1956,587,531,537', 'OAK': '237,499,400,524', 'PHI': '427,522,1410,566', 'PIT': '3390,484,452,477', 'SD': '103,510,584,4904', 'SEA': '403,515,529,574', 'SF': '105,461,476,3410', 'STL': '279,235,440,443', 'TB': '2498,233,234,421', 'TEX': '102,485,540,448', 'TOR': '424,435,463,422', 'WSH': '436,534,547,426' }
29
+
28
30
  // Other country options would be USA, Canada, or other
29
31
  const ESPN_SUNDAY_NIGHT_BLACKOUT_COUNTRIES = ["Angola", "Anguilla", "Antigua and Barbuda", "Argentina", "Aruba", "Australia", "Bahamas", "Barbados", "Belize", "Belize", "Benin", "Bermuda", "Bolivia", "Bonaire", "Botswana", "Brazil", "British Virgin Islands", "Burkina Faso", "Burundi", "Cameroon", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "Colombia", "Comoros", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Curacao", "Democratic Republic of the Congo", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "El Salvador", "England", "Equatorial Guinea", "Eritrea", "Eswatini", "Ethiopia", "Falkland Islands", "Falkland Islands", "Fiji", "French Guiana", "French Guiana", "French Polynesia", "Gabon", "Ghana", "Grenada", "Guadeloupe", "Guatemala", "Guinea", "Guinea Bissau", "Guyana", "Guyana", "Haiti", "Honduras", "Ireland", "Jamaica", "Kenya", "Kiribati", "Lesotho", "Liberia", "Madagascar", "Malawi", "Mali", "Marshall Islands", "Martinique", "Mayotte", "Mexico", "Micronesia", "Montserrat", "Mozambique", "Namibia", "Netherlands", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Northern Ireland", "Palau Islands", "Panama", "Paraguay", "Peru", "Republic of Ireland", "Reunion", "Rwanda", "Saba", "Saint Maarten", "Samoa", "Sao Tome & Principe", "Scotland", "Senegal", "Seychelles", "Sierra Leone", "Solomon Islands", "Somalia", "South Africa", "St. Barthelemy", "St. Eustatius", "St. Kitts and Nevis", "St. Lucia", "St. Martin", "St. Vincent and the Grenadines", "Sudan", "Surinam", "Suriname", "Tahiti", "Tanzania & Zanzibar", "The Gambia", "The Republic of Congo", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Uruguay", "Venezuela", "Wales", "Zambia", "Zimbabwe"]
30
32
 
@@ -1106,14 +1108,36 @@ class sessionClass {
1106
1108
  return LEVELS
1107
1109
  }
1108
1110
 
1111
+ getLevelNameFromSportId(sportId) {
1112
+ let sportIds = Object.values(LEVELS)
1113
+ for (var i=0; i<sportIds.length; i++) {
1114
+ if ( sportId == sportIds[i] ) {
1115
+ let levelNames = Object.keys(LEVELS)
1116
+ return levelNames[i]
1117
+ }
1118
+ }
1119
+ }
1120
+
1109
1121
  getTeamIds(team_abbr = false) {
1110
1122
  if ( team_abbr ) {
1111
1123
  return TEAM_IDS[team_abbr]
1112
1124
  } else {
1113
- return Object.values(TEAM_IDS).toString()
1125
+ return Object.values(TEAM_IDS).toString() + ',159,160'
1126
+ }
1127
+ }
1128
+
1129
+ getAffiliateTeamIds(team_abbr = false) {
1130
+ if ( team_abbr ) {
1131
+ return AFFILIATE_TEAM_IDS[team_abbr]
1132
+ } else {
1133
+ return Object.values(AFFILIATE_TEAM_IDS).toString()
1114
1134
  }
1115
1135
  }
1116
1136
 
1137
+ getOrgs() {
1138
+ return Object.keys(AFFILIATE_TEAM_IDS)
1139
+ }
1140
+
1117
1141
  // get parent org nickname
1118
1142
  getParent(parent) {
1119
1143
  let long_orgs = [ 'Jays', 'Sox' ]
@@ -1179,6 +1203,17 @@ class sessionClass {
1179
1203
  this.save_cache_data()
1180
1204
  }
1181
1205
 
1206
+ setWeekCacheExpiry(cache_name, expiryDate) {
1207
+ if ( !this.cache.weeks ) {
1208
+ this.cache.weeks={}
1209
+ }
1210
+ if ( !this.cache.weeks[cache_name] ) {
1211
+ this.cache.weeks[cache_name] = {}
1212
+ }
1213
+ this.cache.weeks[cache_name].weekCacheExpiry = expiryDate
1214
+ this.save_cache_data()
1215
+ }
1216
+
1182
1217
  setAiringsCacheExpiry(cache_name, expiryDate) {
1183
1218
  if ( !this.cache.airings ) {
1184
1219
  this.cache.airings={}
@@ -1775,7 +1810,7 @@ class sessionClass {
1775
1810
  }
1776
1811
 
1777
1812
  // get mediaId for a live channel request
1778
- async getMediaId(team, mediaType, mediaDate, gameNumber, includeBlackouts) {
1813
+ async getMediaId(team, level, mediaType, mediaDate, gameNumber, includeBlackouts) {
1779
1814
  try {
1780
1815
  this.debuglog('getMediaId')
1781
1816
 
@@ -1820,100 +1855,136 @@ class sessionClass {
1820
1855
  if ( includeBlackouts == 'false' ) blackouts = await this.get_blackout_games(cache_data.dates[0].games, true)
1821
1856
 
1822
1857
  for (var j = 0; j < cache_data.dates[0].games.length; j++) {
1823
- if ( mediaInfo.mediaId ) break
1858
+ if ( mediaInfo.mediaId || mediaInfo.gamePk ) break
1824
1859
 
1825
- // check blackout status, if necessary
1826
1860
  let gamePk = cache_data.dates[0].games[j].gamePk.toString()
1827
- if ( (mediaType == 'MLBTV') && (includeBlackouts == 'false') && blackouts[gamePk] ) {
1828
- if ( !blackouts[gamePk].blackoutExpiry || (currentDate < blackouts[gamePk].blackoutExpiry) ) {
1829
- this.debuglog('getMediaId requested game is blacked out')
1830
- continue
1831
- }
1832
- }
1833
1861
 
1834
- if ( (typeof cache_data.dates[0].games[j] !== 'undefined') && cache_data.dates[0].games[j].content && cache_data.dates[0].games[j].content.media && cache_data.dates[0].games[j].content.media.epg ) {
1862
+ if ( typeof cache_data.dates[0].games[j] !== 'undefined' ) {
1835
1863
  let home_team = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1836
1864
  let away_team = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1865
+ let home_level = cache_data.dates[0].games[j].teams['home'].team.sport.id
1866
+ let away_level = cache_data.dates[0].games[j].teams['away'].team.sport.id
1867
+ this.debuglog('checking game ' + cache_data.dates[0].games[j].teams['home'].team.abbreviation + '@' + cache_data.dates[0].games[j].teams['away'].team.abbreviation)
1837
1868
 
1838
1869
  // check that that game involves the requested team, or if it's a national or free game and we've requested that
1839
- if ( (team.toUpperCase() == home_team) || (team.toUpperCase() == away_team) || ((team.toUpperCase().indexOf('NATIONAL.') == 0) && ((cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType] == 'NATIONAL') || ((mediaType == 'MLBTV') && (cache_data.dates[0].games[j].seriesDescription != 'Regular Season') && (cache_data.dates[0].games[j].seriesDescription != 'Spring Training')))) || (team.toUpperCase().startsWith('FREE.') && cache_data.dates[0].games[j].content.media.freeGame) ) {
1840
-
1841
- let teamType
1842
- let broadcast_count = 0
1843
- for (var k = 0; k < cache_data.dates[0].games[j].content.media.epg.length; k++) {
1844
- if ( mediaInfo.mediaId ) break
1845
- let mediaTitle = cache_data.dates[0].games[j].content.media.epg[k].title
1846
- if ( mediaType == mediaTitle ) {
1847
- // initial loop will count number of broadcasts
1848
- broadcast_count = await this.count_broadcasts(cache_data.dates[0].games[j].content.media.epg[k].items, mediaType, mediaTitle, language)
1849
-
1850
- for (var x = 0; x < cache_data.dates[0].games[j].content.media.epg[k].items.length; x++) {
1851
- // for video, check that it's not in-market
1852
- if ( (mediaType == 'MLBTV') && await this.check_in_market(cache_data.dates[0].games[j].content.media.epg[k].items[x]) ) {
1853
- continue
1870
+ if ( ((team.toUpperCase() == home_team) && (LEVELS[level.toUpperCase()] == home_level)) || ((team.toUpperCase() == away_team) && (LEVELS[level.toUpperCase()] == away_level)) || ((team.toUpperCase().indexOf('NATIONAL.') == 0) && ((cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType] == 'NATIONAL') || ((mediaType == 'MLBTV') && (cache_data.dates[0].games[j].seriesDescription != 'Regular Season') && (cache_data.dates[0].games[j].seriesDescription != 'Spring Training')))) || (team.toUpperCase().startsWith('FREE.') && cache_data.dates[0].games[j].content.media.freeGame) ) {
1871
+ this.debuglog('matched team for ' + cache_data.dates[0].games[j].teams['home'].team.abbreviation + '@' + cache_data.dates[0].games[j].teams['away'].team.abbreviation)
1872
+
1873
+ // Check if Winter League / MiLB game first
1874
+ if ( (cache_data.dates[0].games[j].teams['home'].team.sport.id != LEVELS['MLB']) && (mediaType == 'MLBTV') ) {
1875
+ if ( cache_data.dates[0].games[j].broadcasts ) {
1876
+ let broadcastName = 'N/A'
1877
+ for (var k = 0; k < cache_data.dates[0].games[j].broadcasts.length; k++) {
1878
+ if ( cache_data.dates[0].games[j].broadcasts[k].name != 'Audio' ) {
1879
+ broadcastName = mediaType
1880
+ break
1881
+ }
1882
+ }
1883
+ if ( broadcastName == 'N/A' ) {
1884
+ break
1885
+ } else {
1886
+ if ( gameNumber && (gameNumber > 1) ) {
1887
+ this.debuglog('matched team for MILB game number 1')
1888
+ gameNumber--
1889
+ } else {
1890
+ this.debuglog('matched team for MILB event')
1891
+ mediaInfo.gamePk = gamePk
1892
+ break
1854
1893
  }
1894
+ }
1895
+ }
1896
+ break
1897
+ }
1855
1898
 
1856
- if ( ((typeof cache_data.dates[0].games[j].content.media.epg[k].items[x].language) == 'undefined') || (cache_data.dates[0].games[j].content.media.epg[k].items[x].language == language) ) {
1857
- teamType = cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType]
1899
+ // MLB games
1900
+ if ( cache_data.dates[0].games[j].content && cache_data.dates[0].games[j].content.media && cache_data.dates[0].games[j].content.media.epg ) {
1858
1901
 
1859
- let station = cache_data.dates[0].games[j].content.media.epg[k].items[x].callLetters
1902
+ // check blackout status, if necessary
1903
+ if ( (mediaType == 'MLBTV') && (includeBlackouts == 'false') && blackouts[gamePk] ) {
1904
+ if ( !blackouts[gamePk].blackoutExpiry || (currentDate < blackouts[gamePk].blackoutExpiry) ) {
1905
+ this.debuglog('getMediaId requested game is blacked out')
1906
+ continue
1907
+ }
1908
+ }
1860
1909
 
1861
- // process requested national games (with the team/channel NATIONAL.x)
1862
- if ( (team.toUpperCase().indexOf('NATIONAL.') == 0) && ((cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType] == 'NATIONAL') || ((mediaType == 'MLBTV') && (cache_data.dates[0].games[j].seriesDescription != 'Regular Season') && (cache_data.dates[0].games[j].seriesDescription != 'Spring Training'))) ) {
1910
+ let teamType
1911
+ let broadcast_count = 0
1912
+ for (var k = 0; k < cache_data.dates[0].games[j].content.media.epg.length; k++) {
1913
+ if ( mediaInfo.mediaId ) break
1914
+ let mediaTitle = cache_data.dates[0].games[j].content.media.epg[k].title
1915
+ if ( mediaType == mediaTitle ) {
1916
+ // initial loop will count number of broadcasts
1917
+ broadcast_count = await this.count_broadcasts(cache_data.dates[0].games[j].content.media.epg[k].items, mediaType, mediaTitle, language)
1863
1918
 
1864
- nationalCount += 1
1865
- let nationalArray = team.split('.')
1866
- if ( (nationalArray.length == 2) && (nationalArray[1] == nationalCount) ) {
1867
- this.debuglog('matched national event')
1868
- mediaInfo = await this.check_media_state(cache_data.dates[0].games[j].content.media.epg[k].items[x], cache_data.dates[0].games[j].status.abstractGameState, mediaDate)
1869
- break
1870
- }
1919
+ for (var x = 0; x < cache_data.dates[0].games[j].content.media.epg[k].items.length; x++) {
1920
+ // for video, check that it's not in-market
1921
+ if ( (mediaType == 'MLBTV') && await this.check_in_market(cache_data.dates[0].games[j].content.media.epg[k].items[x]) ) {
1922
+ continue
1923
+ }
1871
1924
 
1872
- // process requested free games (with the team/channel FREE.x)
1873
- } else if ( team.toUpperCase().startsWith('FREE.') && cache_data.dates[0].games[j].content.media.freeGame ) {
1925
+ if ( ((typeof cache_data.dates[0].games[j].content.media.epg[k].items[x].language) == 'undefined') || (cache_data.dates[0].games[j].content.media.epg[k].items[x].language == language) ) {
1926
+ teamType = cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType]
1874
1927
 
1875
- freeCount += 1
1876
- let freeArray = team.split('.')
1877
- if ( (freeArray.length == 2) && (freeArray[1] == freeCount) ) {
1878
- this.debuglog('matched free event')
1879
- mediaInfo = await this.check_media_state(cache_data.dates[0].games[j].content.media.epg[k].items[x], cache_data.dates[0].games[j].status.abstractGameState, mediaDate)
1880
- break
1881
- }
1928
+ let station = cache_data.dates[0].games[j].content.media.epg[k].items[x].callLetters
1882
1929
 
1883
- // process requested team games
1884
- } else if ( (team.toUpperCase() == home_team) || (team.toUpperCase() == away_team) ) {
1930
+ // process requested national games (with the team/channel NATIONAL.x)
1931
+ if ( (team.toUpperCase().indexOf('NATIONAL.') == 0) && ((cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType] == 'NATIONAL') || ((mediaType == 'MLBTV') && (cache_data.dates[0].games[j].seriesDescription != 'Regular Season') && (cache_data.dates[0].games[j].seriesDescription != 'Spring Training'))) ) {
1885
1932
 
1886
- if ( ((team.toUpperCase() == home_team) && (teamType == 'HOME')) || ((team.toUpperCase() == away_team) && (teamType == 'AWAY')) || (broadcast_count == 1) ) {
1887
- if ( gameNumber && (gameNumber > 1) ) {
1888
- this.debuglog('matched team for game number 1')
1889
- gameNumber--
1890
- } else {
1891
- this.debuglog('matched team for event')
1933
+ nationalCount += 1
1934
+ let nationalArray = team.split('.')
1935
+ if ( (nationalArray.length == 2) && (nationalArray[1] == nationalCount) ) {
1936
+ this.debuglog('matched national event')
1892
1937
  mediaInfo = await this.check_media_state(cache_data.dates[0].games[j].content.media.epg[k].items[x], cache_data.dates[0].games[j].status.abstractGameState, mediaDate)
1893
1938
  break
1894
1939
  }
1895
- }
1896
1940
 
1941
+ // process requested free games (with the team/channel FREE.x)
1942
+ } else if ( team.toUpperCase().startsWith('FREE.') && cache_data.dates[0].games[j].content.media.freeGame ) {
1943
+
1944
+ freeCount += 1
1945
+ let freeArray = team.split('.')
1946
+ if ( (freeArray.length == 2) && (freeArray[1] == freeCount) ) {
1947
+ this.debuglog('matched free event')
1948
+ mediaInfo = await this.check_media_state(cache_data.dates[0].games[j].content.media.epg[k].items[x], cache_data.dates[0].games[j].status.abstractGameState, mediaDate)
1949
+ break
1950
+ }
1951
+
1952
+ // process requested team games
1953
+ } else if ( (team.toUpperCase() == home_team) || (team.toUpperCase() == away_team) ) {
1954
+
1955
+ if ( ((team.toUpperCase() == home_team) && (teamType == 'HOME')) || ((team.toUpperCase() == away_team) && (teamType == 'AWAY')) || (broadcast_count == 1) ) {
1956
+ if ( gameNumber && (gameNumber > 1) ) {
1957
+ this.debuglog('matched team for game number 1')
1958
+ gameNumber--
1959
+ } else {
1960
+ this.debuglog('matched team for event')
1961
+ mediaInfo = await this.check_media_state(cache_data.dates[0].games[j].content.media.epg[k].items[x], cache_data.dates[0].games[j].status.abstractGameState, mediaDate)
1962
+ break
1963
+ }
1964
+ }
1965
+
1966
+ }
1897
1967
  }
1898
1968
  }
1899
1969
  }
1900
1970
  }
1901
- }
1902
1971
 
1903
- // grab any alternate audio tracks, if necessary
1904
- if ( mediaInfo.mediaId && (mediaType == 'MLBTV') && (broadcast_count == 1) ) {
1905
- mediaInfo.alternateAudioTracks = await this.getAlternateAudioTracks(cache_data.dates[0].games[j].content.media.epg, teamType)
1972
+ // grab any alternate audio tracks, if necessary
1973
+ if ( mediaInfo.mediaId && (mediaType == 'MLBTV') && (broadcast_count == 1) ) {
1974
+ mediaInfo.alternateAudioTracks = await this.getAlternateAudioTracks(cache_data.dates[0].games[j].content.media.epg, teamType)
1975
+ }
1976
+
1906
1977
  }
1907
1978
 
1908
1979
  }
1909
1980
  }
1910
1981
  }
1911
1982
 
1912
- if (mediaInfo.mediaId) {
1983
+ if (mediaInfo.mediaId || mediaInfo.gamePk) {
1913
1984
  return mediaInfo
1914
1985
  }
1915
1986
  }
1916
- this.log('could not find mediaId')
1987
+ this.log('could not find mediaId or gamePk')
1917
1988
  } else {
1918
1989
  this.log('will not find mediaId for future date')
1919
1990
  }
@@ -2012,11 +2083,11 @@ class sessionClass {
2012
2083
  }
2013
2084
 
2014
2085
  // get data for a day, either from cache or an API call
2015
- async getDayData(dateString, team = false, level_ids = LEVELS.MLB, team_ids = '') {
2086
+ async getDayData(dateString, team = false, level_ids = LEVELS.All, team_ids = '') {
2016
2087
  try {
2017
2088
  let cache_data
2018
2089
  let cache_name = dateString
2019
- if ( level_ids != '1' ) {
2090
+ if ( level_ids != LEVELS['MLB'] ) {
2020
2091
  cache_name += '.' + level_ids
2021
2092
  }
2022
2093
  if ( team_ids != '' ) {
@@ -2109,7 +2180,7 @@ class sessionClass {
2109
2180
  }
2110
2181
 
2111
2182
  // get data for 3 weeks, either from cache or an API call
2112
- async getWeeksData() {
2183
+ async getWeeksData(level_ids, team_ids) {
2113
2184
  try {
2114
2185
  this.debuglog('getWeeksData')
2115
2186
 
@@ -2118,15 +2189,29 @@ class sessionClass {
2118
2189
 
2119
2190
  let cache_data
2120
2191
  let cache_name = 'week'
2192
+
2193
+ if ( level_ids != LEVELS['All'] ) {
2194
+ cache_name += '.' + level_ids
2195
+ }
2196
+ if ( team_ids != '' ) {
2197
+ cache_name += '.' + team_ids
2198
+ }
2199
+
2121
2200
  let cache_file = path.join(this.CACHE_DIRECTORY, cache_name + '.json')
2122
2201
  let currentDate = new Date()
2123
- if ( !fs.existsSync(cache_file) || !this.cache || !this.cache.weekCacheExpiry || (currentDate > new Date(this.cache.weekCacheExpiry)) ) {
2202
+ if ( !fs.existsSync(cache_file) || !this.cache || !this.cache.weeks || !this.cache.weeks[cache_name] || !this.cache.weeks[cache_name].weekCacheExpiry || (currentDate > new Date(this.cache.weeks[cache_name].weekCacheExpiry)) ) {
2124
2203
  let startDate = this.liveDate(utcHours)
2125
2204
  let endDate = new Date(startDate)
2126
2205
  endDate.setDate(endDate.getDate()+20)
2127
2206
  endDate = endDate.toISOString().substring(0,10)
2207
+ let data_url = 'http://statsapi.mlb.com/api/v1/schedule?sportId=' + level_ids
2208
+ if ( team_ids != '' ) {
2209
+ data_url += '&teamId=' + team_ids
2210
+ }
2211
+ data_url += '&startDate=' + startDate + '&endDate=' + endDate + '&hydrate=broadcasts(all),game(content(media(epg))),probablePitcher,linescore,team'
2128
2212
  let reqObj = {
2129
- url: 'https://bdfed.stitch.mlbinfra.com/bdfed/transform-mlb-scoreboard?stitch_env=prod&sortTemplate=2&sportId=1&sportId=17&startDate=' + startDate + '&endDate=' + endDate + '&gameType=E&&gameType=S&&gameType=R&&gameType=F&&gameType=D&&gameType=L&&gameType=W&&gameType=A&language=en&leagueId=104&leagueId=103&leagueId=131&contextTeamId=',
2213
+ //url: 'https://bdfed.stitch.mlbinfra.com/bdfed/transform-mlb-scoreboard?stitch_env=prod&sortTemplate=2&sportId=1&sportId=17&startDate=' + startDate + '&endDate=' + endDate + '&gameType=E&&gameType=S&&gameType=R&&gameType=F&&gameType=D&&gameType=L&&gameType=W&&gameType=A&language=en&leagueId=104&leagueId=103&leagueId=131&contextTeamId=',
2214
+ url: data_url,
2130
2215
  headers: {
2131
2216
  'User-agent': USER_AGENT,
2132
2217
  'Origin': 'https://www.mlb.com',
@@ -2140,17 +2225,18 @@ class sessionClass {
2140
2225
  //this.debuglog(response)
2141
2226
  cache_data = JSON.parse(response)
2142
2227
  this.save_json_cache_file(cache_name, cache_data)
2228
+
2143
2229
  this.debuglog('setting channels cache expiry to next day')
2144
- let nextDate = new Date(startDate)
2145
- nextDate.setDate(nextDate.getDate()+1)
2146
- nextDate.setHours(nextDate.getHours()+utcHours)
2147
- this.cache.weekCacheExpiry = nextDate
2148
- this.save_cache_data()
2230
+ let cacheExpiry = new Date(startDate)
2231
+ cacheExpiry.setDate(cacheExpiry.getDate()+1)
2232
+ cacheExpiry.setHours(cacheExpiry.getHours()+utcHours)
2233
+ // finally save the setting
2234
+ this.setWeekCacheExpiry(cache_name, cacheExpiry)
2149
2235
  } else {
2150
2236
  this.log('error : invalid json from url ' + reqObj.url)
2151
2237
  }
2152
2238
  } else {
2153
- this.debuglog('using cached channel data')
2239
+ this.debuglog('using cached week data')
2154
2240
  cache_data = this.readFileToJson(cache_file)
2155
2241
  }
2156
2242
  if (cache_data) {
@@ -2162,7 +2248,7 @@ class sessionClass {
2162
2248
  }
2163
2249
 
2164
2250
  // get TV data (channels or guide)
2165
- async getTVData(dataType, mediaType, includeTeams, excludeTeams, server, includeBlackouts, resolution='best', pipe='false', startingChannelNumber=1) {
2251
+ async getTVData(dataType, mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution='best', pipe='false', startingChannelNumber=1) {
2166
2252
  try {
2167
2253
  this.debuglog('getTVData for ' + dataType)
2168
2254
 
@@ -2194,7 +2280,40 @@ class sessionClass {
2194
2280
  }
2195
2281
  }
2196
2282
 
2197
- let cache_data = await this.getWeeksData()
2283
+ var level_ids = LEVELS['All']
2284
+ var team_ids = ''
2285
+ if ( includeLevels.length > 0 ) {
2286
+ if ( (includeLevels.length > 1) || !includeLevels.includes('ALL') ) {
2287
+ let level_list = []
2288
+ for (let i = 0; i < includeLevels.length; i++) {
2289
+ level_list.append(LEVELS[includeLevels[i]])
2290
+ }
2291
+ level_ids = level_list.toString()
2292
+ }
2293
+ } else {
2294
+ if ( includeOrgs.length > 0 ) {
2295
+ team_ids = this.getTeamIds()
2296
+ for (let i = 0; i < includeOrgs.length; i++) {
2297
+ team_ids += ',' + AFFILIATE_TEAM_IDS[includeOrgs[i]]
2298
+ }
2299
+ } else if ( includeTeams.length > 0 ) {
2300
+ team_ids = this.getTeamIds()
2301
+ for (let i=0; i<includeTeams.length; i++) {
2302
+ if ( includeTeams[i] != '' ) {
2303
+ team_ids += ',' + AFFILIATE_TEAM_IDS[includeTeams[i]]
2304
+ }
2305
+ }
2306
+ } else {
2307
+ team_ids = this.getTeamIds()
2308
+ for (let i=0; i<this.credentials.fav_teams.length; i++) {
2309
+ if ( this.credentials.fav_teams[i] != '' ) {
2310
+ team_ids += ',' + AFFILIATE_TEAM_IDS[this.credentials.fav_teams[i]]
2311
+ }
2312
+ }
2313
+ }
2314
+ }
2315
+
2316
+ let cache_data = await this.getWeeksData(level_ids, team_ids)
2198
2317
  if (cache_data) {
2199
2318
  let today = this.liveDate()
2200
2319
  var nationalChannels = {}
@@ -2214,59 +2333,92 @@ class sessionClass {
2214
2333
 
2215
2334
  for (var j = 0; j < cache_data.dates[i].games.length; j++) {
2216
2335
  this.debuglog('getTVData processing game ' + j + ' for date ' + cache_data.dates[i].date)
2217
- // First check if Winter League games
2218
- if ( cache_data.dates[i].games[j].teams['home'].team.sport.name == 'Winter Leagues' ) {
2219
- if ( (excludeTeams.length > 0) && excludeTeams.includes('LIDOM') ) {
2220
- continue
2221
- } else if ( (includeTeams.length == 0) || includeTeams.includes('LIDOM') ) {
2222
- if ( cache_data.dates[i].games[j].broadcasts ) {
2336
+ // Check if Winter League / MiLB game first
2337
+ if ( (cache_data.dates[i].games[j].teams['home'].team.sport.id != LEVELS['MLB']) && (mediaType == 'MLBTV') ) {
2338
+ if ( cache_data.dates[i].games[j].broadcasts ) {
2339
+ let broadcastName = 'N/A'
2340
+ for (var k = 0; k < cache_data.dates[i].games[j].broadcasts.length; k++) {
2341
+ if ( cache_data.dates[i].games[j].broadcasts[k].name != 'Audio' ) {
2342
+ broadcastName = mediaType
2343
+ break
2344
+ }
2345
+ }
2346
+ if ( broadcastName == 'N/A' ) {
2347
+ continue
2348
+ } else {
2223
2349
  for (var k = 0; k < cache_data.dates[i].games[j].broadcasts.length; k++) {
2224
- if ( (mediaType == 'MLBTV') && (cache_data.dates[i].games[j].broadcasts[k].name == 'MLB.TV') ) {
2225
- let team = cache_data.dates[i].games[j].teams['home'].team.clubName.toUpperCase()
2226
- let channelid = mediaType + '.' + team
2227
- let logo = server + '/image.svg?teamId=MLB'
2228
- let streamMediaType = 'Video'
2229
- let stream = server + '/stream.m3u8?event=' + encodeURIComponent(team) + '&mediaType=' + streamMediaType
2230
- stream += '&resolution=' + resolution
2231
- if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
2232
- if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
2233
- channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
2234
-
2235
- let title = cache_data.dates[i].games[j].teams['home'].team.league.name + ': ' + cache_data.dates[i].games[j].teams['away'].team.name + ' at ' + cache_data.dates[i].games[j].teams['home'].team.name
2236
-
2237
- let description = ''
2238
- if ( cache_data.dates[i].games[j].doubleHeader != 'N' ) {
2239
- description += 'Game ' + cache_data.dates[i].games[j].gameNumber + '. '
2350
+ let team = cache_data.dates[i].games[j].teams['home'].team.abbreviation
2351
+ let team_id = cache_data.dates[i].games[j].teams['home'].team.id
2352
+ let opponent_team_id = cache_data.dates[i].games[j].teams['away'].team.id
2353
+ if ( team_ids.includes(opponent_team_id) && !team_ids.includes(team_id) ) {
2354
+ team_id = opponent_team_id
2355
+ team = cache_data.dates[i].games[j].teams['away'].team.abbreviation
2356
+ }
2357
+ let channelid = mediaType + '.' + team
2358
+ let logo = server + '/image.svg?teamId=' + team_id
2359
+ let streamMediaType = 'Video'
2360
+ let stream = server + '/stream.m3u8?team=' + encodeURIComponent(team) + '&mediaType=' + streamMediaType
2361
+ let sportId = cache_data.dates[i].games[j].teams['home'].team.sport.id
2362
+ stream += '&level=' + this.getLevelNameFromSportId(sportId)
2363
+ stream += '&resolution=' + resolution
2364
+ if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
2365
+ if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
2366
+ channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
2367
+
2368
+ let title = cache_data.dates[i].games[j].teams['home'].team.league.name + ': ' + cache_data.dates[i].games[j].teams['away'].team.name + ' at ' + cache_data.dates[i].games[j].teams['home'].team.name
2369
+
2370
+ let description = ''
2371
+ if ( cache_data.dates[i].games[j].seriesDescription != 'Regular Season' ) {
2372
+ description += cache_data.dates[i].games[j].seriesDescription + '. '
2373
+ }
2374
+ if ( cache_data.dates[i].games[j].doubleHeader != 'N' ) {
2375
+ description += 'Doubleheader game ' + cache_data.dates[i].games[j].gameNumber + '. '
2376
+ }
2377
+ var scheduledInnings = await this.get_scheduled_innings(cache_data.dates[0].games[j])
2378
+ if ( scheduledInnings != '9' ) {
2379
+ description += scheduledInnings + '-inning game. '
2380
+ }
2381
+ if ( (cache_data.dates[i].games[j].teams['away'].probablePitcher && cache_data.dates[i].games[j].teams['away'].probablePitcher.fullName) || (cache_data.dates[i].games[j].teams['home'].probablePitcher && cache_data.dates[i].games[j].teams['home'].probablePitcher.fullName) ) {
2382
+ if ( cache_data.dates[i].games[j].teams['away'].probablePitcher && cache_data.dates[i].games[j].teams['away'].probablePitcher.fullName ) {
2383
+ description += cache_data.dates[i].games[j].teams['away'].probablePitcher.fullName
2384
+ } else {
2385
+ description += 'TBD'
2240
2386
  }
2241
- description += 'In Spanish. From ' + cache_data.dates[i].games[j].teams['home'].team.venue.name + '. '
2242
-
2243
- let gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2244
- let gameHours = 3
2245
- // Handle suspended, TBD, and doubleheaders
2246
- if ( cache_data.dates[i].games[j].status.resumedFrom ) {
2247
- gameHours = 1
2248
- if ( cache_data.dates[i].games[j].description ) {
2249
- description += cache_data.dates[i].games[j].description
2250
- } else {
2251
- description += 'Resumption of suspended game.'
2252
- }
2253
- gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2254
- gameDate.setHours(gameDate.getHours()+1)
2255
- } else if ( (cache_data.dates[i].games[j].status.startTimeTBD == true) && (cache_data.dates[i].games[j].doubleHeader == 'Y') && (cache_data.dates[i].games[j].gameNumber == 2) ) {
2256
- description += 'Start time TBD.'
2257
- gameDate = new Date(cache_data.dates[i].games[j-1].gameDate)
2258
- gameDate.setHours(gameDate.getHours()+4)
2259
- } else if ( cache_data.dates[i].games[j].status.startTimeTBD == true ) {
2260
- continue
2387
+ description += ' vs. '
2388
+ if ( cache_data.dates[i].games[j].teams['home'].probablePitcher && cache_data.dates[i].games[j].teams['home'].probablePitcher.fullName ) {
2389
+ description += cache_data.dates[i].games[j].teams['home'].probablePitcher.fullName
2390
+ } else {
2391
+ description += 'TBD'
2261
2392
  }
2262
- let start = this.convertDateToXMLTV(gameDate)
2263
- gameDate.setHours(gameDate.getHours()+gameHours)
2264
- let stop = this.convertDateToXMLTV(gameDate)
2265
-
2266
- programs += await this.generate_xml_program(channelid, start, stop, title, description, logo)
2393
+ description += '. '
2394
+ }
2267
2395
 
2268
- break
2396
+ let gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2397
+ let gameHours = 3
2398
+ // Handle suspended, TBD, and doubleheaders
2399
+ if ( cache_data.dates[i].games[j].status.resumedFrom ) {
2400
+ gameHours = 1
2401
+ if ( cache_data.dates[i].games[j].description ) {
2402
+ description += cache_data.dates[i].games[j].description
2403
+ } else {
2404
+ description += 'Resumption of suspended game.'
2405
+ }
2406
+ gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2407
+ gameDate.setHours(gameDate.getHours()+1)
2408
+ } else if ( (cache_data.dates[i].games[j].status.startTimeTBD == true) && (cache_data.dates[i].games[j].doubleHeader == 'Y') && (cache_data.dates[i].games[j].gameNumber == 2) ) {
2409
+ description += 'Start time TBD.'
2410
+ gameDate = new Date(cache_data.dates[i].games[j-1].gameDate)
2411
+ gameDate.setHours(gameDate.getHours()+4)
2412
+ } else if ( cache_data.dates[i].games[j].status.startTimeTBD == true ) {
2413
+ continue
2269
2414
  }
2415
+ let start = this.convertDateToXMLTV(gameDate)
2416
+ gameDate.setHours(gameDate.getHours()+gameHours)
2417
+ let stop = this.convertDateToXMLTV(gameDate)
2418
+
2419
+ programs += await this.generate_xml_program(channelid, start, stop, title, description, logo)
2420
+
2421
+ break
2270
2422
  }
2271
2423
  }
2272
2424
  }
@@ -2446,7 +2598,7 @@ class sessionClass {
2446
2598
  channels = Object.assign(channels, nationalChannels)
2447
2599
 
2448
2600
  // Big Inning
2449
- if ( mediaType == 'MLBTV' ) {
2601
+ if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2450
2602
  if ( (excludeTeams.length > 0) && excludeTeams.includes('BIGINNING') ) {
2451
2603
  // do nothing
2452
2604
  } else if ( (includeTeams.length == 0) || includeTeams.includes('BIGINNING') ) {
@@ -2493,7 +2645,7 @@ class sessionClass {
2493
2645
  }
2494
2646
 
2495
2647
  // Game Changer
2496
- if ( mediaType == 'MLBTV' ) {
2648
+ if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2497
2649
  if ( (excludeTeams.length > 0) && excludeTeams.includes('GAMECHANGER') ) {
2498
2650
  // do nothing
2499
2651
  } else if ( (includeTeams.length == 0) || includeTeams.includes('GAMECHANGER') ) {
@@ -2530,7 +2682,7 @@ class sessionClass {
2530
2682
  }
2531
2683
 
2532
2684
  // Multiview
2533
- if ( (mediaType == 'MLBTV') && (typeof this.data.multiviewStreamURLPath !== 'undefined') ) {
2685
+ if ( (mediaType == 'MLBTV') && (typeof this.data.multiviewStreamURLPath !== 'undefined') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2534
2686
  if ( (excludeTeams.length > 0) && excludeTeams.includes('MULTIVIEW') ) {
2535
2687
  // do nothing
2536
2688
  } else if ( (includeTeams.length == 0) || includeTeams.includes('MULTIVIEW') ) {
@@ -3026,7 +3178,8 @@ class sessionClass {
3026
3178
  day = parts[1].substring(0,parts[1].length-3)*/
3027
3179
  // new date format (01/01/70)
3028
3180
  parts = col[0].split('/')
3029
- year = '20' + parts[2]
3181
+ //year = '20' + parts[2]
3182
+ year = parts[2]
3030
3183
  // get month index, zero-based
3031
3184
  month = parseInt(parts[0]) - 1
3032
3185
  day = parts[1]
@@ -3366,6 +3519,7 @@ class sessionClass {
3366
3519
  this.log('getBlackoutTeams for zip code ' + this.credentials.zip_code)
3367
3520
  let reqObj = {
3368
3521
  url: 'https://content.mlb.com/data/blackouts/' + this.credentials.zip_code + '.json',
3522
+ rejectUnauthorized: false,
3369
3523
  headers: {
3370
3524
  'User-Agent': USER_AGENT,
3371
3525
  'Origin': 'https://www.mlb.com',
@@ -3404,13 +3558,16 @@ class sessionClass {
3404
3558
  for (var k = 0; k < games[j].content.media.epg.length; k++) {
3405
3559
  if ( games[j].content.media.epg[k].title == 'MLBTV' ) {
3406
3560
  for (var x = 0; x < games[j].content.media.epg[k].items.length; x++) {
3561
+ this.debuglog('check_regional_fox_games checking ' + games[j].content.media.epg[k].items[x].callLetters)
3407
3562
  if ( games[j].content.media.epg[k].items[x].callLetters == 'FOX' ) {
3563
+ this.debuglog('check_regional_fox_games found FOX game')
3408
3564
  if ( fox_start_time && (games[j].gameDate == fox_start_time) ) {
3409
- this.debuglog('check_regional_fox_games found')
3565
+ this.debuglog('check_regional_fox_games determined regional game')
3410
3566
  regional_fox_games_exist = 'true'
3411
3567
  break
3412
3568
  } else {
3413
3569
  fox_start_time = games[j].gameDate
3570
+ break
3414
3571
  }
3415
3572
  }
3416
3573
  }
@@ -3424,6 +3581,7 @@ class sessionClass {
3424
3581
 
3425
3582
  // get all blackout games for a date
3426
3583
  async get_blackout_games(games, calculate_expiries=false) {
3584
+ this.debuglog('get_blackout_games')
3427
3585
  let blackouts = {}
3428
3586
 
3429
3587
  let usa_blackout = /(^\d{5}$)/.test(this.credentials.zip_code) && (this.credentials.country == 'USA')
@@ -3434,14 +3592,18 @@ class sessionClass {
3434
3592
  if ( games[j].content && games[j].content.media && games[j].content.media.epg ) {
3435
3593
  for (var k = 0; k < games[j].content.media.epg.length; k++) {
3436
3594
  if ( games[j].content.media.epg[k].title == 'MLBTV' ) {
3595
+ this.debuglog('get_blackout_games checking ' + game_pk)
3437
3596
  for (var x = 0; x < games[j].content.media.epg[k].items.length; x++) {
3438
- if (games[j].content.media.epg[k].items[x].mediaFeedType == 'NATIONAL') {
3597
+ this.debuglog('get_blackout_games checking feed ' + games[j].content.media.epg[k].items[x].mediaFeedType + ' ' + games[j].content.media.epg[k].items[x].callLetters)
3598
+ if ( (games[j].content.media.epg[k].items[x].mediaFeedType == 'NATIONAL') || (await this.check_pay_tv(games[j].content.media.epg[k].items[x])) ) {
3599
+ this.debuglog('get_blackout_games checking national game')
3439
3600
  // International blackouts according to https://www.mlb.com/live-stream-games/help-center/blackouts-available-games
3440
- if ( usa_blackout && (games[j].content.media.epg[k].items[x].callLetters == 'FOX') && (games[j].seriesDescription == 'Regular Season') ) {
3441
- if ( !regional_fox_games_exist ) {
3601
+ if ( usa_blackout && (games[j].content.media.epg[k].items[x].callLetters == 'FOX') ) {
3602
+ if ( !regional_fox_games_exist && (games[j].seriesDescription == 'Regular Season') ) {
3442
3603
  regional_fox_games_exist = await this.check_regional_fox_games(games)
3443
3604
  }
3444
- if ( regional_fox_games_exist == 'false' ) {
3605
+ if ( !regional_fox_games_exist || (regional_fox_games_exist == 'false') ) {
3606
+ this.debuglog('get_blackout_games found non-regional FOX game')
3445
3607
  blackouts[game_pk] = { blackout_type:'National' }
3446
3608
  break
3447
3609
  }
@@ -4023,7 +4185,7 @@ class sessionClass {
4023
4185
 
4024
4186
  async check_pay_tv(item) {
4025
4187
  try {
4026
- if ( item.foxAuthRequired || item.tbsAuthRequired || item.espnAuthRequired || item.fs1AuthRequired || item.mlbnAuthRequired ) {
4188
+ if ( item.foxAuthRequired || item.tbsAuthRequired || item.espnAuthRequired || item.espn2AuthRequired || item.fs1AuthRequired || item.mlbnAuthRequired || item.abcAuthRequired ) {
4027
4189
  return true
4028
4190
  }
4029
4191
  } catch (e) {
@@ -4034,7 +4196,7 @@ class sessionClass {
4034
4196
 
4035
4197
  async get_scheduled_innings(game) {
4036
4198
  var scheduledInnings = '9'
4037
- if ( game.linescore && game.linescore.scheduledInnings ) {
4199
+ if ( game && game.linescore && game.linescore.scheduledInnings ) {
4038
4200
  scheduledInnings = game.linescore.scheduledInnings
4039
4201
  if ( (game.status.abstractGameState == 'Final') && game.linescore.currentInning && (game.linescore.currentInning < 9) ) {
4040
4202
  scheduledInnings = game.linescore.currentInning