mlbserver 2026.3.15 → 2026.3.27-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/Dockerfile +40 -5
  2. package/index.js +22 -9
  3. package/package.json +2 -1
  4. package/session.js +168 -53
package/Dockerfile CHANGED
@@ -1,13 +1,24 @@
1
- FROM node:16-alpine
1
+ # --- Build Stage ---
2
+ FROM node:18-alpine AS build
2
3
 
3
- RUN apk update && apk add tzdata
4
+ # Set environment variable to skip the automatic Chromium download by Puppeteer
5
+ ENV PUPPETEER_SKIP_DOWNLOAD=true
6
+ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
7
+
8
+ # Install system-level dependencies for Chromium on Alpine
9
+ RUN apk update && apk add --no-cache \
10
+ tzdata \
11
+ udev \
12
+ ttf-freefont \
13
+ chromium \
14
+ nss \
15
+ freetype \
16
+ harfbuzz \
17
+ ca-certificates
4
18
 
5
19
  # Create app directory
6
20
  WORKDIR /mlbserver
7
21
 
8
- # Add data directory
9
- VOLUME /mlbserver/data_directory
10
-
11
22
  # Install app dependencies
12
23
  # A wildcard is used to ensure both package.json AND package-lock.json are copied
13
24
  # where available (npm@5+)
@@ -20,5 +31,29 @@ RUN npm install
20
31
  # Bundle app source
21
32
  COPY . .
22
33
 
34
+ # --- Runtime Stage ---
35
+ FROM node:20-alpine AS runtime
36
+
37
+ # Install only the necessary runtime dependencies again
38
+ RUN apk add --no-cache \
39
+ tzdata \
40
+ udev \
41
+ ttf-freefont \
42
+ chromium \
43
+ nss \
44
+ freetype \
45
+ harfbuzz \
46
+ ca-certificates
47
+
48
+ # Set the executable path for Puppeteer
49
+ ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
50
+
51
+ WORKDIR /mlbserver
52
+ # Copy built application from the build stage
53
+ COPY --from=build /mlbserver .
54
+
55
+ # Add data directory
56
+ VOLUME /mlbserver/data_directory
57
+
23
58
  EXPOSE 9999 10000
24
59
  CMD [ "node", "index.js", "--env", "--port", "9999", "--multiview_port", "10000", "--data_directory", "/mlbserver/data_directory" ]
