mlbserver 2025.2.16 → 2025.2.21
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 -0
- package/index.js +77 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ services:
|
|
|
52
52
|
#- page_password=
|
|
53
53
|
#- content_protect=
|
|
54
54
|
#- gamechanger_delay=0
|
|
55
|
+
#- http_root=/mlbserver
|
|
55
56
|
ports:
|
|
56
57
|
- 9999:9999
|
|
57
58
|
- 10000:10000
|
|
@@ -111,6 +112,7 @@ Advanced command line or Docker environment options:
|
|
|
111
112
|
--page_password (password to protect pages; default is no protection)
|
|
112
113
|
--content_protect (specify the content protection key to include as a URL parameter, if page protection is enabled)
|
|
113
114
|
--gamechanger_delay (specify extra delay for the gamechanger switches in 10 second increments, default is 0)
|
|
115
|
+
--http_root (specify the alternative http webroot or initial path prefix, default is none)
|
|
114
116
|
```
|
|
115
117
|
|
|
116
118
|
For multiview, the default software encoder is limited by your CPU. You may want to experiment with different ffmpeg hardware encoders. "h264_videotoolbox" is confirmed to work on supported Macs, and "h264_v4l2m2m" is confirmed to work on a Raspberry Pi 4 (and likely other Linux systems) when ffmpeg is compiled with this patch: https://www.raspberrypi.org/forums/viewtopic.php?p=1780625#p1780625
|
package/index.js
CHANGED
|
@@ -29,10 +29,10 @@ const VALID_CONTROLS = [ 'Show', 'Hide' ]
|
|
|
29
29
|
const VALID_INNING_HALF = [ '', 'top', 'bottom' ]
|
|
30
30
|
const VALID_INNING_NUMBER = [ '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' ]
|
|
31
31
|
const VALID_SCORES = [ 'Hide', 'Show' ]
|
|
32
|
-
const VALID_RESOLUTIONS = [ 'adaptive', '720p60', '720p', '540p', '504p', '360p', 'none' ]
|
|
32
|
+
const VALID_RESOLUTIONS = [ 'adaptive', '1080p60', '720p60', '720p', '540p', '504p', '360p', '288p', '214p', 'none' ]
|
|
33
33
|
const DEFAULT_MULTIVIEW_RESOLUTION = '504p'
|
|
34
34
|
// Corresponding andwidths to display for above resolutions
|
|
35
|
-
const DISPLAY_BANDWIDTHS = [ '', '6600k', '4160k', '2950k', '2120k', '1400k', '' ]
|
|
35
|
+
const DISPLAY_BANDWIDTHS = [ '', '9600k', '6600k', '4160k', '2950k', '2120k', '1400k', '1000k', '600k', '' ]
|
|
36
36
|
const VALID_AUDIO_TRACKS = [ 'all', 'English', 'Home Radio', 'Casa Radio', 'Away Radio', 'Visita Radio', 'Park', 'none' ]
|
|
37
37
|
const DISPLAY_AUDIO_TRACKS = [ 'all', 'TV', 'Radio', 'Spa.', 'Away Rad.', 'Away Sp.', 'Park', 'none' ]
|
|
38
38
|
const DEFAULT_MULTIVIEW_AUDIO_TRACK = 'English'
|
|
@@ -103,7 +103,7 @@ var argv = minimist(process.argv, {
|
|
|
103
103
|
e: 'env'
|
|
104
104
|
},
|
|
105
105
|
boolean: ['ffmpeg_logging', 'debug', 'logout', 'session', 'cache', 'version', 'free', 'env'],
|
|
106
|
-
string: ['account_username', 'account_password', 'fav_teams', 'multiview_path', 'ffmpeg_path', 'ffmpeg_encoder', 'page_username', 'page_password', 'content_protect', 'data_directory']
|
|
106
|
+
string: ['account_username', 'account_password', 'fav_teams', 'multiview_path', 'ffmpeg_path', 'ffmpeg_encoder', 'page_username', 'page_password', 'content_protect', 'data_directory', 'http_root']
|
|
107
107
|
})
|
|
108
108
|
|
|
109
109
|
if (argv.env) argv = process.env
|
|
@@ -151,6 +151,22 @@ const ffmpegEncoder = argv.ffmpeg_encoder || defaultEncoder
|
|
|
151
151
|
// Declare web server
|
|
152
152
|
var app = root()
|
|
153
153
|
|
|
154
|
+
var http_root = '';
|
|
155
|
+
if (argv.http_root) {
|
|
156
|
+
http_root = argv.http_root
|
|
157
|
+
app.get(http_root + '/*', async function(req, res) {
|
|
158
|
+
if ( ! (await protect(req, res)) ) return
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
req.url = req.url.substr(http_root.length, (req.url.length - http_root.length))
|
|
162
|
+
app.route(req, res);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
session.log('http_root request error : ' + e.message)
|
|
165
|
+
res.end('http_root request error, check log')
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
154
170
|
// Get appname from directory
|
|
155
171
|
var appname = path.basename(__dirname)
|
|
156
172
|
|
|
@@ -202,7 +218,7 @@ app.get('/clearcache', async function(req, res) {
|
|
|
202
218
|
session.clear_session_data()
|
|
203
219
|
session = new sessionClass(argv)
|
|
204
220
|
|
|
205
|
-
let server = 'http://' + req.headers.host
|
|
221
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
206
222
|
res.redirect(server)
|
|
207
223
|
} catch (e) {
|
|
208
224
|
session.log('clearcache request error : ' + e.message)
|
|
@@ -231,11 +247,7 @@ app.get('/stream.m3u8', async function(req, res) {
|
|
|
231
247
|
streamURL = SAMPLE_STREAM_URL
|
|
232
248
|
options.referer = 'https://hls-js-dev.netlify.app/'
|
|
233
249
|
} else {
|
|
234
|
-
|
|
235
|
-
options.resolution = VALID_RESOLUTIONS[1]
|
|
236
|
-
} else {
|
|
237
|
-
options.resolution = session.returnValidItem(req.query.resolution, VALID_RESOLUTIONS)
|
|
238
|
-
}
|
|
250
|
+
options.resolution = req.query.resolution
|
|
239
251
|
options.audio_track = session.returnValidItem(req.query.audio_track, VALID_AUDIO_TRACKS)
|
|
240
252
|
options.force_vod = req.query.force_vod || VALID_FORCE_VOD[0]
|
|
241
253
|
|
|
@@ -455,8 +467,15 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
455
467
|
var audio_track_matched = false
|
|
456
468
|
var frame_rate = '29.97'
|
|
457
469
|
if ( (resolution != VALID_RESOLUTIONS[0]) && (resolution != VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1]) ) {
|
|
458
|
-
if ( resolution.endsWith('p60') ) {
|
|
470
|
+
if ( resolution.endsWith('p60') || (resolution == 'best') ) {
|
|
459
471
|
frame_rate = '59.94'
|
|
472
|
+
if ( resolution == 'best') {
|
|
473
|
+
if ( body.find(a =>a.includes("x1080,")) ) {
|
|
474
|
+
resolution = VALID_RESOLUTIONS[1]
|
|
475
|
+
} else {
|
|
476
|
+
resolution = VALID_RESOLUTIONS[2]
|
|
477
|
+
}
|
|
478
|
+
}
|
|
460
479
|
resolution = resolution.slice(0, -3)
|
|
461
480
|
} else if ( resolution.endsWith('p') ) {
|
|
462
481
|
resolution = resolution.slice(0, -1)
|
|
@@ -480,7 +499,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
480
499
|
return line
|
|
481
500
|
} else if ( segment_found ) {
|
|
482
501
|
segment_found = false
|
|
483
|
-
return '/ts?url='+encodeURIComponent(url.resolve(streamURL, line.trim())) + content_protect + referer_parameter + token_parameter
|
|
502
|
+
return http_root + '/ts?url='+encodeURIComponent(url.resolve(streamURL, line.trim())) + content_protect + referer_parameter + token_parameter
|
|
484
503
|
}
|
|
485
504
|
|
|
486
505
|
// Omit keyframe tracks
|
|
@@ -542,7 +561,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
542
561
|
//var parsed = line.match(/URI="([^"]+)"?$/)
|
|
543
562
|
var parsed = line.match(',URI="([^"]+)"')
|
|
544
563
|
if ( parsed[1] ) {
|
|
545
|
-
newurl = '/playlist?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim()))
|
|
564
|
+
newurl = http_root + '/playlist?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim()))
|
|
546
565
|
if ( force_vod != VALID_FORCE_VOD[0] ) newurl += '&force_vod=on'
|
|
547
566
|
if ( inning_half != VALID_INNING_HALF[0] ) newurl += '&inning_half=' + inning_half
|
|
548
567
|
if ( inning_number != VALID_INNING_NUMBER[0] ) newurl += '&inning_number=' + inning_number
|
|
@@ -574,17 +593,15 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
574
593
|
if ( line.startsWith('#EXT-X-STREAM-INF:BANDWIDTH=') ) {
|
|
575
594
|
if ( resolution == VALID_RESOLUTIONS[VALID_RESOLUTIONS.length-1] ) {
|
|
576
595
|
return
|
|
596
|
+
} else if ( resolution === VALID_RESOLUTIONS[0] ) {
|
|
597
|
+
return line
|
|
577
598
|
} else {
|
|
578
|
-
if ( resolution
|
|
599
|
+
if ( (video_track_found == false) && (line.indexOf(resolution+',FRAME-RATE='+frame_rate) > 0) ) {
|
|
600
|
+
video_track_matched = true
|
|
601
|
+
video_track_found = true
|
|
579
602
|
return line
|
|
580
603
|
} else {
|
|
581
|
-
|
|
582
|
-
video_track_matched = true
|
|
583
|
-
video_track_found = true
|
|
584
|
-
return line
|
|
585
|
-
} else {
|
|
586
|
-
return
|
|
587
|
-
}
|
|
604
|
+
return
|
|
588
605
|
}
|
|
589
606
|
}
|
|
590
607
|
}
|
|
@@ -598,7 +615,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
598
615
|
if ( line.startsWith('#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="') ) {
|
|
599
616
|
var parsed = line.match(',URI="([^"]+)"')
|
|
600
617
|
if ( parsed[1] ) {
|
|
601
|
-
newurl = '/playlist?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim())) + content_protect + referer_parameter + token_parameter
|
|
618
|
+
newurl = http_root + '/playlist?url='+encodeURIComponent(url.resolve(streamURL, parsed[1].trim())) + content_protect + referer_parameter + token_parameter
|
|
602
619
|
return '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="' + newurl + '"'
|
|
603
620
|
}
|
|
604
621
|
return
|
|
@@ -620,7 +637,7 @@ function getMasterPlaylist(streamURL, req, res, options = {}) {
|
|
|
620
637
|
if ( gamePk ) newurl += '&gamePk=' + gamePk
|
|
621
638
|
if ( audio_track != VALID_AUDIO_TRACKS[0] ) newurl += '&audio_track=' + encodeURIComponent(audio_track)
|
|
622
639
|
newurl += content_protect + referer_parameter + token_parameter
|
|
623
|
-
return '/playlist?url='+newurl
|
|
640
|
+
return http_root + '/playlist?url='+newurl
|
|
624
641
|
}
|
|
625
642
|
})
|
|
626
643
|
.filter(function(line) {
|
|
@@ -792,9 +809,9 @@ app.get('/playlist', async function(req, res) {
|
|
|
792
809
|
|
|
793
810
|
if (line[0] === '#') return line
|
|
794
811
|
|
|
795
|
-
let newline = '/ts'
|
|
812
|
+
let newline = http_root + '/ts'
|
|
796
813
|
if ( line.includes('.vtt') ) {
|
|
797
|
-
newline = '/vtt'
|
|
814
|
+
newline = http_root + '/vtt'
|
|
798
815
|
}
|
|
799
816
|
|
|
800
817
|
newline += '?url='+encodeURIComponent(url.resolve(u, line.trim())) + content_protect + referer_parameter + token_parameter
|
|
@@ -961,12 +978,12 @@ app.get('/gamechanger.m3u8', async function(req, res) {
|
|
|
961
978
|
|
|
962
979
|
var resolution
|
|
963
980
|
if ( req.query.resolution && (req.query.resolution == 'best') ) {
|
|
964
|
-
resolution = VALID_RESOLUTIONS[
|
|
981
|
+
resolution = VALID_RESOLUTIONS[2]
|
|
965
982
|
} else {
|
|
966
983
|
resolution = session.returnValidItem(req.query.resolution, VALID_RESOLUTIONS)
|
|
967
984
|
}
|
|
968
985
|
if ( resolution == VALID_RESOLUTIONS[0] ) {
|
|
969
|
-
resolution = VALID_RESOLUTIONS[
|
|
986
|
+
resolution = VALID_RESOLUTIONS[2]
|
|
970
987
|
}
|
|
971
988
|
|
|
972
989
|
var includeTeams = ''
|
|
@@ -1020,7 +1037,7 @@ app.get('/gamechangerplaylist', async function(req, res) {
|
|
|
1020
1037
|
} else {
|
|
1021
1038
|
var game_changer_title = 'Game changer ' + id + ' '
|
|
1022
1039
|
|
|
1023
|
-
var resolution = req.query.resolution || VALID_RESOLUTIONS[
|
|
1040
|
+
var resolution = req.query.resolution || VALID_RESOLUTIONS[2]
|
|
1024
1041
|
|
|
1025
1042
|
var includeTeams = req.query.includeTeams || []
|
|
1026
1043
|
if ( includeTeams.length > 0 ) includeTeams = includeTeams.split(',')
|
|
@@ -1219,8 +1236,9 @@ app.get('/', async function(req, res) {
|
|
|
1219
1236
|
|
|
1220
1237
|
session.requestlog('homepage', req)
|
|
1221
1238
|
|
|
1222
|
-
let server = 'http://' + req.headers.host
|
|
1239
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host
|
|
1223
1240
|
let multiview_server = server.replace(':' + session.data.port, ':' + session.data.multiviewPort)
|
|
1241
|
+
server += http_root
|
|
1224
1242
|
|
|
1225
1243
|
let gameDate = session.liveDate()
|
|
1226
1244
|
let today = gameDate
|
|
@@ -1366,7 +1384,7 @@ app.get('/', async function(req, res) {
|
|
|
1366
1384
|
body += 'var date="' + gameDate + '";var level="' + level + '";var org="' + org + '";var mediaType="' + mediaType + '";var resolution="' + resolution + '";var audio_track="' + audio_track + '";var force_vod="' + force_vod + '";var inning_half="' + inning_half + '";var inning_number="' + inning_number + '";var skip="' + skip + '";var skip_adjust="' + skip_adjust + '";var pad="' + pad + '";var linkType="' + linkType + '";var startFrom="' + startFrom + '";var scores="' + scores + '";var controls="' + controls + '";var scan_mode="' + scan_mode + '";var content_protect="' + content_protect + '";' + "\n"
|
|
1367
1385
|
|
|
1368
1386
|
// Reload function, called after options change
|
|
1369
|
-
body += 'var defaultDate="' + today + '";var curDate=new Date();var utcHours=curDate.getUTCHours();if ((utcHours >= ' + todayUTCHours + ') && (utcHours < ' + YESTERDAY_UTC_HOURS + ')){defaultDate="' + yesterday + '"}function reload(){var newurl="/?";if (date != defaultDate){var urldate=date;if (date == "' + today + '"){urldate="today"}else if (date == "' + yesterday + '"){urldate="yesterday"}newurl+="date="+urldate+"&"}if (level != "' + default_level + '"){newurl+="level="+encodeURIComponent(level)+"&"}if (org != "All"){newurl+="org="+encodeURIComponent(org)+"&"}if (mediaType != "' + VALID_MEDIA_TYPES[0] + '"){newurl+="mediaType="+mediaType+"&"}if (mediaType=="Video"){if (resolution != "' + VALID_RESOLUTIONS[0] + '"){newurl+="resolution="+resolution+"&"}if (audio_track != "' + VALID_AUDIO_TRACKS[0] + '"){newurl+="audio_track="+encodeURIComponent(audio_track)+"&"}else if (resolution == "none"){newurl+="audio_track="+encodeURIComponent("' + VALID_AUDIO_TRACKS[2] + '")+"&"}if (inning_half != "' + VALID_INNING_HALF[0] + '"){newurl+="inning_half="+inning_half+"&"}if (inning_number != "' + VALID_INNING_NUMBER[0] + '"){newurl+="inning_number="+inning_number+"&"}if (skip != "' + VALID_SKIP[0] + '"){newurl+="skip="+skip+"&";if (skip_adjust != "' + DEFAULT_SKIP_ADJUST + '"){newurl+="skip_adjust="+skip_adjust+"&"}}}if (pad != "' + VALID_PAD[0] + '"){newurl+="pad="+pad+"&";}if (linkType != "' + VALID_LINK_TYPES[0] + '"){newurl+="linkType="+linkType+"&"}if (linkType=="' + VALID_LINK_TYPES[0] + '"){if (startFrom != "' + VALID_START_FROM[0] + '"){newurl+="startFrom="+startFrom+"&"}if (controls != "' + VALID_CONTROLS[0] + '"){newurl+="controls="+controls+"&"}}if (linkType=="Stream"){if (force_vod != "' + VALID_FORCE_VOD[0] + '"){newurl+="force_vod="+force_vod+"&"}}if (scores != "' + VALID_SCORES[0] + '"){newurl+="scores="+scores+"&"}if (scan_mode != "' + session.data.scan_mode + '"){newurl+="scan_mode="+scan_mode+"&"}if (content_protect != ""){newurl+="content_protect="+content_protect+"&"}window.location=newurl.substring(0,newurl.length-1)}' + "\n"
|
|
1387
|
+
body += 'var defaultDate="' + today + '";var curDate=new Date();var utcHours=curDate.getUTCHours();if ((utcHours >= ' + todayUTCHours + ') && (utcHours < ' + YESTERDAY_UTC_HOURS + ')){defaultDate="' + yesterday + '"}function reload(){var newurl="' + http_root + '/?";if (date != defaultDate){var urldate=date;if (date == "' + today + '"){urldate="today"}else if (date == "' + yesterday + '"){urldate="yesterday"}newurl+="date="+urldate+"&"}if (level != "' + default_level + '"){newurl+="level="+encodeURIComponent(level)+"&"}if (org != "All"){newurl+="org="+encodeURIComponent(org)+"&"}if (mediaType != "' + VALID_MEDIA_TYPES[0] + '"){newurl+="mediaType="+mediaType+"&"}if (mediaType=="Video"){if (resolution != "' + VALID_RESOLUTIONS[0] + '"){newurl+="resolution="+resolution+"&"}if (audio_track != "' + VALID_AUDIO_TRACKS[0] + '"){newurl+="audio_track="+encodeURIComponent(audio_track)+"&"}else if (resolution == "none"){newurl+="audio_track="+encodeURIComponent("' + VALID_AUDIO_TRACKS[2] + '")+"&"}if (inning_half != "' + VALID_INNING_HALF[0] + '"){newurl+="inning_half="+inning_half+"&"}if (inning_number != "' + VALID_INNING_NUMBER[0] + '"){newurl+="inning_number="+inning_number+"&"}if (skip != "' + VALID_SKIP[0] + '"){newurl+="skip="+skip+"&";if (skip_adjust != "' + DEFAULT_SKIP_ADJUST + '"){newurl+="skip_adjust="+skip_adjust+"&"}}}if (pad != "' + VALID_PAD[0] + '"){newurl+="pad="+pad+"&";}if (linkType != "' + VALID_LINK_TYPES[0] + '"){newurl+="linkType="+linkType+"&"}if (linkType=="' + VALID_LINK_TYPES[0] + '"){if (startFrom != "' + VALID_START_FROM[0] + '"){newurl+="startFrom="+startFrom+"&"}if (controls != "' + VALID_CONTROLS[0] + '"){newurl+="controls="+controls+"&"}}if (linkType=="Stream"){if (force_vod != "' + VALID_FORCE_VOD[0] + '"){newurl+="force_vod="+force_vod+"&"}}if (scores != "' + VALID_SCORES[0] + '"){newurl+="scores="+scores+"&"}if (scan_mode != "' + session.data.scan_mode + '"){newurl+="scan_mode="+scan_mode+"&"}if (content_protect != ""){newurl+="content_protect="+content_protect+"&"}window.location=newurl.substring(0,newurl.length-1)}' + "\n"
|
|
1370
1388
|
|
|
1371
1389
|
// Ajax function for multiview and highlights
|
|
1372
1390
|
body += 'function makeGETRequest(url, callback){var request=new XMLHttpRequest();request.onreadystatechange=function(){if (request.readyState==4 && request.status==200){callback(request.responseText)}};request.open("GET", url);request.send();}' + "\n"
|
|
@@ -1499,7 +1517,7 @@ app.get('/', async function(req, res) {
|
|
|
1499
1517
|
} else {
|
|
1500
1518
|
force_vod = VALID_FORCE_VOD[0]
|
|
1501
1519
|
}
|
|
1502
|
-
var thislink = '/' + link
|
|
1520
|
+
var thislink = http_root + '/' + link
|
|
1503
1521
|
|
|
1504
1522
|
let blackouts = {}
|
|
1505
1523
|
|
|
@@ -1600,7 +1618,7 @@ app.get('/', async function(req, res) {
|
|
|
1600
1618
|
compareEnd.setHours(compareEnd.getHours()+4)
|
|
1601
1619
|
}
|
|
1602
1620
|
compareEnd.setHours(compareEnd.getHours()+4)
|
|
1603
|
-
body += '<tr><td><span class="tooltip">' + compareStart.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + ' - ' + compareEnd.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + '<span class="tooltiptext">The game changer stream will automatically switch between the highest leverage active live non-blackout games, and should be available whenever there are such games available. Does not support adaptive bitrate switching, will default to
|
|
1621
|
+
body += '<tr><td><span class="tooltip">' + compareStart.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + ' - ' + compareEnd.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }) + '<span class="tooltiptext">The game changer stream will automatically switch between the highest leverage active live non-blackout games, and should be available whenever there are such games available. Does not support adaptive bitrate switching, will default to 720p60 resolution if not specified.</span></span></td><td>'
|
|
1604
1622
|
if ( (currentDate >= compareStart) && (currentDate < compareEnd) ) {
|
|
1605
1623
|
let streamURL = server + '/gamechanger.m3u8'
|
|
1606
1624
|
let multiviewquerystring = streamURL + '?resolution=' + DEFAULT_MULTIVIEW_RESOLUTION + content_protect_b
|
|
@@ -2048,7 +2066,7 @@ app.get('/', async function(req, res) {
|
|
|
2048
2066
|
}
|
|
2049
2067
|
|
|
2050
2068
|
if ( mediaType == VALID_MEDIA_TYPES[0] ) {
|
|
2051
|
-
body += '<p><span class="tooltip">Video<span class="tooltiptext">For video streams only: you can manually specifiy a video track (resolution) to use. Adaptive will let your client choose. 720p60 is the best quality. 540p is default for multiview (see below).<br/><br/>None will allow to remove the video tracks, if you just want to listen to the audio while using the "start at inning" or "skip breaks" options enabled.</span></span>: '
|
|
2069
|
+
body += '<p><span class="tooltip">Video<span class="tooltiptext">For video streams only: you can manually specifiy a video track (resolution) to use. Adaptive will let your client choose. 1080p60 or 720p60 is the best quality. 540p is default for multiview (see below).<br/><br/>None will allow to remove the video tracks, if you just want to listen to the audio while using the "start at inning" or "skip breaks" options enabled.</span></span>: '
|
|
2052
2070
|
for (var i = 0; i < VALID_RESOLUTIONS.length; i++) {
|
|
2053
2071
|
body += '<button '
|
|
2054
2072
|
if ( resolution == VALID_RESOLUTIONS[i] ) body += 'class="default" '
|
|
@@ -2105,7 +2123,7 @@ app.get('/', async function(req, res) {
|
|
|
2105
2123
|
body += '<input type="checkbox" id="reencode"/> <span class="tooltip">Re-encode all audio<span class="tooltiptext">Uses more CPU. Generally only necessary if you need the multiview stream to continue after one of the individual streams has ended. (Any streams with sync adjustments above will automatically be re-encoded, regardless of this setting.)</span></span><br/>' + "\n"
|
|
2106
2124
|
body += '<input type="checkbox" id="park_audio"/> <span class="tooltip">Park audio: filter out announcers<span class="tooltiptext">Implies re-encoding all audio. If this is enabled, an extra audio filter is applied to remove the announcer voices.</span></span><br/>' + "\n"
|
|
2107
2125
|
body += '<hr><span class="tooltip">Alternate audio URL and sync<span class="tooltiptext">Optional: you can also include a separate audio-only URL as an additional alternate audio track. Archive games will likely require a very large negative sync value, as the radio broadcasts may not be trimmed like the video archives.</span></span>:<br/><textarea id="audio_url" rows=2 cols=60 oninput="this.value=stream_substitution(this.value)"></textarea><input id="audio_url_seek" type="number" value="0" style="vertical-align:top;font-size:.8em;width:4em"/>'
|
|
2108
|
-
body += '<hr>Watch: <a href="/embed.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Embed</a> | <a href="' + multiview_server + multiview_url_path + content_protect_b + '">Stream</a> | <a href="/chromecast.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Chromecast</a> | <a href="/advanced.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Advanced</a> | <a href="/download.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '&filename=' + gameDate + ' Multiview">Download</a><br/><span class="tinytext">Kodi STRM files: <a href="/kodi.strm?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Matrix/19+</a> (<a href="/kodi.strm?version=18&src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Leia/18</a>)</span>'
|
|
2126
|
+
body += '<hr>Watch: <a href="' + http_root + '/embed.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Embed</a> | <a href="' + multiview_server + multiview_url_path + content_protect_b + '">Stream</a> | <a href="' + http_root + '/chromecast.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Chromecast</a> | <a href="' + http_root + '/advanced.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Advanced</a> | <a href="' + http_root + '/download.html?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '&filename=' + gameDate + ' Multiview">Download</a><br/><span class="tinytext">Kodi STRM files: <a href="' + http_root + '/kodi.strm?src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Matrix/19+</a> (<a href="' + http_root + '/kodi.strm?version=18&src=' + encodeURIComponent(multiview_server + multiview_url_path) + content_protect_b + '">Leia/18</a>)</span>'
|
|
2109
2127
|
body += '</td></tr></table><br/>' + "\n"
|
|
2110
2128
|
}
|
|
2111
2129
|
|
|
@@ -2135,42 +2153,42 @@ app.get('/', async function(req, res) {
|
|
|
2135
2153
|
resolution = 'best'
|
|
2136
2154
|
}
|
|
2137
2155
|
|
|
2138
|
-
body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all live MLB broadcasts (all games plus MLB Network, Big Inning, Game Changer, and Multiview). If favorite team(s) have been provided, it will also include affiliate games for those organizations. 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> and <a href="/calendar.ics?mediaType=' + mediaType + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2156
|
+
body += '<p><span class="tooltip">All<span class="tooltiptext">Will include all live MLB broadcasts (all games plus MLB Network, Big Inning, Game Changer, and Multiview). If favorite team(s) have been provided, it will also include affiliate games for those organizations. Channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + content_protect_b + '">channels.m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + content_protect_b + '">guide.xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2139
2157
|
|
|
2140
2158
|
let include_teams = 'ath,national'
|
|
2141
2159
|
if ( session.credentials.fav_teams.length > 0 ) {
|
|
2142
2160
|
include_teams = session.credentials.fav_teams.toString()
|
|
2143
2161
|
}
|
|
2144
|
-
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. 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> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2162
|
+
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. Channels/games subject to blackout will be omitted by default. See below for an additional option to override that.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=' + include_teams + content_protect_b + '">channels.m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + content_protect_b + '">guide.xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2145
2163
|
|
|
2146
|
-
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> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2164
|
+
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="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">channels.m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">guide.xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeBlackouts=true' + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2147
2165
|
|
|
2148
2166
|
let exclude_teams = 'ath,national'
|
|
2149
|
-
body += '<p><span class="tooltip">Exclude a team + national<span class="tooltiptext">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 + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">ics</a></p>' + "\n"
|
|
2167
|
+
body += '<p><span class="tooltip">Exclude a team + national<span class="tooltiptext">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="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&excludeTeams=' + exclude_teams + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&excludeTeams=' + exclude_teams + content_protect_b + '">ics</a></p>' + "\n"
|
|
2150
2168
|
|
|
2151
|
-
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 + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2169
|
+
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="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=lidom' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=lidom' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2152
2170
|
|
|
2153
|
-
body += '<p><span class="tooltip">Include (or exclude) MLB Network<span class="tooltiptext">MLB Network live stream is now available in the USA for paid MLBTV subscribers 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>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=mlbn' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=mlbn' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=mlb' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2171
|
+
body += '<p><span class="tooltip">Include (or exclude) MLB Network<span class="tooltiptext">MLB Network live stream is now available in the USA for paid MLBTV subscribers 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>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=mlbn' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=mlbn' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=mlb' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2154
2172
|
|
|
2155
|
-
body += '<p><span class="tooltip">Include (or exclude) Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://www.mlb.com/live-stream-games/big-inning">See here for more information</a>.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=biginning' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2173
|
+
body += '<p><span class="tooltip">Include (or exclude) Big Inning<span class="tooltiptext">Big Inning is the live look-in and highlights show. <a href="https://www.mlb.com/live-stream-games/big-inning">See here for more information</a>.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=biginning' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=biginning' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2156
2174
|
|
|
2157
2175
|
let gamechanger_resolution = resolution
|
|
2158
2176
|
if ( gamechanger_resolution == VALID_RESOLUTIONS[0] ) {
|
|
2159
2177
|
gamechanger_resolution = 'best'
|
|
2160
2178
|
}
|
|
2161
|
-
body += '<p><span class="tooltip">Include (or exclude) Game Changer<span class="tooltiptext">The game changer stream will automatically switch between the highest leverage active live non-blackout games, and should be available whenever there are such games available. Does not support adaptive bitrate switching, will default to best resolution if not specified.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&resolution=' + gamechanger_resolution + '&includeTeams=gamechanger' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=gamechanger' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=gamechanger' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2179
|
+
body += '<p><span class="tooltip">Include (or exclude) Game Changer<span class="tooltiptext">The game changer stream will automatically switch between the highest leverage active live non-blackout games, and should be available whenever there are such games available. Does not support adaptive bitrate switching, will default to best resolution if not specified.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + gamechanger_resolution + '&includeTeams=gamechanger' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=gamechanger' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=gamechanger' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2162
2180
|
|
|
2163
|
-
body += '<p><span class="tooltip">Include (or exclude) Multiview<span class="tooltiptext">Requires starting and stopping the multiview stream from the web interface.</span></span>: <a href="/channels.m3u?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2181
|
+
body += '<p><span class="tooltip">Include (or exclude) Multiview<span class="tooltiptext">Requires starting and stopping the multiview stream from the web interface.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=multiview' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2164
2182
|
|
|
2165
2183
|
if ( argv.free ) {
|
|
2166
|
-
body += '<p><span class="tooltip">Free games only<span class="tooltiptext">Only includes games marked as free. Blackouts still apply. 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 + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeTeams=free' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeTeams=free' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2184
|
+
body += '<p><span class="tooltip">Free games only<span class="tooltiptext">Only includes games marked as free. Blackouts still apply. Channels/games subject to blackout will be omitted by default.</span></span>: <a href="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeTeams=free' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=free' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=free' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2167
2185
|
}
|
|
2168
2186
|
|
|
2169
|
-
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=ath,atl' + content_protect_b + '">m3u</a> and <a href="/guide.xml?mediaType=' + mediaType + '&includeOrgs=ath' + content_protect_b + '">xml</a> and <a href="/calendar.ics?mediaType=' + mediaType + '&includeOrgs=ath' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2187
|
+
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="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeOrgs=ath,atl' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeOrgs=ath' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeOrgs=ath' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2170
2188
|
|
|
2171
|
-
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"
|
|
2189
|
+
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="' + http_root + '/channels.m3u?mediaType=' + mediaType + '&resolution=' + resolution + '&includeLevels=a%2B,aaa' + content_protect_b + '">m3u</a> and <a href="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeLevels=a%2B,aaa' + content_protect_b + '">xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeLevels=a%2B,aaa' + content_protect_b + '">ics</a></p>' + "\n"
|
|
2172
2190
|
|
|
2173
|
-
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"
|
|
2191
|
+
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="' + http_root + '/guide.xml?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeTeamsInTitles=true' + content_protect_b + '">guide.xml</a> and <a href="' + http_root + '/calendar.ics?mediaType=' + mediaType + '&includeTeams=' + include_teams + '&includeTeamsInTitles=true' + content_protect_b + '">calendar.ics</a></p>' + "\n"
|
|
2174
2192
|
|
|
2175
2193
|
body += '</td></tr></table><br/>' + "\n"
|
|
2176
2194
|
|
|
@@ -2207,7 +2225,7 @@ app.get('/', async function(req, res) {
|
|
|
2207
2225
|
for (var i=0; i<examples.length; i++) {
|
|
2208
2226
|
body += '• ' + examples[i][0] + ': '
|
|
2209
2227
|
for (var j=0; j<example_types.length; j++) {
|
|
2210
|
-
body += '<a href="/' + example_types[j][0] + examples[i][1]
|
|
2228
|
+
body += '<a href="' + http_root + '/' + example_types[j][0] + examples[i][1]
|
|
2211
2229
|
body += content_protect_b
|
|
2212
2230
|
body += '">' + example_types[j][1] + '</a>'
|
|
2213
2231
|
if ( j < (example_types.length-1) ) {
|
|
@@ -2225,17 +2243,17 @@ app.get('/', async function(req, res) {
|
|
|
2225
2243
|
let gamechanger_types = ['in', 'ex']
|
|
2226
2244
|
for (var i=0; i<gamechanger_types.length; i++) {
|
|
2227
2245
|
let example_streamURL = gamechanger_streamURL + '&' + gamechanger_types[i] + 'cludeTeams=ath'
|
|
2228
|
-
body += '• ' + gamechanger_types[i] + 'clude: <a href="/embed.html?src=' + encodeURIComponent(example_streamURL) + '&startFrom=' + VALID_START_FROM[1] + content_protect_b + '">Embed</a> | <a href="' + example_streamURL + '">Stream</a> | <a href="/chromecast.html?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Chromecast</a> | <a href="/advanced.html?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Advanced</a> | <a href="/kodi.strm?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Kodi</a><br/>' + "\n"
|
|
2246
|
+
body += '• ' + gamechanger_types[i] + 'clude: <a href="' + http_root + '/embed.html?src=' + encodeURIComponent(example_streamURL) + '&startFrom=' + VALID_START_FROM[1] + content_protect_b + '">Embed</a> | <a href="' + example_streamURL + '">Stream</a> | <a href="' + http_root + '/chromecast.html?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Chromecast</a> | <a href="' + http_root + '/advanced.html?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Advanced</a> | <a href="' + http_root + '/kodi.strm?src=' + encodeURIComponent(example_streamURL) + content_protect_b + '">Kodi</a><br/>' + "\n"
|
|
2229
2247
|
}
|
|
2230
2248
|
|
|
2231
2249
|
body += '</p></td></tr></table><br/>' + "\n"
|
|
2232
2250
|
|
|
2233
|
-
body += '<p><span class="tooltip">Sample video<span class="tooltiptext">A sample stream. Useful for testing and troubleshooting.</span></span>: <a href="/embed.html' + content_protect_a + '">Embed</a> | <a href="/stream.m3u8' + content_protect_a + '">Stream</a> | <a href="/chromecast.html' + content_protect_a + '">Chromecast</a> | <a href="/advanced.html' + content_protect_a + '">Advanced</a></p>' + "\n"
|
|
2251
|
+
body += '<p><span class="tooltip">Sample video<span class="tooltiptext">A sample stream. Useful for testing and troubleshooting.</span></span>: <a href="' + http_root + '/embed.html' + content_protect_a + '">Embed</a> | <a href="' + http_root + '/stream.m3u8' + content_protect_a + '">Stream</a> | <a href="' + http_root + '/chromecast.html' + content_protect_a + '">Chromecast</a> | <a href="' + http_root + '/advanced.html' + content_protect_a + '">Advanced</a></p>' + "\n"
|
|
2234
2252
|
|
|
2235
2253
|
body += '<p><span class="tooltip">Bookmarklets for MLB.com<span class="tooltiptext">If you watch at MLB.com, drag these bookmarklets to your bookmarks toolbar and use them to hide parts of the interface.</span></span>: <a href="javascript:(function(){let x=document.querySelector(\'#mlbtv-stats-panel\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}})();">Boxscore</a> | <a href="javascript:(function(){let x=document.querySelector(\'.mlbtv-header-container\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">Scoreboard</a> | <a href="javascript:(function(){let x=document.querySelector(\'.mlbtv-container--footer\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">Linescore</a> | <a href="javascript:(function(){let x=document.querySelector(\'#mlbtv-stats-panel\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}x=document.querySelector(\'.mlbtv-header-container\');if(x.style.display==\'none\'){x.style.display=\'initial\';}else{x.style.display=\'none\';}x=document.querySelector(\'.mlbtv-container--footer\');if(x.style.display==\'none\'){let y=document.querySelector(\'.mlbtv-players-container\');y.style.display=\'none\';x.style.display=\'initial\';setTimeout(function(){y.style.display=\'initial\';},15);}else{x.style.display=\'none\';}})();">All</a></p>' + "\n"
|
|
2236
2254
|
|
|
2237
2255
|
// Print version
|
|
2238
|
-
body += '<p class="tinytext">Version ' + version + ' (<a href="/clearcache">clear session and cache</a>)</p>' + "\n"
|
|
2256
|
+
body += '<p class="tinytext">Version ' + version + ' (<a href="' + http_root + '/clearcache">clear session and cache</a>)</p>' + "\n"
|
|
2239
2257
|
|
|
2240
2258
|
// Datepicker functions
|
|
2241
2259
|
body += '<script>var datePicker=document.getElementById("gameDate");function changeDate(e){date=datePicker.value;reload()}function removeDate(e){datePicker.removeEventListener("change",changeDate,false);datePicker.addEventListener("blur",changeDate,false);if(e.keyCode===13){date=datePicker.value;reload()}}datePicker.addEventListener("change",changeDate,false);datePicker.addEventListener("keypress",removeDate,false)</script>' + "\n"
|
|
@@ -2286,7 +2304,7 @@ app.get('/embed.html', async function(req, res) {
|
|
|
2286
2304
|
controls = req.query.controls
|
|
2287
2305
|
}
|
|
2288
2306
|
|
|
2289
|
-
let video_url = '/stream.m3u8'
|
|
2307
|
+
let video_url = http_root + '/stream.m3u8'
|
|
2290
2308
|
if ( req.query.src ) {
|
|
2291
2309
|
video_url = req.query.src
|
|
2292
2310
|
} else {
|
|
@@ -2303,7 +2321,7 @@ app.get('/embed.html', async function(req, res) {
|
|
|
2303
2321
|
}
|
|
2304
2322
|
|
|
2305
2323
|
// Adapted from https://hls-js.netlify.app/demo/basic-usage.html and https://hls-js-dev.netlify.app/demo
|
|
2306
|
-
var body = '<html><head><meta charset="UTF-8"><meta http-equiv="Content-type" content="text/html;charset=UTF-8"><title>' + appname + ' player</title><link rel="icon" href="favicon.svg' + content_protect + '"><style type="text/css">input[type=text],input[type=button]{-webkit-appearance:none;-webkit-border-radius:0}body{background-color:black;color:lightgrey;font-family:Arial,Helvetica,sans-serif}video{width:100% !important;height:auto !important;max-width:1280px}input[type=number]::-webkit-inner-spin-button{opacity:1}button{color:lightgray;background-color:black}button.default{color:black;background-color:lightgray}</style><script>function goBack(){var prevPage=window.location.href;window.history.go(-1);setTimeout(function(){if(window.location.href==prevPage){window.location.href="/' + content_protect + '"}}, 500)}function toggleAudio(x){var elements=document.getElementsByClassName("audioButton");for(var i=0;i<elements.length;i++){elements[i].className="audioButton"}document.getElementById("audioButton"+x).className+=" default";hls.audioTrack=x}function changeTime(x){video.currentTime+=x}function changeRate(x){let newRate=Math.round((Number(document.getElementById("playback_rate").value)+x)*10)/10;if((newRate<=document.getElementById("playback_rate").max) && (newRate>=document.getElementById("playback_rate").min)){document.getElementById("playback_rate").value=newRate.toFixed(1);video.defaultPlaybackRate=video.playbackRate=document.getElementById("playback_rate").value}}function myKeyPress(e){if(e.key=="ArrowRight"){changeTime(10)}else if(e.key=="ArrowLeft"){changeTime(-10)}else if(e.key=="ArrowUp"){changeRate(0.1)}else if(e.key=="ArrowDown"){changeRate(-0.1)}}</script></head><body onkeydown="myKeyPress(event)"><script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script><video id="video"'
|
|
2324
|
+
var body = '<html><head><meta charset="UTF-8"><meta http-equiv="Content-type" content="text/html;charset=UTF-8"><title>' + appname + ' player</title><link rel="icon" href="favicon.svg' + content_protect + '"><style type="text/css">input[type=text],input[type=button]{-webkit-appearance:none;-webkit-border-radius:0}body{background-color:black;color:lightgrey;font-family:Arial,Helvetica,sans-serif}video{width:100% !important;height:auto !important;max-width:1280px}input[type=number]::-webkit-inner-spin-button{opacity:1}button{color:lightgray;background-color:black}button.default{color:black;background-color:lightgray}</style><script>function goBack(){var prevPage=window.location.href;window.history.go(-1);setTimeout(function(){if(window.location.href==prevPage){window.location.href="' + http_root + '/' + content_protect + '"}}, 500)}function toggleAudio(x){var elements=document.getElementsByClassName("audioButton");for(var i=0;i<elements.length;i++){elements[i].className="audioButton"}document.getElementById("audioButton"+x).className+=" default";hls.audioTrack=x}function changeTime(x){video.currentTime+=x}function changeRate(x){let newRate=Math.round((Number(document.getElementById("playback_rate").value)+x)*10)/10;if((newRate<=document.getElementById("playback_rate").max) && (newRate>=document.getElementById("playback_rate").min)){document.getElementById("playback_rate").value=newRate.toFixed(1);video.defaultPlaybackRate=video.playbackRate=document.getElementById("playback_rate").value}}function myKeyPress(e){if(e.key=="ArrowRight"){changeTime(10)}else if(e.key=="ArrowLeft"){changeTime(-10)}else if(e.key=="ArrowUp"){changeRate(0.1)}else if(e.key=="ArrowDown"){changeRate(-0.1)}}</script></head><body onkeydown="myKeyPress(event)"><script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script><video id="video"'
|
|
2307
2325
|
if ( controls == VALID_CONTROLS[0] ) {
|
|
2308
2326
|
body += ' controls'
|
|
2309
2327
|
}
|
|
@@ -2327,7 +2345,7 @@ app.get('/advanced.html', async function(req, res) {
|
|
|
2327
2345
|
|
|
2328
2346
|
session.requestlog('advanced.html', req)
|
|
2329
2347
|
|
|
2330
|
-
let server = 'http://' + req.headers.host
|
|
2348
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2331
2349
|
|
|
2332
2350
|
let video_url = '/stream.m3u8'
|
|
2333
2351
|
if ( req.query.src ) {
|
|
@@ -2350,7 +2368,7 @@ app.get('/chromecast.html', async function(req, res) {
|
|
|
2350
2368
|
|
|
2351
2369
|
session.requestlog('chromecast.html', req)
|
|
2352
2370
|
|
|
2353
|
-
let server = 'http://' + req.headers.host
|
|
2371
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2354
2372
|
|
|
2355
2373
|
let video_url = '/stream.m3u8'
|
|
2356
2374
|
if ( req.query.src ) {
|
|
@@ -2387,7 +2405,7 @@ app.get('/channels.m3u', async function(req, res) {
|
|
|
2387
2405
|
excludeTeams = req.query.excludeTeams.toUpperCase().split(',')
|
|
2388
2406
|
}
|
|
2389
2407
|
|
|
2390
|
-
let server = 'http://' + req.headers.host
|
|
2408
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2391
2409
|
|
|
2392
2410
|
let resolution = 'best'
|
|
2393
2411
|
if ( req.query.resolution ) {
|
|
@@ -2466,7 +2484,7 @@ app.get('/calendar.ics', async function(req, res) {
|
|
|
2466
2484
|
includeTeamsInTitles = req.query.includeTeamsInTitles
|
|
2467
2485
|
}
|
|
2468
2486
|
|
|
2469
|
-
let server = 'http://' + req.headers.host
|
|
2487
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2470
2488
|
|
|
2471
2489
|
var body = await session.getTVData('calendar', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, includeTeamsInTitles)
|
|
2472
2490
|
|
|
@@ -2519,7 +2537,7 @@ app.get('/guide.xml', async function(req, res) {
|
|
|
2519
2537
|
includeTeamsInTitles = req.query.includeTeamsInTitles
|
|
2520
2538
|
}
|
|
2521
2539
|
|
|
2522
|
-
let server = 'http://' + req.headers.host
|
|
2540
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2523
2541
|
|
|
2524
2542
|
var body = await session.getTVData('guide', mediaType, includeTeams, excludeTeams, includeLevels, includeOrgs, server, includeBlackouts, includeTeamsInTitles)
|
|
2525
2543
|
|
|
@@ -2893,7 +2911,7 @@ app.get('/kodi.strm', async function(req, res) {
|
|
|
2893
2911
|
try {
|
|
2894
2912
|
session.requestlog('kodi.strm', req)
|
|
2895
2913
|
|
|
2896
|
-
let server = 'http://' + req.headers.host
|
|
2914
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2897
2915
|
|
|
2898
2916
|
let video_url = '/stream.m3u8'
|
|
2899
2917
|
let file_name = 'kodi'
|
|
@@ -2943,7 +2961,7 @@ app.get('/download.html', async function(req, res) {
|
|
|
2943
2961
|
session.debuglog('force alternate audio', req)
|
|
2944
2962
|
}
|
|
2945
2963
|
|
|
2946
|
-
let server = 'http://' + req.headers.host
|
|
2964
|
+
let server = (req.headers['x-forwarded-proto'] ? req.headers['x-forwarded-proto'] : 'http') + '://' + req.headers.host + http_root
|
|
2947
2965
|
|
|
2948
2966
|
let video_url = '/stream.m3u8'
|
|
2949
2967
|
if ( req.query.src ) {
|