mlbserver 2023.5.25 → 2023.7.10

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 +74 -31
  3. package/package.json +1 -1
  4. package/session.js +266 -126
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mlbserver
2
2
 
3
- Current version 2023.05.25
3
+ Current version 2023.07.10
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
 
@@ -317,11 +315,16 @@ app.get('/stream.m3u8', async function(req, res) {
317
315
  let mediaType = req.query.mediaType || VALID_MEDIA_TYPES[0]
318
316
  let mediaInfo = await session.getMediaId(decodeURIComponent(req.query.team), mediaType, req.query.date, req.query.game, includeBlackouts)
319
317
  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
318
+
319
+ if ( mediaInfo.gamePk ) {
320
+ streamURL = await session.getEventStreamURL(false, mediaInfo.gamePk)
321
+ } else {
322
+ mediaId = mediaInfo.mediaId
323
+ contentId = mediaInfo.contentId
324
+ if ( mediaInfo.alternateAudioTracks ) {
325
+ for (const [key, value] of Object.entries(mediaInfo.alternateAudioTracks)) {
326
+ options.alternate_audio_tracks[key] = value
327
+ }
325
328
  }
326
329
  }
327
330
  } else {
@@ -329,13 +332,15 @@ app.get('/stream.m3u8', async function(req, res) {
329
332
  }
330
333
  }
331
334
 
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)
335
+ if ( !streamURL ) {
336
+ if ( !mediaId ) {
337
+ session.log('failed to get mediaId : ' + req.url)
338
+ res.end('')
339
+ return
340
+ } else {
341
+ session.debuglog('mediaId : ' + mediaId)
342
+ streamURL = await session.getStreamURL(mediaId)
343
+ }
339
344
  }
340
345
  }
341
346
  }
@@ -1264,20 +1269,18 @@ app.get('/', async function(req, res) {
1264
1269
  var team_ids = ''
1265
1270
  if ( req.query.org ) {
1266
1271
  org = decodeURIComponent(req.query.org)
1267
- if ( typeof AFFILIATE_TEAM_IDS[org] === 'undefined' ) {
1272
+ if ( typeof session.getAffiliateTeamIds(org) === 'undefined' ) {
1268
1273
  org = default_org
1269
1274
  } else {
1270
- team_ids += session.getTeamIds(org) + ',' + AFFILIATE_TEAM_IDS[org]
1275
+ team_ids += session.getTeamIds(org) + ',' + session.getAffiliateTeamIds(org)
1271
1276
  level = default_org
1272
1277
  }
1273
- } else if ( level_ids == '1' ) {
1278
+ } else if ( level_ids == levels['MLB'] ) {
1274
1279
  team_ids = session.getTeamIds()
1275
1280
  for (let i=0; i<session.credentials.fav_teams.length; i++) {
1276
1281
  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]]
1282
+ level_ids = levels['All']
1283
+ team_ids += ',' + session.getAffiliateTeamIds(session.credentials.fav_teams[i])
1281
1284
  }
1282
1285
  }
1283
1286
  }