package/index.js CHANGED
@@ -431,7 +431,7 @@ var getKey = function(url, headers, cb) {
431
431
  requestRetry(url, headers, function(err, response) {
432
432
  if (err) return cb(err)
433
433
  let key = response.body
434
- session.debuglog('key returned ' + key)
434
+ session.debuglog('key returned ' + Buffer.from(key, 'binary').toString('base64'))
435
435
  session.temp_cache.prevKeys[url] = key
436
436
  cb(null, key)
437
437
  })
@@ -1262,15 +1262,28 @@ app.get('/gamechangerplaylist.m3u8', async function(req, res) {
1262
1262
  let new_segments_complete = false
1263
1263
  let segment_count = 0
1264
1264
  for (var i=(body.length-1); i>=0; i--) {
1265
- if ( body[i].startsWith('#EXTINF:') ) {
1266
- let line = url.resolve(u, body[i+1])
1267
- if ( !new_segments_complete ) {
1268
- session.debuglog(game_changer_title + 'found segment ' + line)
1265
+ if ( body[i].startsWith('#EXT-X-KEY') ) {
1266
+ let key = url.resolve(u, body[i].match('URI="([^"]+)"')[1])
1267
+ let iv = body[i].match('IV=0x(.*)$')[1]
1268
+ let ts
1269
+ let extinf
1270
+ for (var j=1; j<=4; j++) {
1271
+ if ( body[i+j] ) {
1272
+ if ( !extinf && body[i+j].startsWith('#EXTINF') ) {
1273
+ extinf = body[i+j]
1274
+ } else if ( !ts && !body[i+j].startsWith('#') ) {
1275
+ ts = url.resolve(u, body[i+j])
1276
+ }
1277
+ if ( extinf && ts ) break;
1278
+ }
1279
+ }
1280
+ if ( key && iv && extinf && ts && !new_segments_complete ) {
1281
+ session.debuglog(game_changer_title + 'found segment ' + ts)
1269
1282
  if ( discontinuity ) {
1270
1283
  session.debuglog(game_changer_title + 'only getting newest segment after stream change')
1271
- new_segments.unshift({'extinf':body[i], 'ts':line, 'streamURLToken':streamURLToken})
1284
+ new_segments.unshift({'key':key, 'iv':iv, 'extinf':extinf, 'ts':ts, 'streamURLToken':streamURLToken})
1272
1285
  new_segments_complete = true
1273
- } else if ( !discontinuity && (session.temp_cache.gamechanger[id].segments.length > 0) && (line == session.temp_cache.gamechanger[id].segments[session.temp_cache.gamechanger[id].segments.length-1].ts) ) {
1286
+ } else if ( !discontinuity && (session.temp_cache.gamechanger[id].segments.length > 0) && (ts == session.temp_cache.gamechanger[id].segments[session.temp_cache.gamechanger[id].segments.length-1].ts) ) {
1274
1287
  session.debuglog(game_changer_title + 'found previous last segment')
1275
1288
  new_segments_complete = true
1276
1289
  } else if ( segment_count == GAMECHANGER_LIST_SIZE ) {
@@ -1281,7 +1294,7 @@ app.get('/gamechangerplaylist.m3u8', async function(req, res) {
1281
1294
  }
1282
1295
  new_segments_complete = true
1283
1296
  } else {
1284
- new_segments.unshift({'extinf':body[i], 'ts':line, 'streamURLToken':streamURLToken})
1297
+ new_segments.unshift({'key':key, 'iv':iv, 'extinf':extinf, 'ts':ts, 'streamURLToken':streamURLToken})
1285
1298
  }
1286
1299
  }
1287
1300
  segment_count++
@@ -1314,7 +1327,7 @@ app.get('/gamechangerplaylist.m3u8', async function(req, res) {
1314
1327
  if ( session.temp_cache.gamechanger[id].segments[i].discontinuity ) {
1315
1328
  session.temp_cache.gamechanger[id].playlist[resolution] += '#EXT-X-DISCONTINUITY' + '\n'
1316
1329
  }
1317
- session.temp_cache.gamechanger[id].playlist[resolution] += session.temp_cache.gamechanger[id].segments[i].extinf + '\n' + http_root + '/segment.ts?url=' + encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].ts) + '&streamURLToken='+encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].streamURLToken) + content_protect + '\n'
1330
+ session.temp_cache.gamechanger[id].playlist[resolution] += session.temp_cache.gamechanger[id].segments[i].extinf + '\n' + http_root + '/segment.ts?url=' + encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].ts) + '&streamURLToken='+encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].streamURLToken) + '&key='+encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].key) + '&iv='+encodeURIComponent(session.temp_cache.gamechanger[id].segments[i].iv) + content_protect + '\n'
1318
1331
  }
1319
1332
 
1320
1333
  session.debuglog(game_changer_title + 'playlist ' + session.temp_cache.gamechanger[id].playlist[resolution])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlbserver",
3
- "version": "2026.3.15",
3
+ "version": "2026.3.27-2",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,6 +13,7 @@
13
13
  "http": "0.0.1-security",
14
14
  "http-attach": "^1.0.0",
15
15
  "minimist": "^1.2.8",
16
+ "puppeteer": "^24.40.0",
16
17
  "readline-sync": "^1.4.10",
17
18
  "request": "^2.88.2",
18
19
  "request-promise": "^4.2.6",
package/session.js CHANGED
@@ -8,6 +8,7 @@ const path = require('path')
8
8
  const readlineSync = require('readline-sync')
9
9
  const FileCookieStore = require('tough-cookie-filestore')
10
10
  const parseString = require('xml2js').parseString
11
+ const puppeteer = require('puppeteer')
11
12
 
12
13
  const MULTIVIEW_DIRECTORY_NAME = 'multiview'
13
14
 
