mlbserver 2024.7.25 → 2024.7.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.
- package/README.md +2 -1
- package/index.js +56 -5
- package/package.json +1 -1
- package/session.js +77 -239
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
[](https://hub.docker.com/r/tonywagner/mlbserver)
|
|
2
|
+
[](https://www.npmjs.com/package/mlbserver)
|
|
2
3
|

|
|
3
4
|
[](https://github.com/tonywagner/mlbserver/graphs/contributors)
|
|
4
5
|
|
package/index.js
CHANGED
|
@@ -252,7 +252,7 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
252
252
|
streamURL = SAMPLE_STREAM_URL
|
|
253
253
|
options.referer = 'https://hls-js-dev.netlify.app/'
|
|
254
254
|
} else {
|
|
255
|
-
if ( req.query.resolution && (
|
|
255
|
+
if ( req.query.resolution && (req.query.resolution == 'best') ) {
|
|
256
256
|
options.resolution = VALID_RESOLUTIONS[1]
|
|
257
257
|
} else {
|
|
258
258
|
options.resolution = session.returnValidItem(req.query.resolution, VALID_RESOLUTIONS)
|
|
@@ -469,6 +469,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
469
469
|
|
|
470
470
|
// Some variables for controlling audio/video stream selection, if specified
|
|
471
471
|
var video_track_matched = false
|
|
472
|
+
var video_track_found = false
|
|
472
473
|
var audio_track_matched = false
|
|
473
474
|
var frame_rate = '29.97'
|
|
474
475
|
if ( (resolution != VALID_RESOLUTIONS[0]) && (resolution != VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) ) {
|
|
@@ -588,8 +589,9 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
588
589
|
if ( resolution === VALID_RESOLUTIONS[0] ) {
|
|
589
590
|
return line
|
|
590
591
|
} else {
|
|
591
|
-
if ( line.indexOf(resolution+',FRAME-RATE='+frame_rate) > 0 ) {
|
|
592
|
+
if ( (video_track_found == false) && (line.indexOf(resolution+',FRAME-RATE='+frame_rate) > 0) ) {
|
|
592
593
|
video_track_matched = true
|
|
594
|
+
video_track_found = true
|
|
593
595
|
return line
|
|
594
596
|
} else {
|
|
595
597
|
return
|
|
@@ -1501,6 +1503,32 @@ app.get('/', async function(req, res) {
|
|
|
1501
1503
|
|
|
1502
1504
|
let currentDate = new Date()
|
|
1503
1505
|
|
|
1506
|
+
// MLB Network live stream for paid accounts
|
|
1507
|
+
try {
|
|
1508
|
+
let entitlements = await session.getEntitlements()
|
|
1509
|
+
if ( entitlements.includes('MLBN') || entitlements.includes('MLBALL') || entitlements.includes('MLBTVMLBNADOBEPASS') ) {
|
|
1510
|
+
body += '<tr><td><span class="tooltip">MLB Network<span class="tooltiptext">MLB Network live stream is now included with paid MLBTV subscriptions, or as a paid add-on, in addition to authenticated TV subscribers. <a href="https://www.mlb.com/news/mlb-network-launches-direct-to-consumer-streaming-option">See here for more information</a>.</span></span></td><td>'
|
|
1511
|
+
let querystring = '?event=MLBN'
|
|
1512
|
+
let multiviewquerystring = querystring + '&resolution=' + DEFAULT_MULTIVIEW_RESOLUTION
|
|
1513
|
+
if ( linkType == VALID_LINK_TYPES[0] ) {
|
|
1514
|
+
if ( startFrom != VALID_START_FROM[0] ) querystring += '&startFrom=' + startFrom
|
|
1515
|
+
if ( controls != VALID_CONTROLS[0] ) querystring += '&controls=' + controls
|
|
1516
|
+
}
|
|
1517
|
+
if ( resolution != VALID_RESOLUTIONS[0] ) querystring += '&resolution=' + resolution
|
|
1518
|
+
if ( linkType == VALID_LINK_TYPES[1] ) {
|
|
1519
|
+
if ( force_vod != VALID_FORCE_VOD[0] ) querystring += '&force_vod=' + force_vod
|
|
1520
|
+
}
|
|
1521
|
+
querystring += content_protect_b
|
|
1522
|
+
multiviewquerystring += content_protect_b
|
|
1523
|
+
body += '<a href="' + thislink + querystring + '">MLB Network</a>'
|
|
1524
|
+
body += '<input type="checkbox" value="' + server + '/stream.m3u8' + multiviewquerystring + '" onclick="addmultiview(this)">'
|
|
1525
|
+
body += '</td></tr>' + "\n"
|
|
1526
|
+
|
|
1527
|
+
}
|
|
1528
|
+
} catch (e) {
|
|
1529
|
+
session.debuglog('MLB Network detect error : ' + e.message)
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1504
1532
|
if ( (mediaType == 'MLBTV') && ((level_ids == levels['MLB']) || level_ids.startsWith(levels['MLB'] + ',')) ) {
|
|
1505
1533
|
// Recap Rundown beginning in 2023, disabled because it stopped working
|
|
1506
1534
|
/*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) ) {
|
|
@@ -2119,6 +2147,8 @@ app.get('/', async function(req, res) {
|
|
|
2119
2147
|
|
|
2120
2148
|
body += '<p><span class="tooltip">Include by level<span class="tooltiptext">Including a level (AAA, AA, A+ encoded as A%2B, 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=a%2B,aaa' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeLevels=a%2B,aaa' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeLevels=a%2B,aaa' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2121
2149
|
|
|
2150
|
+
body += '<p><span class="tooltip">Include teams in titles<span class="tooltiptext">An optional parameter added to the URL will include team names in the ICS/XML titles.</span></span>: <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeTeamsInTitles=true' + content_protect_b + '">guide.xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeTeamsInTitles=true' + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2151
|
+
|
|
2122
2152
|
body += '</td></tr></table><br/>' + "\n"
|
|
2123
2153
|
|
|
2124
2154
|
body += '<table><tr><td>' + "\n"
|
|
@@ -2366,7 +2396,7 @@ app.get('/channels.m3u', async function(req, res) {
|
|
|
2366
2396
|
includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
|
|
2367
2397
|
}
|
|
2368
2398
|
|
|
2369
|
-
var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution, pipe, startingChannelNumber)
|
|
2399
|
+
var body = await session.getTVData('channels', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, 'false', resolution, pipe, startingChannelNumber)
|
|
2370
2400
|
|
|
2371
2401
|
res.writeHead(200, {'Content-Type': 'audio/x-mpegurl'})
|
|
2372
2402
|
res.end(body)
|
|
@@ -2408,9 +2438,14 @@ app.get('/calendar.ics', async function(req, res) {
|
|
|
2408
2438
|
includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
|
|
2409
2439
|
}
|
|
2410
2440
|
|
|
2441
|
+
let includeTeamsInTitles = 'false'
|
|
2442
|
+
if ( req.query.includeTeamsInTitles ) {
|
|
2443
|
+
includeTeamsInTitles = req.query.includeTeamsInTitles
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2411
2446
|
let server = 'http://' + req.headers.host
|
|
2412
2447
|
|
|
2413
|
-
var body = await session.getTVData('calendar', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts)
|
|
2448
|
+
var body = await session.getTVData('calendar', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, includeTeamsInTitles)
|
|
2414
2449
|
|
|
2415
2450
|
res.writeHead(200, {'Content-Type': 'text/calendar'})
|
|
2416
2451
|
res.end(body)
|
|
@@ -2456,9 +2491,25 @@ app.get('/guide.xml', async function(req, res) {
|
|
|
2456
2491
|
includeOrgs = req.query.includeOrgs.toUpperCase().split(',')
|
|
2457
2492
|
}
|
|
2458
2493
|
|
|
2494
|
+
let includeTeamsInTitles = 'false'
|
|
2495
|
+
if ( req.query.includeTeamsInTitles ) {
|
|
2496
|
+
includeTeamsInTitles = req.query.includeTeamsInTitles
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2459
2499
|
let server = 'http://' + req.headers.host
|
|
2460
2500
|
|
|
2461
|
-
var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts)
|
|
2501
|
+
var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, includeTeamsInTitles)
|
|
2502
|
+
|
|
2503
|
+
res.end(body)
|
|
2504
|
+
})
|
|
2505
|
+
|
|
2506
|
+
// Listen for entitlements requests
|
|
2507
|
+
app.get('/entitlements', async function(req, res) {
|
|
2508
|
+
if ( ! (await protect(req, res)) ) return
|
|
2509
|
+
|
|
2510
|
+
session.requestlog('entitlements', req, true)
|
|
2511
|
+
|
|
2512
|
+
var body = await session.getEntitlements()
|
|
2462
2513
|
|
|
2463
2514
|
res.end(body)
|
|
2464
2515
|
})
|
package/package.json
CHANGED
package/session.js
CHANGED
|
@@ -1472,46 +1472,11 @@ class sessionClass {
|
|
|
1472
1472
|
})
|
|
1473
1473
|
}
|
|
1474
1474
|
|
|
1475
|
-
// API call
|
|
1476
|
-
/*async getApiKeys() {
|
|
1477
|
-
this.debuglog('getApiKeys')
|
|
1478
|
-
let reqObj = {
|
|
1479
|
-
url: 'https://www.mlb.com/tv/g632102/',
|
|
1480
|
-
headers: {
|
|
1481
|
-
'User-agent': USER_AGENT,
|
|
1482
|
-
'Origin': 'https://www.mlb.com',
|
|
1483
|
-
'Accept-Encoding': 'gzip, deflate, br'
|
|
1484
|
-
},
|
|
1485
|
-
gzip: true
|
|
1486
|
-
}
|
|
1487
|
-
var response = await this.httpGet(reqObj)
|
|
1488
|
-
if ( response ) {
|
|
1489
|
-
// disabled because it's very big!
|
|
1490
|
-
//this.debuglog('getApiKeys response : ' + response)
|
|
1491
|
-
var parsed = response.match('"x-api-key","value":"([^"]+)"')
|
|
1492
|
-
if ( parsed[1] ) {
|
|
1493
|
-
this.data.xApiKey = parsed[1]
|
|
1494
|
-
this.save_session_data()
|
|
1495
|
-
} else {
|
|
1496
|
-
this.log('getApiKeys xApiKey parse failure')
|
|
1497
|
-
}
|
|
1498
|
-
parsed = response.match('"clientApiKey":"([^"]+)"')
|
|
1499
|
-
if ( parsed[1] ) {
|
|
1500
|
-
this.data.clientApiKey = parsed[1]
|
|
1501
|
-
this.save_session_data()
|
|
1502
|
-
} else {
|
|
1503
|
-
this.log('getApiKeys clientApiKey parse failure')
|
|
1504
|
-
}
|
|
1505
|
-
} else {
|
|
1506
|
-
this.log('getApiKeys response failure')
|
|
1507
|
-
}
|
|
1508
|
-
}*/
|
|
1509
|
-
|
|
1510
1475
|
// new API call
|
|
1511
1476
|
async getDeviceId() {
|
|
1512
1477
|
this.debuglog('getDeviceId')
|
|
1513
1478
|
if ( !this.data.deviceId ) {
|
|
1514
|
-
this.getSession()
|
|
1479
|
+
await this.getSession()
|
|
1515
1480
|
}
|
|
1516
1481
|
if ( this.data.deviceId ) {
|
|
1517
1482
|
this.debuglog('using cached deviceId')
|
|
@@ -1525,7 +1490,7 @@ class sessionClass {
|
|
|
1525
1490
|
async getSessionId() {
|
|
1526
1491
|
this.debuglog('getSessionId')
|
|
1527
1492
|
if ( !this.data.sessionId ) {
|
|
1528
|
-
this.getSession()
|
|
1493
|
+
await this.getSession()
|
|
1529
1494
|
}
|
|
1530
1495
|
if ( this.data.sessionId ) {
|
|
1531
1496
|
this.debuglog('using cached sessionId')
|
|
@@ -1535,6 +1500,20 @@ class sessionClass {
|
|
|
1535
1500
|
}
|
|
1536
1501
|
}
|
|
1537
1502
|
|
|
1503
|
+
// new API call
|
|
1504
|
+
async getEntitlements() {
|
|
1505
|
+
this.debuglog('getEntitlements')
|
|
1506
|
+
if ( !this.data.entitlements ) {
|
|
1507
|
+
await this.getSession()
|
|
1508
|
+
}
|
|
1509
|
+
if ( this.data.entitlements ) {
|
|
1510
|
+
this.debuglog('using cached entitlements')
|
|
1511
|
+
return this.data.entitlements
|
|
1512
|
+
} else {
|
|
1513
|
+
this.log('failed to getEntitlements')
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1538
1517
|
// new API call
|
|
1539
1518
|
async getSession() {
|
|
1540
1519
|
this.debuglog('getSession')
|
|
@@ -1578,6 +1557,11 @@ class sessionClass {
|
|
|
1578
1557
|
this.debuglog('getSession response : ' + JSON.stringify(response))
|
|
1579
1558
|
this.data.deviceId = response.data.initSession.deviceId
|
|
1580
1559
|
this.data.sessionId = response.data.initSession.sessionId
|
|
1560
|
+
var entitlements = []
|
|
1561
|
+
for (var i=0; i<response.data.initSession.entitlements.length; i++) {
|
|
1562
|
+
entitlements.push(response.data.initSession.entitlements[i].code)
|
|
1563
|
+
}
|
|
1564
|
+
this.data.entitlements = entitlements
|
|
1581
1565
|
this.save_session_data()
|
|
1582
1566
|
} else {
|
|
1583
1567
|
this.log('getSession response failure')
|
|
@@ -1651,204 +1635,6 @@ class sessionClass {
|
|
|
1651
1635
|
}
|
|
1652
1636
|
}
|
|
1653
1637
|
|
|
1654
|
-
// API call
|
|
1655
|
-
/*async getStreamURL(mediaId) {
|
|
1656
|
-
this.debuglog('getStreamURL from ' + mediaId)
|
|
1657
|
-
if ( this.cache.media && this.cache.media[mediaId] && this.cache.media[mediaId].streamURL && this.cache.media[mediaId].streamURLExpiry && (Date.parse(this.cache.media[mediaId].streamURLExpiry) > new Date()) ) {
|
|
1658
|
-
this.debuglog('using cached streamURL')
|
|
1659
|
-
return this.cache.media[mediaId].streamURL
|
|
1660
|
-
} else if ( this.cache.media && this.cache.media[mediaId] && this.cache.media[mediaId].blackout && this.cache.media[mediaId].blackoutExpiry && (Date.parse(this.cache.media[mediaId].blackoutExpiry) > new Date()) ) {
|
|
1661
|
-
this.log('mediaId recently blacked out, skipping')
|
|
1662
|
-
} else {
|
|
1663
|
-
let playbackURL = 'https://edge.svcs.mlb.com/media/' + mediaId + '/scenarios/browser~csai'
|
|
1664
|
-
let reqObj = {
|
|
1665
|
-
url: playbackURL,
|
|
1666
|
-
simple: false,
|
|
1667
|
-
headers: {
|
|
1668
|
-
'Authorization': await this.getBamAccessToken() || this.halt('missing bamAccessToken'),
|
|
1669
|
-
'User-agent': USER_AGENT,
|
|
1670
|
-
'Accept': 'application/vnd.media-service+json; version=1',
|
|
1671
|
-
'x-bamsdk-version': BAM_SDK_VERSION,
|
|
1672
|
-
'x-bamsdk-platform': PLATFORM,
|
|
1673
|
-
'Origin': 'https://www.mlb.com',
|
|
1674
|
-
'Accept-Encoding': 'gzip, deflate, br',
|
|
1675
|
-
'Content-type': 'application/json'
|
|
1676
|
-
},
|
|
1677
|
-
gzip: true
|
|
1678
|
-
}
|
|
1679
|
-
var response = await this.httpGet(reqObj)
|
|
1680
|
-
if ( response && this.isValidJson(response) ) {
|
|
1681
|
-
this.debuglog('getStreamURL response : ' + response)
|
|
1682
|
-
let obj = JSON.parse(response)
|
|
1683
|
-
if ( obj.errors ) {
|
|
1684
|
-
this.log('getStreamURL error: ' + obj.errors[0])
|
|
1685
|
-
if ( obj.errors[0] == 'blackout' ) {
|
|
1686
|
-
this.markBlackoutError(mediaId)
|
|
1687
|
-
}
|
|
1688
|
-
return false
|
|
1689
|
-
} else {
|
|
1690
|
-
let streamURL = obj.stream.complete
|
|
1691
|
-
this.debuglog('getStreamURL : ' + streamURL)
|
|
1692
|
-
this.cacheStreamURL(mediaId, streamURL)
|
|
1693
|
-
return streamURL
|
|
1694
|
-
}
|
|
1695
|
-
} else {
|
|
1696
|
-
this.log('getStreamURL response failure')
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
// API call
|
|
1702
|
-
async getBamAccessToken() {
|
|
1703
|
-
this.debuglog('getBamAccessToken')
|
|
1704
|
-
if ( !this.data.bamAccessToken || !this.data.bamAccessTokenExpiry || (Date.parse(this.data.bamAccessTokenExpiry) < new Date()) ) {
|
|
1705
|
-
this.debuglog('need to get new bamAccessToken')
|
|
1706
|
-
let reqObj = {
|
|
1707
|
-
url: BAM_TOKEN_URL,
|
|
1708
|
-
headers: {
|
|
1709
|
-
'Authorization': 'Bearer ' + API_KEY,
|
|
1710
|
-
'User-agent': USER_AGENT,
|
|
1711
|
-
'Accept': 'application/vnd.media-service+json; version=1',
|
|
1712
|
-
'x-bamsdk-version': BAM_SDK_VERSION,
|
|
1713
|
-
'x-bamsdk-platform': PLATFORM,
|
|
1714
|
-
'Origin': 'https://www.mlb.com',
|
|
1715
|
-
'Accept-Encoding': 'gzip, deflate, br',
|
|
1716
|
-
'Content-type': 'application/x-www-form-urlencoded'
|
|
1717
|
-
},
|
|
1718
|
-
form: {
|
|
1719
|
-
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
|
1720
|
-
'platform': 'browser',
|
|
1721
|
-
'subject_token': await this.getEntitlementToken() || this.halt('missing EntitlementToken'),
|
|
1722
|
-
'subject_token_type': 'urn:bamtech:params:oauth:token-type:account'
|
|
1723
|
-
},
|
|
1724
|
-
gzip: true
|
|
1725
|
-
}
|
|
1726
|
-
var response = await this.httpPost(reqObj)
|
|
1727
|
-
if ( this.isValidJson(response) ) {
|
|
1728
|
-
let obj = JSON.parse(response)
|
|
1729
|
-
this.debuglog('getBamAccessToken : ' + obj.access_token)
|
|
1730
|
-
this.debuglog('getBamAccessToken expires in : ' + obj.expires_in)
|
|
1731
|
-
this.data.bamAccessToken = obj.access_token
|
|
1732
|
-
this.data.bamAccessTokenExpiry = new Date(new Date().getTime() + obj.expires_in * 1000)
|
|
1733
|
-
this.save_session_data()
|
|
1734
|
-
return this.data.bamAccessToken
|
|
1735
|
-
} else {
|
|
1736
|
-
this.log('getBamAccessToken response failure')
|
|
1737
|
-
}
|
|
1738
|
-
} else {
|
|
1739
|
-
return this.data.bamAccessToken
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
// API call
|
|
1744
|
-
async getEntitlementToken() {
|
|
1745
|
-
this.debuglog('getEntitlementToken')
|
|
1746
|
-
let reqObj = {
|
|
1747
|
-
url: 'https://media-entitlement.mlb.com/api/v3/jwt',
|
|
1748
|
-
headers: {
|
|
1749
|
-
'Authorization': 'Bearer ' + await this.getLoginToken() || this.halt('missing loginToken'),
|
|
1750
|
-
'Origin': 'https://www.mlb.com'
|
|
1751
|
-
},
|
|
1752
|
-
qs: {
|
|
1753
|
-
'os': PLATFORM,
|
|
1754
|
-
'did': await this.getDeviceId() || this.halt('missing deviceId'),
|
|
1755
|
-
'appname': 'mlbtv_web'
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
var response = await this.httpGet(reqObj)
|
|
1759
|
-
if ( response ) {
|
|
1760
|
-
this.debuglog('getEntitlementToken response : ' + response)
|
|
1761
|
-
this.debuglog('getEntitlementToken : ' + response)
|
|
1762
|
-
return response
|
|
1763
|
-
} else {
|
|
1764
|
-
this.log('getEntitlementToken response failure')
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
async getDeviceId() {
|
|
1769
|
-
this.debuglog('getDeviceId')
|
|
1770
|
-
let reqObj = {
|
|
1771
|
-
url: 'https://us.edge.bamgrid.com/session',
|
|
1772
|
-
headers: {
|
|
1773
|
-
'Authorization': await this.getDeviceAccessToken() || this.halt('missing device_access_token'),
|
|
1774
|
-
'User-agent': USER_AGENT,
|
|
1775
|
-
'Origin': 'https://www.mlb.com',
|
|
1776
|
-
'Accept': 'application/vnd.session-service+json; version=1',
|
|
1777
|
-
'Accept-Encoding': 'gzip, deflate, br',
|
|
1778
|
-
'Accept-Language': 'en-US,en;q=0.5',
|
|
1779
|
-
'x-bamsdk-version': BAM_SDK_VERSION,
|
|
1780
|
-
'x-bamsdk-platform': PLATFORM,
|
|
1781
|
-
'Content-type': 'application/json',
|
|
1782
|
-
'TE': 'Trailers'
|
|
1783
|
-
},
|
|
1784
|
-
gzip: true
|
|
1785
|
-
}
|
|
1786
|
-
var response = await this.httpGet(reqObj)
|
|
1787
|
-
if ( response && this.isValidJson(response) ) {
|
|
1788
|
-
this.debuglog('getDeviceId response : ' + response)
|
|
1789
|
-
let obj = JSON.parse(response)
|
|
1790
|
-
this.debuglog('getDeviceId : ' + obj.device.id)
|
|
1791
|
-
return obj.device.id
|
|
1792
|
-
} else {
|
|
1793
|
-
this.log('getDeviceId response failure')
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
// API call
|
|
1798
|
-
async getDeviceAccessToken() {
|
|
1799
|
-
this.debuglog('getDeviceAccessToken')
|
|
1800
|
-
let reqObj = {
|
|
1801
|
-
url: BAM_TOKEN_URL,
|
|
1802
|
-
headers: {
|
|
1803
|
-
'Authorization': 'Bearer ' + API_KEY,
|
|
1804
|
-
'Origin': 'https://www.mlb.com'
|
|
1805
|
-
},
|
|
1806
|
-
form: {
|
|
1807
|
-
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
|
1808
|
-
'latitude': '0',
|
|
1809
|
-
'longitude': '0',
|
|
1810
|
-
'platform': 'browser',
|
|
1811
|
-
'subject_token': await this.getDevicesAssertion() || this.halt('missing devicesAssertion'),
|
|
1812
|
-
'subject_token_type': 'urn:bamtech:params:oauth:token-type:device'
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
var response = await this.httpPost(reqObj)
|
|
1816
|
-
if ( this.isValidJson(response) ) {
|
|
1817
|
-
this.debuglog('getDeviceAccessToken response : ' + response)
|
|
1818
|
-
let obj = JSON.parse(response)
|
|
1819
|
-
this.debuglog('getDeviceAccessToken : ' + obj.access_token)
|
|
1820
|
-
return obj.access_token
|
|
1821
|
-
} else {
|
|
1822
|
-
this.log('getDeviceAccessToken response failure')
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// API call
|
|
1827
|
-
async getDevicesAssertion() {
|
|
1828
|
-
this.debuglog('getDevicesAssertion')
|
|
1829
|
-
let reqObj = {
|
|
1830
|
-
url: 'https://us.edge.bamgrid.com/devices',
|
|
1831
|
-
headers: {
|
|
1832
|
-
'Authorization': 'Bearer ' + API_KEY,
|
|
1833
|
-
'Origin': 'https://www.mlb.com'
|
|
1834
|
-
},
|
|
1835
|
-
json: {
|
|
1836
|
-
'applicationRuntime': 'firefox',
|
|
1837
|
-
'attributes': {},
|
|
1838
|
-
'deviceFamily': 'browser',
|
|
1839
|
-
'deviceProfile': 'macosx'
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
var response = await this.httpPost(reqObj)
|
|
1843
|
-
if ( response.assertion ) {
|
|
1844
|
-
this.debuglog('getDevicesAssertion response : ' + JSON.stringify(response))
|
|
1845
|
-
this.debuglog('getDevicesAssertion : ' + response.assertion)
|
|
1846
|
-
return response.assertion
|
|
1847
|
-
} else {
|
|
1848
|
-
this.log('getDevicesAssertion response failure')
|
|
1849
|
-
}
|
|
1850
|
-
}*/
|
|
1851
|
-
|
|
1852
1638
|
// API call
|
|
1853
1639
|
async getLoginToken() {
|
|
1854
1640
|
this.debuglog('getLoginToken')
|
|
@@ -1870,8 +1656,9 @@ class sessionClass {
|
|
|
1870
1656
|
}
|
|
1871
1657
|
var response = await this.httpPost(reqObj)
|
|
1872
1658
|
if ( this.isValidJson(response) ) {
|
|
1659
|
+
this.debuglog('getLoginToken : ' + response)
|
|
1873
1660
|
let obj = JSON.parse(response)
|
|
1874
|
-
this.debuglog('getLoginToken : ' + obj.access_token)
|
|
1661
|
+
this.debuglog('getLoginToken token : ' + obj.access_token)
|
|
1875
1662
|
this.debuglog('getLoginToken expires in : ' + obj.expires_in)
|
|
1876
1663
|
this.data.loginToken = obj.access_token
|
|
1877
1664
|
this.data.loginTokenExpiry = new Date(new Date().getTime() + obj.expires_in * 1000)
|
|
@@ -2313,7 +2100,7 @@ class sessionClass {
|
|
|
2313
2100
|
}
|
|
2314
2101
|
|
|
2315
2102
|
// get TV data (channels or guide)
|
|
2316
|
-
async getTVData(dataType, mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, resolution='best', pipe='false', startingChannelNumber=1) {
|
|
2103
|
+
async getTVData(dataType, mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, includeTeamsInTitles='false', resolution='best', pipe='false', startingChannelNumber=1) {
|
|
2317
2104
|
try {
|
|
2318
2105
|
this.debuglog('getTVData for ' + dataType)
|
|
2319
2106
|
|
|
@@ -2447,6 +2234,10 @@ class sessionClass {
|
|
|
2447
2234
|
let home_team = cache_data.dates[i].games[j].teams['home'].team.name
|
|
2448
2235
|
let subtitle = away_team + ' at ' + home_team
|
|
2449
2236
|
|
|
2237
|
+
if (includeTeamsInTitles == 'true') {
|
|
2238
|
+
title = 'MiLB: ' + subtitle
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2450
2241
|
let description = cache_data.dates[i].games[j].teams['home'].team.league.name + '. '
|
|
2451
2242
|
if ( cache_data.dates[i].games[j].seriesDescription != 'Regular Season' ) {
|
|
2452
2243
|
description += cache_data.dates[i].games[j].seriesDescription + '. '
|
|
@@ -2626,6 +2417,17 @@ class sessionClass {
|
|
|
2626
2417
|
let home_team = cache_data.dates[i].games[j].teams['home'].team.teamName
|
|
2627
2418
|
let subtitle = away_team + ' at ' + home_team
|
|
2628
2419
|
|
|
2420
|
+
if (includeTeamsInTitles == 'true') {
|
|
2421
|
+
title = 'MLB: ' + subtitle + ' (' + station
|
|
2422
|
+
if ( language == 'es' ) {
|
|
2423
|
+
title += ' Spanish'
|
|
2424
|
+
}
|
|
2425
|
+
if ( mediaType == 'Audio' ) {
|
|
2426
|
+
title += ' Radio'
|
|
2427
|
+
}
|
|
2428
|
+
title += ')'
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2629
2431
|
let description = station
|
|
2630
2432
|
if ( mediaType == 'Audio' ) {
|
|
2631
2433
|
if ( language == 'es' ) {
|
|
@@ -2714,6 +2516,40 @@ class sessionClass {
|
|
|
2714
2516
|
channels = this.sortObj(channels)
|
|
2715
2517
|
channels = Object.assign(channels, nationalChannels)
|
|
2716
2518
|
|
|
2519
|
+
// MLB Network
|
|
2520
|
+
try {
|
|
2521
|
+
let entitlements = await this.getEntitlements()
|
|
2522
|
+
if ( entitlements.includes('MLBN') || entitlements.includes('MLBALL') || entitlements.includes('MLBTVMLBNADOBEPASS') ) {
|
|
2523
|
+
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
2524
|
+
if ( (excludeTeams.length > 0) && excludeTeams.includes('MLBN') ) {
|
|
2525
|
+
// do nothing
|
|
2526
|
+
} else if ( (includeTeams.length == 0) || includeTeams.includes('MLBN') ) {
|
|
2527
|
+
this.debuglog('getTVData processing MLB Network')
|
|
2528
|
+
let logo = 'https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQRgC2JdbtFplKjfhXm5_vzpkUQ3XyDT91SEnHmuB0p5tReQ3Ez'
|
|
2529
|
+
let channelid = mediaType + '.MLBN'
|
|
2530
|
+
if ( this.protection.content_protect ) logo += '&content_protect=' + this.protection.content_protect
|
|
2531
|
+
let stream = server + '/stream.m3u8?event=mlbn&mediaType=Video&resolution=' + resolution
|
|
2532
|
+
if ( this.protection.content_protect ) stream += '&content_protect=' + this.protection.content_protect
|
|
2533
|
+
if ( pipe == 'true' ) stream = await this.convert_stream_to_pipe(stream, channelid)
|
|
2534
|
+
channels[channelid] = await this.create_channel_object(channelid, logo, stream, mediaType)
|
|
2535
|
+
|
|
2536
|
+
let title = 'MLB Network'
|
|
2537
|
+
let description = 'Live stream of MLB Network'
|
|
2538
|
+
|
|
2539
|
+
let start = this.convertDateToXMLTV(new Date(cache_data.dates[0].date + ' 00:00:00'))
|
|
2540
|
+
let stop = this.convertDateToXMLTV(new Date(cache_data.dates[cache_data.dates.length-1].date + ' 00:00:00'))
|
|
2541
|
+
|
|
2542
|
+
// Big Inning guide XML
|
|
2543
|
+
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertStringToAirDate(cache_data.dates[0].date))
|
|
2544
|
+
this.debuglog('getTVData completed MLB Network')
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
}
|
|
2549
|
+
} catch (e) {
|
|
2550
|
+
this.debuglog('getTVData MLB Network detect error : ' + e.message)
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2717
2553
|
// Big Inning
|
|
2718
2554
|
if ( (mediaType == 'MLBTV') && ((includeLevels.length == 0) || includeLevels.includes('MLB') || includeLevels.includes('ALL')) ) {
|
|
2719
2555
|
if ( (excludeTeams.length > 0) && excludeTeams.includes('BIGINNING') ) {
|
|
@@ -2759,7 +2595,7 @@ class sessionClass {
|
|
|
2759
2595
|
calendar += await this.generate_ics_event(prefix, new Date(this.cache.bigInningSchedule[gameDate].start), new Date(this.cache.bigInningSchedule[gameDate].end), title, description, location)
|
|
2760
2596
|
|
|
2761
2597
|
// Big Inning guide XML
|
|
2762
|
-
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.
|
|
2598
|
+
programs += await this.generate_xml_program(channelid, start, stop, title, description, logo, this.convertDateToAirDate(new Date(this.cache.bigInningSchedule[gameDate].start)))
|
|
2763
2599
|
}
|
|
2764
2600
|
this.debuglog('getTVData completed Big Inning for date ' + cache_data.dates[i].date)
|
|
2765
2601
|
}
|
|
@@ -3535,6 +3371,8 @@ class sessionClass {
|
|
|
3535
3371
|
let dateString = eventName.substring(12)
|
|
3536
3372
|
this.debuglog('getEventStreamURL RecapRundown for ' + dateString)
|
|
3537
3373
|
playbackURL = await this.getRecapRundownURL(dateString)
|
|
3374
|
+
} else if ( eventName.toUpperCase() == 'MLBN' ) {
|
|
3375
|
+
playbackURL = 'https://falcon.mlbinfra.com/api/v1/linear/mlbn'
|
|
3538
3376
|
} else {
|
|
3539
3377
|
playbackURL = await this.getEventURL(eventName)
|
|
3540
3378
|
}
|
|
@@ -4356,7 +4194,7 @@ class sessionClass {
|
|
|
4356
4194
|
let xml_output = "\n" + ' <programme channel="' + channelid + '" start="' + start + '" stop="' + stop + '">' + "\n" +
|
|
4357
4195
|
' <title lang="en">' + title + '</title>' + "\n"
|
|
4358
4196
|
if ( subtitle ) {
|
|
4359
|
-
xml_output += ' <
|
|
4197
|
+
xml_output += ' <sub-title lang="en">' + subtitle + '</sub-title>' + "\n"
|
|
4360
4198
|
}
|
|
4361
4199
|
xml_output += ' <desc lang="en">' + description.trim() + '</desc>' + "\n" +
|
|
4362
4200
|
' <category lang="en">Sports</category>' + "\n" +
|