@@ -1408,10 +1411,11 @@ app.get('/', async function(req, res) {
1408
1411
  body += ' or <span class="tooltip">Org<span class="tooltiptext">Major league parent organization</span></span>: '
1409
1412
  body += '<select id="org" onchange="level=\'' + default_org + '\';org=this.value;reload()">'
1410
1413
  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> '
1414
+ var orgs = session.getOrgs()
1415
+ for (var i = 0; i < orgs.length; i++) {
1416
+ body += '<option value="' + orgs[i] + '"'
1417
+ if ( org == orgs[i] ) body += ' selected'
1418
+ body += '>' + orgs[i] + '</option> '
1415
1419
  }
1416
1420
  body += '</select></p>' + "\n"
1417
1421
 
@@ -1599,15 +1603,30 @@ app.get('/', async function(req, res) {
1599
1603
  let game_started = false
1600
1604
 
1601
1605
  let awayteam = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1606
+ let awayteam_abbr
1602
1607
  if ( cache_data.dates[0].games[j].teams['away'].team.sport.name != 'Major League Baseball' ) {
1603
1608
  awayteam = cache_data.dates[0].games[j].teams['away'].team.shortName + ' (' + session.getParent(cache_data.dates[0].games[j].teams['away'].team.parentOrgName) + ')'
1609
+ awayteam_abbr = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1604
1610
  }
1605
1611
  let hometeam = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1612
+ let hometeam_abbr
1606
1613
  if ( cache_data.dates[0].games[j].teams['home'].team.sport.name != 'Major League Baseball' ) {
1607
1614
  hometeam = cache_data.dates[0].games[j].teams['home'].team.shortName + ' (' + session.getParent(cache_data.dates[0].games[j].teams['home'].team.parentOrgName) + ')'
1615
+ hometeam_abbr = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1608
1616
  }
1609
1617
 
1610
- let teams = awayteam + " @ " + hometeam
1618
+ let teams = ""
1619
+ if ( awayteam_abbr ) {
1620
+ teams += '<span class="tooltip">' + awayteam + '<span class="tooltiptext">Team Abbreviation: ' + awayteam_abbr + '</span></span>'
1621
+ } else {
1622
+ teams += awayteam
1623
+ }
1624
+ teams += " @ "
1625
+ if ( hometeam_abbr ) {
1626
+ teams += '<span class="tooltip">' + hometeam + '<span class="tooltiptext">Team Abbreviation: ' + hometeam_abbr+ '</span></span>'
1627
+ } else {
1628
+ teams += hometeam
1629
+ }
1611
1630
  let pitchers = ""
1612
1631
  let state = "<br/>"
1613
1632
 
@@ -2088,13 +2107,13 @@ app.get('/', async function(req, res) {
2088
2107
  resolution = 'best'
2089
2108
  }
2090
2109
 
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"
2110
+ 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
2111
 
2093
2112
  let include_teams = 'ari,national'
2094
2113
  if ( session.credentials.fav_teams.length > 0 ) {
2095
2114
  include_teams = session.credentials.fav_teams.toString()
2096
2115
  }
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"
2116
+ 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
2117
 
2099
2118
  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
2119
 
@@ -2103,7 +2122,7 @@ app.get('/', async function(req, res) {
2103
2122
  exclude_teams = session.credentials.blackout_teams.toString()
2104
2123
  exclude_teams += ',national'
2105
2124
  }
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"
2125
+ 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
2126
 
2108
2127
  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
2128
 
@@ -2121,6 +2140,10 @@ app.get('/', async function(req, res) {
2121
2140
  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
2141
  }
2123
2142
 
2143
+ 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"
2144
+
2145
+ 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"
2146
+
2124
2147
  body += '</td></tr></table><br/>' + "\n"
2125
2148
 
2126
2149
  body += '<table><tr><td>' + "\n"
@@ -2419,7 +2442,17 @@ app.get('/channels.m3u', async function(req, res) {
2419
2442
  includeBlackouts = req.query.includeBlackouts
2420
2443
  }
2421
2444
 
2422
- var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, server, includeBlackouts, resolution, pipe, startingChannelNumber)
2445
+ let includeLevels = []
2446
+ if ( req.query.includeLevels ) {
2447
+ includeLevels = decodeURIComponent(req.query.includeLevels.toUpperCase()).split(',')
2448
+ }
2449
+
2450
+ let includeOrgs = []
2451
+ if ( req.query.includeOrgs ) {
2452
+ includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
2453
+ }
2454
+
2455
+ var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution, pipe, startingChannelNumber)
2423
2456
 
2424
2457
  res.writeHead(200, {'Content-Type': 'audio/x-mpegurl'})
2425
2458
  res.end(body)
@@ -2450,9 +2483,19 @@ app.get('/guide.xml', async function(req, res) {
2450
2483
  includeBlackouts = req.query.includeBlackouts
2451
2484
  }
2452
2485
 
2486
+ let includeLevels = []
2487
+ if ( req.query.includeLevels ) {
2488
+ includeLevels = decodeURIComponent(req.query.includeLevels.toUpperCase()).split(',')
2489
+ }
2490
+
2491
+ let includeOrgs = []
2492
+ if ( req.query.includeOrgs ) {
2493
+ includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
2494
+ }
2495
+
2453
2496
  let server = 'http://' + req.headers.host
2454
2497
 
2455
- var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, server, includeBlackouts)
2498
+ var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts)
2456
2499
 
2457
2500
  res.end(body)
2458
2501
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlbserver",
3
- "version": "2023.05.25",
3
+ "version": "2023.07.10",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
package/session.js CHANGED
@@ -25,11 +25,13 @@ 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
 
31
33
  // First is default level, last should be All (also used as default org)
32
- const LEVELS = { 'MLB': '1', 'AAA': '11', 'AA': '12', 'A+': '13', 'A': '14', 'All': '1,11,12,13,14' }
34
+ const LEVELS = { 'MLB': '1,51', 'AAA': '11', 'AA': '12', 'A+': '13', 'A': '14', 'All': '1,11,12,13,14,51' }
33
35
 
34
36
  // These are the events to ignore, if we're skipping breaks
35
37
  const BREAK_TYPES = ['Game Advisory', 'Pitching Substitution', 'Offensive Substitution', 'Defensive Sub', 'Defensive Switch', 'Runner Placed On Base']
@@ -1110,10 +1112,22 @@ class sessionClass {
1110
1112
  if ( team_abbr ) {
1111
1113
  return TEAM_IDS[team_abbr]
1112
1114
  } else {
1113
- return Object.values(TEAM_IDS).toString()
1115
+ return Object.values(TEAM_IDS).toString() + ',159,160'
1114
1116
  }
1115
1117
  }
1116
1118
 
1119
+ getAffiliateTeamIds(team_abbr = false) {
1120
+ if ( team_abbr ) {
1121
+ return AFFILIATE_TEAM_IDS[team_abbr]
1122
+ } else {
1123
+ return Object.values(AFFILIATE_TEAM_IDS).toString()
1124
+ }
1125
+ }
1126
+
1127
+ getOrgs() {
1128
+ return Object.keys(AFFILIATE_TEAM_IDS)
1129
+ }
1130
+
1117
1131
  // get parent org nickname
1118
1132
  getParent(parent) {
1119
1133
  let long_orgs = [ 'Jays', 'Sox' ]
@@ -1179,6 +1193,17 @@ class sessionClass {
1179
1193
  this.save_cache_data()
1180
1194
  }
1181
1195
 
1196
+ setWeekCacheExpiry(cache_name, expiryDate) {
1197
+ if ( !this.cache.weeks ) {
1198
+ this.cache.weeks={}
1199
+ }
1200
+ if ( !this.cache.weeks[cache_name] ) {
1201
+ this.cache.weeks[cache_name] = {}
1202
+ }
1203
+ this.cache.weeks[cache_name].weekCacheExpiry = expiryDate
1204
+ this.save_cache_data()
1205
+ }
1206
+
1182
1207
  setAiringsCacheExpiry(cache_name, expiryDate) {
1183
1208
  if ( !this.cache.airings ) {
1184
1209
  this.cache.airings={}
@@ -1820,100 +1845,134 @@ class sessionClass {
1820
1845
  if ( includeBlackouts == 'false' ) blackouts = await this.get_blackout_games(cache_data.dates[0].games, true)
1821
1846
 
1822
1847
  for (var j = 0; j < cache_data.dates[0].games.length; j++) {
1823
- if ( mediaInfo.mediaId ) break
1848
+ if ( mediaInfo.mediaId || mediaInfo.gamePk ) break
1824
1849
 
1825
- // check blackout status, if necessary
1826
1850
  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
1851
 
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 ) {
1852
+ if ( typeof cache_data.dates[0].games[j] !== 'undefined' ) {
1835
1853
  let home_team = cache_data.dates[0].games[j].teams['home'].team.abbreviation
1836
1854
  let away_team = cache_data.dates[0].games[j].teams['away'].team.abbreviation
1855
+ 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
1856
 
1838
1857
  // check that that game involves the requested team, or if it's a national or free game and we've requested that
1839
1858
  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
1859
+ 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)
1860
+
1861
+ // Check if Winter League / MiLB game first
1862
+ if ( (cache_data.dates[0].games[j].teams['home'].team.sport.id != '1') && (mediaType == 'MLBTV') ) {
1863
+ if ( cache_data.dates[0].games[j].broadcasts ) {
1864
+ let broadcastName = 'N/A'
1865
+ for (var k = 0; k < cache_data.dates[0].games[j].broadcasts.length; k++) {
1866
+ if ( cache_data.dates[0].games[j].broadcasts[k].name != 'Audio' ) {
1867
+ broadcastName = mediaType
1868
+ break
1854
1869
  }
1870
+ }
1871
+ if ( broadcastName == 'N/A' ) {
1872
+ break
1873
+ } else {
1874
+ if ( gameNumber && (gameNumber > 1) ) {
1875
+ this.debuglog('matched team for MILB game number 1')
1876
+ gameNumber--
1877
+ } else {
1878
+ this.debuglog('matched team for MILB event')
1879
+ mediaInfo.gamePk = gamePk
1880
+ break
1881
+ }
1882
+ }
1883
+ }
1884
+ break
1885
+ }
1855
1886
 
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]
1887
+ // MLB games
1888
+ 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
1889
 
1859
- let station = cache_data.dates[0].games[j].content.media.epg[k].items[x].callLetters
1890
+ // check blackout status, if necessary
1891
+ if ( (mediaType == 'MLBTV') && (includeBlackouts == 'false') && blackouts[gamePk] ) {
1892
+ if ( !blackouts[gamePk].blackoutExpiry || (currentDate < blackouts[gamePk].blackoutExpiry) ) {
1893
+ this.debuglog('getMediaId requested game is blacked out')
1894
+ continue
1895
+ }
1896
+ }
1860
1897
 
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'))) ) {
1898
+ let teamType
1899
+ let broadcast_count = 0
1900
+ for (var k = 0; k < cache_data.dates[0].games[j].content.media.epg.length; k++) {
1901
+ if ( mediaInfo.mediaId ) break
1902
+ let mediaTitle = cache_data.dates[0].games[j].content.media.epg[k].title
1903
+ if ( mediaType == mediaTitle ) {
1904
+ // initial loop will count number of broadcasts
1905
+ broadcast_count = await this.count_broadcasts(cache_data.dates[0].games[j].content.media.epg[k].items, mediaType, mediaTitle, language)
1863
1906
 
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
- }
1907
+ for (var x = 0; x < cache_data.dates[0].games[j].content.media.epg[k].items.length; x++) {
1908
+ // for video, check that it's not in-market
1909
+ if ( (mediaType == 'MLBTV') && await this.check_in_market(cache_data.dates[0].games[j].content.media.epg[k].items[x]) ) {
1910
+ continue
1911
+ }
1871
1912
 
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 ) {
1913
+ 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) ) {
1914
+ teamType = cache_data.dates[0].games[j].content.media.epg[k].items[x][mediaFeedType]
1874
1915
 
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
- }
1916
+ let station = cache_data.dates[0].games[j].content.media.epg[k].items[x].callLetters
1882
1917
 
1883
- // process requested team games
1884
- } else if ( (team.toUpperCase() == home_team) || (team.toUpperCase() == away_team) ) {
1918
+ // process requested national games (with the team/channel NATIONAL.x)
1919
+ 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
1920
 
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')
1921
+ nationalCount += 1
1922
+ let nationalArray = team.split('.')
1923
+ if ( (nationalArray.length == 2) && (nationalArray[1] == nationalCount) ) {
1924
+ this.debuglog('matched national event')
1892
1925
  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
1926
  break
1894
1927
  }
1895
- }
1896
1928
 
1929
+ // process requested free games (with the team/channel FREE.x)
1930
+ } else if ( team.toUpperCase().startsWith('FREE.') && cache_data.dates[0].games[j].content.media.freeGame ) {
1931
+
1932
+ freeCount += 1
1933
+ let freeArray = team.split('.')
1934
+ if ( (freeArray.length == 2) && (freeArray[1] == freeCount) ) {
1935
+ this.debuglog('matched free event')
1936
+ 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)
1937
+ break
1938
+ }
1939
+
1940
+ // process requested team games
1941
+ } else if ( (team.toUpperCase() == home_team) || (team.toUpperCase() == away_team) ) {
1942
+
1943
+ if ( ((team.toUpperCase() == home_team) && (teamType == 'HOME')) || ((team.toUpperCase() == away_team) && (teamType == 'AWAY')) || (broadcast_count == 1) ) {
1944
+ if ( gameNumber && (gameNumber > 1) ) {
1945
+ this.debuglog('matched team for game number 1')
1946
+ gameNumber--
1947
+ } else {
1948
+ this.debuglog('matched team for event')
1949
+ 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)
1950
+ break
1951
+ }
1952
+ }
1953
+
1954
+ }
1897
1955
  }
1898
1956
  }
1899
1957
  }
1900
1958
  }
1901
- }
1902
1959
 
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)
1960
+ // grab any alternate audio tracks, if necessary
1961
+ if ( mediaInfo.mediaId && (mediaType == 'MLBTV') && (broadcast_count == 1) ) {
1962
+ mediaInfo.alternateAudioTracks = await this.getAlternateAudioTracks(cache_data.dates[0].games[j].content.media.epg, teamType)
1963
+ }
1964
+
1906
1965
  }