@@ -28,7 +29,7 @@ const LIDOM_TEAM_IDS = { 'AGU': '667', 'TOR': '668', 'EST': '669', 'GIG': '670',
28
29
 
29
30
  const LMP_TEAM_IDS = { 'MXC': '673', 'JAL': '674', 'MOC': '675', 'HER': '677', 'CUL': '678', 'MAZ': '679', 'OBR': '680', 'GSV': '5482', 'NAY': '6483', 'TBC': '6484' }
30
31
 
31
- const AFFILIATE_TEAM_IDS = { 'ATH': '237,400,499,524', 'ATL': '431,432,478,6325', 'AZ': '419,516,2310,5368', 'BAL': '418,488,548,568', 'BOS': '414,428,533,546', 'CHC': '451,521,550,553', 'CIN': '416,450,459,498', 'CLE': '402,437,445,481', 'COL': '259,342,486,538', 'CWS': '247,487,494,580', 'DET': '106,512,570,582', 'HOU': '482,573,3712,5434', 'KC': '541,565,1350,3705', 'LAA': '401,460,559,561', 'LAD': '238,260,456,526', 'MIA': '479,554,564,4124', 'MIL': '249,556,572,5015', 'MIN': '492,509,1960,3898', 'NYM': '453,505,507,552', 'NYY': '531,537,587,1956', 'PHI': '427,522,566,1410', 'PIT': '452,477,484,3390', 'SD': '103,510,584,4904', 'SEA': '403,515,529,574', 'SF': '105,461,476,3410', 'STL': '235,279,440,443', 'TB': '233,234,421,2498', 'TEX': '102,448,540,6324', 'TOR': '422,424,435,463', 'WSH': '426,436,534,547' }
32
+ const AFFILIATE_TEAM_IDS = {"ATH":"237,400,499,524","ATL":"431,432,478,6325","AZ":"419,516,2310,5368","BAL":"418,493,548,568","BOS":"414,428,533,546","CHC":"451,521,550,553","CIN":"416,450,459,498","CLE":"402,437,445,481","COL":"259,342,486,538","CWS":"247,487,494,580","DET":"106,512,570,582","HOU":"482,573,3712,5434","KC":"541,565,1350,3705","LAA":"460,526,559,561","LAD":"238,260,456,6482","MIA":"479,554,564,4124","MIL":"249,556,572,5015","MIN":"492,509,1960,3898","NYM":"453,505,507,552","NYY":"531,537,587,1956","PHI":"427,522,566,1410","PIT":"452,477,484,3390","SD":"103,510,584,4904","SEA":"401,403,529,574","SF":"105,461,476,3410","STL":"235,279,440,443","TB":"233,234,421,2498","TEX":"102,448,540,6324","TOR":"422,424,435,463","WSH":"426,436,534,547"}
32
33
 
33
34
  // First is default level, last should be All (also used as default org)
34
35
  const LEVELS = { 'MLB': '1', 'AAA': '11', 'AA': '12', 'A+': '13', 'A': '14', 'WINTER': '17', 'All': '1,11,12,13,14,17' }
@@ -864,6 +865,8 @@ class sessionClass {
864
865
  constructor(argv = {}) {
865
866
  this.debug = argv.debug
866
867
 
868
+ this.executablePath = argv.PUPPETEER_EXECUTABLE_PATH
869
+
867
870
  let dirname = __dirname
868
871
  if ( argv.data_directory ) {
869
872
  dirname = argv.data_directory
@@ -2368,8 +2371,8 @@ class sessionClass {
2368
2371
  stream = server + '/stream.m3u8?event=' + encodeURIComponent(cache_data.dates[i].games[j].teams['home'].team.shortName.toUpperCase())
2369
2372
  }
2370
2373
  stream += '&league_id=' + league_id
2374
+ stream += '&mediaType=' + streamMediaType
2371
2375
  }
2372
- stream += '&mediaType=' + streamMediaType
2373
2376
  stream += '&level=' + encodeURIComponent(this.getLevelNameFromSportId(sportId))
2374
2377
  stream += '&resolution=' + resolution
2375
2378
  if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
@@ -3319,6 +3322,7 @@ class sessionClass {
3319
3322
  async getSkipMarkers(gamePk, skip_type, start_inning, start_inning_half, streamURL, streamURLToken, skip_adjust, broadcast_start_timestamp=false) {
3320
3323
  try {
3321
3324
  this.debuglog('getSkipMarkers')
3325
+ let variantPlaylist;
3322
3326
 
3323
3327
  if ( skip_adjust != 0 ) this.log('manual adjustment of ' + skip_adjust + ' seconds being applied')
3324
3328
 
@@ -3342,7 +3346,7 @@ class sessionClass {
3342
3346
 
3343
3347
  // Get the broadcast start time first, if necessary -- event times will be relative to this
3344
3348
  if ( !broadcast_start_timestamp ) {
3345
- let variantPlaylist = await this.getVariantPlaylist(streamURL, streamURLToken)
3349
+ variantPlaylist = await this.getVariantPlaylist(streamURL, streamURLToken)
3346
3350
  broadcast_start_timestamp = await this.getBroadcastStart(variantPlaylist)
3347
3351
  }
3348
3352
 
@@ -3488,6 +3492,10 @@ class sessionClass {
3488
3492
  // if skipping commercials, look at the variant playlist to detect insertions
3489
3493
  if ( skip_type == 4 ) {
3490
3494
  this.debuglog('detecting commercial breaks')
3495
+ if (!variantPlaylist) {
3496
+ this.debuglog('variantPlaylist missing, fetching...')
3497
+ variantPlaylist = await this.getVariantPlaylist(streamURL, streamURLToken)
3498
+ }
3491
3499
  let body = variantPlaylist
3492
3500
  let break_active = false
3493
3501
  let break_end = 0
@@ -3538,62 +3546,118 @@ class sessionClass {
3538
3546
  let currentDate = new Date()
3539
3547
  if ( !this.cache || !this.cache.bigInningScheduleCacheExpiry || (currentDate > new Date(this.cache.bigInningScheduleCacheExpiry)) ) {
3540
3548
  if ( !this.cache.bigInningSchedule ) this.cache.bigInningSchedule = {}
3541
- let reqObj = {
3542
- url: 'https://api.fubo.tv/gg/series/123881219/live-programs?limit=14&languages=en&countrySlugs=USA',
3543
- headers: {
3544
- 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
3545
- 'accept-language': 'en-US,en;q=0.9',
3546
- 'cache-control': 'no-cache',
3547
- 'dnt': '1',
3548
- 'pragma': 'no-cache',
3549
- 'sec-ch-ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
3550
- 'sec-ch-ua-mobile': '?0',
3551
- 'sec-ch-ua-platform': '"macOS"',
3552
- 'sec-fetch-dest': 'document',
3553
- 'sec-fetch-mode': 'navigate',
3554
- 'sec-fetch-site': 'none',
3555
- 'sec-fetch-user': '?1',
3556
- 'sec-gpc': '1',
3557
- 'upgrade-insecure-requests': '1',
3558
- 'user-agent': USER_AGENT
3559
- },
3560
- json: true,
3561
- gzip: true
3562
- }
3563
- var response = await this.httpGet(reqObj, false)
3564
- if ( response ) {
3565
- this.debuglog(JSON.stringify(response))
3566
-
3567
- if ( response.data ) {
3568
- for (var i=0; i < response.data.length; i++) {
3569
- if ( response.data[i].airings && (response.data[i].airings.length > 0) ) {
3570
- for (var j=0; j < response.data[i].airings.length; j++) {
3571
- if ( response.data[i].airings[j].station && response.data[i].airings[j].station.name && (response.data[i].airings[j].station.name == 'MLB Big Inning') && response.data[i].airings[j].accessRightsV2 && response.data[i].airings[j].accessRightsV2.live ) {
3572
- let est_date = new Date(response.data[i].airings[j].accessRightsV2.live.startTime).toLocaleString("en-US", {timeZone: 'America/New_York'})
3573
- let date_array = est_date.split(',')[0].split('/')
3574
- let this_datestring = date_array[2] + '-' + date_array[0].padStart(2, '0') + '-' + date_array[1].padStart(2, '0')
3575
- this.cache.bigInningSchedule[this_datestring] = {
3576
- start: response.data[i].airings[j].accessRightsV2.live.startTime,
3577
- end: response.data[i].airings[j].accessRightsV2.live.endTime
3578
- }
3549
+
3550
+ const browser = await puppeteer.launch({
3551
+ headless: 'new',
3552
+ executablePath: this.executablePath,
3553
+ args: [
3554
+ '--no-sandbox',
3555
+ '--disable-gpu',
3556
+ '--disable-setuid-sandbox',
3557
+ '--disable-dev-shm-usage'
3558
+ ],
3559
+ })
3560
+ const page = await browser.newPage()
3561
+ await page.setUserAgent(USER_AGENT)
3562
+ await page.goto('https://support.mlb.com/s/article/What-Is-MLB-Big-Inning?language=en_US', { waitUntil: 'networkidle0' })
3563
+ const response = await page.content()
3564
+ await browser.close()
3565
+
3566
+ // break HTML into array based on table rows
3567
+ var rows = response.split('<tr ')
3568
+ // start iterating at 2 (after header row)
3569
+ for (var i=2; i<rows.length; i++) {
3570
+ // split HTML row into array with columns
3571
+ let cols = rows[i].split('<td ')
3572
+
3573
+ // define some variables that persist for each row
3574
+ let parts
3575
+ let year
3576
+ let month
3577
+ let day
3578
+ let this_datestring
3579
+ let add_date = 0
3580
+ let d
3581
+
3582
+ // start iterating at 2 (after DOW column)
3583
+ for (var j=2; j<cols.length; j++) {
3584
+ // split on brackets to get column text at resulting array index 0
3585
+ let col = cols[j].split('>')[1].split('<')
3586
+ switch(j){
3587
+ // first column is date
3588
+ case 2:
3589
+ // split date into array
3590
+ // old date format (January 1, 1970) (disabled)
3591
+ /*parts = col[0].split(' ')
3592
+ year = parts[2]
3593
+ // get month index, zero-based
3594
+ month = new Date(Date.parse(parts[0] +" 1, 2021")).getMonth()
3595
+ day = parts[1].substring(0,parts[1].length-3)*/
3596
+ // new date format (01/01/70)
3597
+ parts = col[0].split('/')
3598
+ year = parts[2]
3599
+ if ( year.length == 2 ) {
3600
+ year = '20' + parts[2]
3601
+ }
3602
+ // get month index, zero-based
3603
+ month = parseInt(parts[0]) - 1
3604
+ day = parts[1]
3605
+ this_datestring = new Date(year, month, day).toISOString().substring(0,10)
3606
+ this.cache.bigInningSchedule[this_datestring] = {}
3607
+ // increment month index (not zero-based)
3608
+ month += 1
3609
+ break
3610
+ // remaining columns are times
3611
+ default:
3612
+ let hour
3613
+ let minute = '00'
3614
+ let ampm
3615
+ // if time has colon, split into array on that to get hour and minute parts
3616
+ if ( col[0].indexOf(':') > 0 ) {
3617
+ parts = col[0].split(':')
3618
+ hour = parseInt(parts[0])
3619
+ minute = parts[1].substring(0,2)
3620
+ } else {
3621
+ hour = parseInt(col[0].substring(0,col[0].length-2))
3622
+ }
3623
+ ampm = col[0].substring(col[0].length-2,col[0].length)
3624
+ // convert hour to 24-hour format
3625
+ if ( (ampm == 'PM') || ((hour == 12) && (ampm == 'AM')) ) {
3626
+ hour += 12
3627
+ }
3628
+ // these times are EDT so add 4 for UTC
3629
+ hour += 4
3630
+ // if hour is beyond 23, note we will have to add 1 day
3631
+ if ( hour > 23 ) {
3632
+ add_date = 1
3633
+ hour -= 24
3634
+ }
3635
+
3636
+ d = new Date(this_datestring + 'T' + hour.toString().padStart(2, '0') + ':' + minute.toString().padStart(2, '0') + ':00.000+00:00')
3637
+ d.setDate(d.getDate()+add_date)
3638
+ switch(j){
3639
+ // 3rd column is start time
3640
+ case 3:
3641
+ this.cache.bigInningSchedule[this_datestring].start = d
3642
+ break
3643
+ // 3rd column is end time
3644
+ case 4:
3645
+ this.cache.bigInningSchedule[this_datestring].end = d
3579
3646
  break
3580
- }
3581
3647
  }
3582
- }
3648
+ break
3583
3649
  }
3584
3650
  }
3585
- this.debuglog(JSON.stringify(this.cache.bigInningSchedule))
3651
+ }
3652
+ this.debuglog(JSON.stringify(this.cache.bigInningSchedule))
3586
3653
 
3587
- // Default cache period is 1 day from now
3588
- let oneDayFromNow = new Date()
3589
- oneDayFromNow.setDate(oneDayFromNow.getDate()+1)
3590
- let cacheExpiry = oneDayFromNow
3591
- this.cache.bigInningScheduleCacheExpiry = cacheExpiry
3654
+ // Default cache period is 1 day from now
3655
+ let oneDayFromNow = new Date()
3656
+ oneDayFromNow.setDate(oneDayFromNow.getDate()+1)
3657
+ let cacheExpiry = oneDayFromNow
3658
+ this.cache.bigInningScheduleCacheExpiry = cacheExpiry
3592
3659
 
3593
- this.save_cache_data()
3594
- } else {
3595
- this.log('error : invalid response from url ' + reqObj.url)
3596
- }
3660
+ this.save_cache_data()
3597
3661
  } else {
3598
3662
  this.debuglog('using cached big inning schedule')
3599
3663
  }
@@ -4349,6 +4413,7 @@ class sessionClass {
4349
4413
  if ( cache_data ) {
4350
4414
  if ( cache_data.dates && cache_data.dates[0] && cache_data.dates[0].games && (cache_data.dates[0].games.length > 0) ) {
4351
4415
  let team_data = this.temp_cache.gamechanger[id].streamFinderData.team_data
4416
+ let games_CLI = this.temp_cache.gamechanger[id].streamFinderData.games_CLI
4352
4417
 
4353
4418
  var games = []
4354
4419
 
@@ -5615,6 +5680,56 @@ class sessionClass {
5615
5680
  this.log('getComskipMarkers error : ' + e.message)
5616
5681
  }
5617
5682
  }
5683
+
5684
+ // generates AFFILIATE_TEAM_IDS, should be done each season
5685
+ async getAffiliates() {
5686
+ try {
5687
+ this.debuglog('getAffiliates')
5688
+
5689
+ let affiliates_data = {}
5690
+ let reqObj = {
5691
+ url: 'https://statsapi.mlb.com/api/v1/teams?sportIds=1,11,12,13,14&activeStatus=true&season=2026',
5692
+ headers: {
5693
+ 'User-agent': USER_AGENT,
5694
+ 'Origin': 'https://www.mlb.com',
5695
+ 'Accept-Encoding': 'gzip, deflate, br',
5696
+ 'Content-type': 'application/json'
5697
+ },
5698
+ gzip: true
5699
+ }
5700
+ var response = await this.httpGet(reqObj, false)
5701
+ if ( response && this.isValidJson(response) ) {
5702
+ //this.debuglog(response)
5703
+ let teams_data = JSON.parse(response)
5704
+
5705
+ let parent_orgs = {}
5706
+ if ( teams_data && teams_data.teams ) {
5707
+ for (var i=0; i<teams_data.teams.length; i++) {
5708
+ if (teams_data.teams[i].sport.id == 1) {
5709
+ parent_orgs[teams_data.teams[i].id] = teams_data.teams[i].abbreviation
5710
+ affiliates_data[teams_data.teams[i].abbreviation] = []
5711
+ }
5712
+ }
5713
+ for (var i=0; i<teams_data.teams.length; i++) {
5714
+ if (teams_data.teams[i].sport.id != 1) {
5715
+ teams_data.teams[i].abbreviation
5716
+ affiliates_data[parent_orgs[teams_data.teams[i].parentOrgId]].push(teams_data.teams[i].id)
5717
+ affiliates_data[parent_orgs[teams_data.teams[i].parentOrgId]].sort((a, b) => a - b)
5718
+ }
5719
+ }
5720
+ for (const [key, value] of Object.entries(affiliates_data)) {
5721
+ affiliates_data[key] = value.join(',')
5722
+ }
5723
+
5724
+ console.log(JSON.stringify(this.sortObj(affiliates_data)))
5725
+ }
5726
+ } else {
5727
+ this.log('error : invalid json from url ' + reqObj.url)
5728
+ }
5729
+ } catch(e) {
5730
+ this.log('getAffiliates error : ' + e.message)
5731
+ }
5732
+ }
5618
5733
  }
5619
5734
 
5620
5735
  module.exports = sessionClass