mlbserver 2025.7.26-2 → 2025.7.26-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +39 -11
- package/package.json +1 -1
- package/session.js +25 -12
package/index.js
CHANGED
|
@@ -451,6 +451,32 @@ var requestRetry = function(u, opts, cb) {
|
|
|
451
451
|
action()
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
+
// Listen for master stream requests
|
|
455
|
+
app.get('/master.m3u8', async function(req, res) {
|
|
456
|
+
if ( ! (await protect(req, res)) ) return
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
session.requestlog('master.m3u8', req)
|
|
460
|
+
|
|
461
|
+
let options = {}
|
|
462
|
+
if ( req.query.streamURL ) {
|
|
463
|
+
let streamURL = decodeURIComponent(req.query.streamURL)
|
|
464
|
+
if ( req.query.streamURLToken ) {
|
|
465
|
+
options.streamURLToken = decodeURIComponent(req.query.streamURLToken)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
getMasterPlaylist(streamURL, req, res, options)
|
|
469
|
+
} else {
|
|
470
|
+
session.log('failed to find master URL : ' + req.url)
|
|
471
|
+
res.end('')
|
|
472
|
+
return
|
|
473
|
+
}
|
|
474
|
+
} catch (e) {
|
|
475
|
+
session.log('master request error : ' + e.message)
|
|
476
|
+
res.end('')
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
|
|
454
480
|
|
|
455
481
|
// Get the master playlist from the stream URL
|
|
456
482
|
function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
@@ -554,12 +580,12 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
554
580
|
}
|
|
555
581
|
|
|
556
582
|
// Omit captions track when TV audio is excluded or no video is specified
|
|
557
|
-
if ( line.startsWith('#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS
|
|
583
|
+
if ( line.startsWith('#EXT-X-MEDIA:') && line.includes('TYPE=CLOSED-CAPTIONS') && ((audio_track != VALID_AUDIO_TRACKS[0]) || (audio_track != VALID_AUDIO_TRACKS[1]) || (resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1])) ) {
|
|
558
584
|
return
|
|
559
585
|
}
|
|
560
586
|
|
|
561
587
|
// Parse audio tracks to only include matching one, if specified
|
|
562
|
-
if ( line.startsWith('#EXT-X-MEDIA:TYPE=AUDIO') ) {
|
|
588
|
+
if ( line.startsWith('#EXT-X-MEDIA:') && line.includes('TYPE=AUDIO') ) {
|
|
563
589
|
// if we've already returned our desired audio track, we can skip subsequent ones
|
|
564
590
|
if ( audio_track_matched ) return
|
|
565
591
|
|
|
@@ -573,7 +599,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
573
599
|
let audio_output = ''
|
|
574
600
|
|
|
575
601
|
// default TV audio, or park sounds
|
|
576
|
-
if ( !line.includes('
|
|
602
|
+
if ( !line.includes('URI=') ) {
|
|
577
603
|
// only include default embedded audio track if requested or if filtering for park audio
|
|
578
604
|
if ( (audio_track == VALID_AUDIO_TRACKS[1]) || (audio_track == VALID_AUDIO_TRACKS[6]) ) {
|
|
579
605
|
audio_track_matched = true
|
|
@@ -600,7 +626,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
600
626
|
|
|
601
627
|
if ( line.match ) {
|
|
602
628
|
//var parsed = line.match(/URI="([^"]+)"?$/)
|
|
603
|
-
var parsed = line.match('
|
|
629
|
+
var parsed = line.match('URI="([^"]+)"')
|
|
604
630
|
if ( parsed[1] ) {
|
|
605
631
|
newurl = http_root + '/playlist.m3u8?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim()))
|
|
606
632
|
if ( force_vod != VALID_FORCE_VOD[0] ) newurl += '&force_vod=on'
|
|
@@ -653,8 +679,8 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
653
679
|
}
|
|
654
680
|
|
|
655
681
|
// Pass through any remaining caption tracks
|
|
656
|
-
if ( line.startsWith('#EXT-X-MEDIA:TYPE=SUBTITLES
|
|
657
|
-
var parsed = line.match('
|
|
682
|
+
if ( line.startsWith('#EXT-X-MEDIA:') && line.includes('TYPE=SUBTITLES') ) {
|
|
683
|
+
var parsed = line.match('URI="([^"]+)"')
|
|
658
684
|
if ( parsed[1] ) {
|
|
659
685
|
newurl = http_root + '/playlist.m3u8?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim()))
|
|
660
686
|
if ( force_vod != VALID_FORCE_VOD[0] ) newurl += '&force_vod=on'
|
|
@@ -757,7 +783,7 @@ app.get('/playlist.m3u8', async function(req, res) {
|
|
|
757
783
|
requestRetry(u, headers, function(err, response) {
|
|
758
784
|
if (err) return res.error(err)
|
|
759
785
|
|
|
760
|
-
|
|
786
|
+
session.debuglog(response.body)
|
|
761
787
|
|
|
762
788
|
var body = response.body.replace(/^\s+|\s+$/g, '').split('\n')
|
|
763
789
|
|
|
@@ -1020,12 +1046,12 @@ app.get('/gamechanger.m3u8', async function(req, res) {
|
|
|
1020
1046
|
|
|
1021
1047
|
var resolution
|
|
1022
1048
|
if ( req.query.resolution && (req.query.resolution == 'best') ) {
|
|
1023
|
-
resolution = VALID_RESOLUTIONS[
|
|
1049
|
+
resolution = VALID_RESOLUTIONS[1]
|
|
1024
1050
|
} else {
|
|
1025
1051
|
resolution = session.returnValidItem(req.query.resolution, VALID_RESOLUTIONS)
|
|
1026
1052
|
}
|
|
1027
1053
|
if ( resolution == VALID_RESOLUTIONS[0] ) {
|
|
1028
|
-
resolution = VALID_RESOLUTIONS[
|
|
1054
|
+
resolution = VALID_RESOLUTIONS[1]
|
|
1029
1055
|
}
|
|
1030
1056
|
|
|
1031
1057
|
var streamFinder = ''
|
|
@@ -1051,7 +1077,9 @@ app.get('/gamechanger.m3u8', async function(req, res) {
|
|
|
1051
1077
|
content_protect = '&content_protect=' + session.protection.content_protect
|
|
1052
1078
|
}
|
|
1053
1079
|
|
|
1054
|
-
var body = '#EXTM3U' + '\n' + '#EXT-X-INDEPENDENT-SEGMENTS' + '\n' + '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="
|
|
1080
|
+
var body = '#EXTM3U' + '\n' + '#EXT-X-INDEPENDENT-SEGMENTS' + '\n' + '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES' + '\n'
|
|
1081
|
+
// disable captions
|
|
1082
|
+
// + '#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cc",LANGUAGE="en",NAME="English",INSTREAM-ID="CC1",AUTOSELECT=YES,DEFAULT=YES' + '\n'
|
|
1055
1083
|
|
|
1056
1084
|
for ( gamechanger_resolution in GAMECHANGER_RESOLUTIONS ) {
|
|
1057
1085
|
if ( resolution == gamechanger_resolution ) {
|
|
@@ -2259,7 +2287,7 @@ app.get('/', async function(req, res) {
|
|
|
2259
2287
|
}
|
|
2260
2288
|
body += '</p>' + "\n"
|
|
2261
2289
|
|
|
2262
|
-
body += '<p><span class="tooltip">Skip<span class="tooltiptext">For video streams only (use the video "none" option above to apply it to audio streams): you can remove all breaks, idle time, non-action pitches, or only commercial breaks from the stream (useful to make your own "condensed games").<br/><br/>
|
|
2290
|
+
body += '<p><span class="tooltip">Skip<span class="tooltiptext">For video streams only (use the video "none" option above to apply it to audio streams): you can remove all breaks, idle time, non-action pitches, or only commercial breaks from the stream (useful to make your own "condensed games").<br/><br/>NOTES: skip timings are only generated when the stream is loaded -- so for live games, it will only skip up to the time you loaded the stream. Also, commercial skip will not work on pre-2024 games, or on MiLB games -- use skip breaks instead.</span></span>: '
|
|
2263
2291
|
for (var i = 0; i < VALID_SKIP.length; i++) {
|
|
2264
2292
|
body += '<button '
|
|
2265
2293
|
if ( skip == VALID_SKIP[i] ) body += 'class="default" '
|
package/package.json
CHANGED
package/session.js
CHANGED
|
@@ -3137,28 +3137,41 @@ class sessionClass {
|
|
|
3137
3137
|
// Get variant playlist
|
|
3138
3138
|
async getVariantPlaylist(streamURL, streamURLToken) {
|
|
3139
3139
|
try {
|
|
3140
|
-
this.debuglog('getVariantPlaylist')
|
|
3141
|
-
|
|
3142
|
-
// MLB version
|
|
3143
|
-
let variant = '_5600K'
|
|
3144
|
-
if ( streamURL.includes('milb.com') ) {
|
|
3145
|
-
variant = '_1280x720_59_5472K'
|
|
3146
|
-
}
|
|
3140
|
+
this.debuglog('getVariantPlaylist from ' + streamURL)
|
|
3147
3141
|
|
|
3148
|
-
let
|
|
3142
|
+
let master_url = 'http://localhost:' + this.data.port + '/master.m3u8?streamURL=' + encodeURIComponent(streamURL)
|
|
3149
3143
|
if ( streamURLToken ) {
|
|
3150
|
-
|
|
3144
|
+
master_url += '&streamURLToken=' + encodeURIComponent(streamURLToken)
|
|
3151
3145
|
}
|
|
3152
3146
|
if ( this.protection.content_protect ) {
|
|
3153
|
-
|
|
3147
|
+
master_url += '&content_protect=' + this.protection.content_protect
|
|
3154
3148
|
}
|
|
3149
|
+
this.debuglog('getVariantPlaylist checking master ' + master_url)
|
|
3155
3150
|
let reqObj = {
|
|
3156
|
-
url:
|
|
3151
|
+
url: master_url
|
|
3157
3152
|
}
|
|
3158
3153
|
var response = await this.httpGet(reqObj, false)
|
|
3159
3154
|
var body = response.replace(/^\s+|\s+$/g, '').split('\n')
|
|
3160
3155
|
|
|
3161
|
-
|
|
3156
|
+
let variant_url
|
|
3157
|
+
for (var i=0; i<body.length; i++) {
|
|
3158
|
+
let line = body[i];
|
|
3159
|
+
if ( line.includes('RESOLUTION=1280x720') && line.includes('FRAME-RATE=59.94') ) {
|
|
3160
|
+
variant_url = 'http://localhost:' + this.data.port + body[i+1]
|
|
3161
|
+
break
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
if (variant_url) {
|
|
3166
|
+
this.debuglog('getVariantPlaylist found variant ' + variant_url)
|
|
3167
|
+
reqObj = {
|
|
3168
|
+
url: variant_url
|
|
3169
|
+
}
|
|
3170
|
+
response = await this.httpGet(reqObj, false)
|
|
3171
|
+
body = response.replace(/^\s+|\s+$/g, '').split('\n')
|
|
3172
|
+
|
|
3173
|
+
return body
|
|
3174
|
+
}
|
|
3162
3175
|
} catch(e) {
|
|
3163
3176
|
this.log('getVariantPlaylist error : ' + e.message)
|
|
3164
3177
|
}
|