1907
1966
 
1908
1967
  }
1909
1968
  }
1910
1969
  }
1911
1970
 
1912
- if (mediaInfo.mediaId) {
1971
+ if (mediaInfo.mediaId || mediaInfo.gamePk) {
1913
1972
  return mediaInfo
1914
1973
  }
1915
1974
  }
1916
- this.log('could not find mediaId')
1975
+ this.log('could not find mediaId or gamePk')
1917
1976
  } else {
1918
1977
  this.log('will not find mediaId for future date')
1919
1978
  }
@@ -2012,7 +2071,7 @@ class sessionClass {
2012
2071
  }
2013
2072
 
2014
2073
  // get data for a day, either from cache or an API call
2015
- async getDayData(dateString, team = false, level_ids = LEVELS.MLB, team_ids = '') {
2074
+ async getDayData(dateString, team = false, level_ids = LEVELS.All, team_ids = '') {
2016
2075
  try {
2017
2076
  let cache_data
2018
2077
  let cache_name = dateString
@@ -2109,7 +2168,7 @@ class sessionClass {
2109
2168
  }
2110
2169
 
2111
2170
  // get data for 3 weeks, either from cache or an API call
2112
- async getWeeksData() {
2171
+ async getWeeksData(level_ids, team_ids) {
2113
2172
  try {
2114
2173
  this.debuglog('getWeeksData')
2115
2174
 
@@ -2118,15 +2177,29 @@ class sessionClass {
2118
2177
 
2119
2178
  let cache_data
2120
2179
  let cache_name = 'week'
2180
+
2181
+ if ( level_ids != LEVELS['All'] ) {
2182
+ cache_name += '.' + level_ids
2183
+ }
2184
+ if ( team_ids != '' ) {
2185
+ cache_name += '.' + team_ids
2186
+ }
2187
+
2121
2188
  let cache_file = path.join(this.CACHE_DIRECTORY, cache_name + '.json')
2122
2189
  let currentDate = new Date()
2123
- if ( !fs.existsSync(cache_file) || !this.cache || !this.cache.weekCacheExpiry || (currentDate > new Date(this.cache.weekCacheExpiry)) ) {
2190
+ 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
2191
  let startDate = this.liveDate(utcHours)
2125
2192
  let endDate = new Date(startDate)
2126
2193
  endDate.setDate(endDate.getDate()+20)
2127
2194
  endDate = endDate.toISOString().substring(0,10)
2195
+ let data_url = 'http://statsapi.mlb.com/api/v1/schedule?sportId=' + level_ids
2196
+ if ( team_ids != '' ) {
2197
+ data_url += '&teamId=' + team_ids
2198
+ }
2199
+ data_url += '&startDate=' + startDate + '&endDate=' + endDate + '&hydrate=broadcasts(all),game(content(media(epg))),probablePitcher,linescore,team'
2128
2200
  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=',
2201
+ //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=',
2202
+ url: data_url,
2130
2203
  headers: {
2131
2204
  'User-agent': USER_AGENT,
2132
2205
  'Origin': 'https://www.mlb.com',
@@ -2140,17 +2213,18 @@ class sessionClass {
2140
2213
  //this.debuglog(response)
2141
2214
  cache_data = JSON.parse(response)
2142
2215
  this.save_json_cache_file(cache_name, cache_data)
2216
+
2143
2217
  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()
2218
+ let cacheExpiry = new Date(startDate)
2219
+ cacheExpiry.setDate(cacheExpiry.getDate()+1)
2220
+ cacheExpiry.setHours(cacheExpiry.getHours()+utcHours)
2221
+ // finally save the setting
2222
+ this.setWeekCacheExpiry(cache_name, cacheExpiry)
2149
2223
  } else {
2150
2224
  this.log('error : invalid json from url ' + reqObj.url)
2151
2225
  }
2152
2226
  } else {
2153
- this.debuglog('using cached channel data')
2227
+ this.debuglog('using cached week data')
2154
2228
  cache_data = this.readFileToJson(cache_file)
2155
2229
  }
2156
2230
  if (cache_data) {
@@ -2162,7 +2236,7 @@ class sessionClass {
2162
2236
  }
2163
2237
 
2164
2238
  // get TV data (channels or guide)
2165
- async getTVData(dataType, mediaType, includeTeams, excludeTeams, server, includeBlackouts, resolution='best', pipe='false', startingChannelNumber=1) {
2239
+ async getTVData(dataType, mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution='best', pipe='false', startingChannelNumber=1) {
2166
2240
  try {
2167
2241
  this.debuglog('getTVData for ' + dataType)
2168
2242
 
@@ -2194,7 +2268,40 @@ class sessionClass {
2194
2268
  }
2195
2269
  }
2196
2270
 
2197
- let cache_data = await this.getWeeksData()
2271
+ var level_ids = LEVELS['All']
2272
+ var team_ids = ''
2273
+ if ( includeLevels.length > 0 ) {
2274
+ if ( (includeLevels.length > 1) || !includeLevels.includes('ALL') ) {
2275
+ let level_list = []
2276
+ for (let i = 0; i < includeLevels.length; i++) {
2277
+ level_list.append(LEVELS[includeLevels[i]])
2278
+ }
2279
+ level_ids = level_list.toString()
2280
+ }
2281
+ } else {
2282
+ if ( includeOrgs.length > 0 ) {
2283
+ team_ids = this.getTeamIds()
2284
+ for (let i = 0; i < includeOrgs.length; i++) {
2285
+ team_ids += ',' + AFFILIATE_TEAM_IDS[includeOrgs[i]]
2286
+ }
2287
+ } else if ( includeTeams.length > 0 ) {
2288
+ team_ids = this.getTeamIds()
2289
+ for (let i=0; i<includeTeams.length; i++) {
2290
+ if ( includeTeams[i] != '' ) {
2291
+ team_ids += ',' + AFFILIATE_TEAM_IDS[includeTeams[i]]
2292
+ }
2293
+ }
2294
+ } else {
2295
+ team_ids = this.getTeamIds()
2296
+ for (let i=0; i<this.credentials.fav_teams.length; i++) {
2297
+ if ( this.credentials.fav_teams[i] != '' ) {
2298
+ team_ids += ',' + AFFILIATE_TEAM_IDS[this.credentials.fav_teams[i]]
2299
+ }
2300
+ }
2301
+ }
2302
+ }
2303
+
2304
+ let cache_data = await this.getWeeksData(level_ids, team_ids)
2198
2305
  if (cache_data) {
2199
2306
  let today = this.liveDate()
2200
2307
  var nationalChannels = {}
@@ -2214,59 +2321,90 @@ class sessionClass {
2214
2321
 
2215
2322
  for (var j = 0; j < cache_data.dates[i].games.length; j++) {
2216
2323
  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 ) {
2324
+ // Check if Winter League / MiLB game first
2325
+ if ( (cache_data.dates[i].games[j].teams['home'].team.sport.id != '1') && (mediaType == 'MLBTV') ) {
2326
+ if ( cache_data.dates[i].games[j].broadcasts ) {
2327
+ let broadcastName = 'N/A'
2328
+ for (var k = 0; k < cache_data.dates[i].games[j].broadcasts.length; k++) {
2329
+ if ( cache_data.dates[i].games[j].broadcasts[k].name != 'Audio' ) {
2330
+ broadcastName = mediaType
2331
+ break
2332
+ }
2333
+ }
2334
+ if ( broadcastName == 'N/A' ) {
2335
+ continue
2336
+ } else {
2223
2337
  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 + '. '
2338
+ let team = cache_data.dates[i].games[j].teams['home'].team.abbreviation
2339
+ let team_id = cache_data.dates[i].games[j].teams['home'].team.id
2340
+ let opponent_team_id = cache_data.dates[i].games[j].teams['away'].team.id
2341
+ if ( team_ids.includes(opponent_team_id) && !team_ids.includes(team_id) ) {
2342
+ team_id = opponent_team_id
2343
+ team = cache_data.dates[i].games[j].teams['away'].team.abbreviation
2344
+ }
2345
+ let channelid = mediaType + '.' + team
2346
+ let logo = server + '/image.svg?teamId=' + team_id
2347
+ let streamMediaType = 'Video'
2348
+ let stream = server + '/stream.m3u8?team=' + encodeURIComponent(team) + '&mediaType=' + streamMediaType
2349
+ stream += '&resolution=' + resolution
2350
+ if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
2351
+ if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
2352
+ channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
2353
+
2354
+ 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
2355
+
2356
+ let description = ''
2357
+ if ( cache_data.dates[i].games[j].seriesDescription != 'Regular Season' ) {
2358
+ description += cache_data.dates[i].games[j].seriesDescription + '. '
2359
+ }
2360
+ if ( cache_data.dates[i].games[j].doubleHeader != 'N' ) {
2361
+ description += 'Doubleheader game ' + cache_data.dates[i].games[j].gameNumber + '. '
2362
+ }
2363
+ var scheduledInnings = await this.get_scheduled_innings(cache_data.dates[0].games[j])
2364
+ if ( scheduledInnings != '9' ) {
2365
+ description += scheduledInnings + '-inning game. '
2366
+ }
2367
+ 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) ) {
2368
+ if ( cache_data.dates[i].games[j].teams['away'].probablePitcher && cache_data.dates[i].games[j].teams['away'].probablePitcher.fullName ) {
2369
+ description += cache_data.dates[i].games[j].teams['away'].probablePitcher.fullName
2370
+ } else {
2371
+ description += 'TBD'
2240
2372
  }
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
2373
+ description += ' vs. '
2374
+ if ( cache_data.dates[i].games[j].teams['home'].probablePitcher && cache_data.dates[i].games[j].teams['home'].probablePitcher.fullName ) {
2375
+ description += cache_data.dates[i].games[j].teams['home'].probablePitcher.fullName
2376
+ } else {
2377
+ description += 'TBD'
2261
2378
  }
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)
2379
+ description += '. '
2380
+ }
2267
2381
 
2268
- break
2382
+ let gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2383
+ let gameHours = 3
2384
+ // Handle suspended, TBD, and doubleheaders
2385
+ if ( cache_data.dates[i].games[j].status.resumedFrom ) {
2386
+ gameHours = 1
2387
+ if ( cache_data.dates[i].games[j].description ) {
2388
+ description += cache_data.dates[i].games[j].description
2389
+ } else {
2390
+ description += 'Resumption of suspended game.'
2391
+ }
2392
+ gameDate = new Date(cache_data.dates[i].games[j].gameDate)
2393
+ gameDate.setHours(gameDate.getHours()+1)
2394
+ } 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) ) {
2395
+ description += 'Start time TBD.'
2396
+ gameDate = new Date(cache_data.dates[i].games[j-1].gameDate)
2397
+ gameDate.setHours(gameDate.getHours()+4)
2398
+ } else if ( cache_data.dates[i].games[j].status.startTimeTBD == true ) {
2399
+ continue
2269
2400
  }
2401
+ let start = this.convertDateToXMLTV(gameDate)
2402
+ gameDate.setHours(gameDate.getHours()+gameHours)
2403
+ let stop = this.convertDateToXMLTV(gameDate)
2404
+
2405
+ programs += await this.generate_xml_program(channelid, start, stop, title, description, logo)
2406
+
2407
+ break
2270
2408
  }
2271
2409
  }
2272
2410
  }
@@ -2446,7 +2584,7 @@ class sessionClass {
2446
2584
  channels = Object.assign(channels, nationalChannels)
2447
2585
 
2448
2586
  // Big Inning
2449
- if ( mediaType == 'MLBTV' ) {
2587
+ if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2450
2588
  if ( (excludeTeams.length > 0) && excludeTeams.includes('BIGINNING') ) {
2451
2589
  // do nothing
2452
2590
  } else if ( (includeTeams.length == 0) || includeTeams.includes('BIGINNING') ) {
@@ -2493,7 +2631,7 @@ class sessionClass {
2493
2631
  }
2494
2632
 
2495
2633
  // Game Changer
2496
- if ( mediaType == 'MLBTV' ) {
2634
+ if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2497
2635
  if ( (excludeTeams.length > 0) && excludeTeams.includes('GAMECHANGER') ) {
2498
2636
  // do nothing
2499
2637
  } else if ( (includeTeams.length == 0) || includeTeams.includes('GAMECHANGER') ) {
@@ -2530,7 +2668,7 @@ class sessionClass {
2530
2668
  }
2531
2669
 
2532
2670
  // Multiview
2533
- if ( (mediaType == 'MLBTV') && (typeof this.data.multiviewStreamURLPath !== 'undefined') ) {
2671
+ if ( (mediaType == 'MLBTV') && (typeof this.data.multiviewStreamURLPath !== 'undefined') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
2534
2672
  if ( (excludeTeams.length > 0) && excludeTeams.includes('MULTIVIEW') ) {
2535
2673
  // do nothing
2536
2674
  } else if ( (includeTeams.length == 0) || includeTeams.includes('MULTIVIEW') ) {
@@ -3026,7 +3164,8 @@ class sessionClass {
3026
3164
  day = parts[1].substring(0,parts[1].length-3)*/
3027
3165
  // new date format (01/01/70)
3028
3166
  parts = col[0].split('/')
3029
- year = '20' + parts[2]
3167
+ //year = '20' + parts[2]
3168
+ year = parts[2]
3030
3169
  // get month index, zero-based
3031
3170
  month = parseInt(parts[0]) - 1
3032
3171
  day = parts[1]
@@ -3366,6 +3505,7 @@ class sessionClass {
3366
3505
  this.log('getBlackoutTeams for zip code ' + this.credentials.zip_code)
3367
3506
  let reqObj = {
3368
3507
  url: 'https://content.mlb.com/data/blackouts/' + this.credentials.zip_code + '.json',
3508
+ rejectUnauthorized: false,
3369
3509
  headers: {
3370
3510
  'User-Agent': USER_AGENT,
3371
3511
  'Origin': 'https://www.mlb.com',
@@ -4034,7 +4174,7 @@ class sessionClass {
4034
4174
 
4035
4175
  async get_scheduled_innings(game) {
4036
4176
  var scheduledInnings = '9'
4037
- if ( game.linescore && game.linescore.scheduledInnings ) {
4177
+ if ( game && game.linescore && game.linescore.scheduledInnings ) {
4038
4178
  scheduledInnings = game.linescore.scheduledInnings
4039
4179
  if ( (game.status.abstractGameState == 'Final') && game.linescore.currentInning && (game.linescore.currentInning < 9) ) {
4040
4180
  scheduledInnings = game.linescore.currentInning