@warren-bank/hls-proxy 3.1.2 → 3.2.1
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 +46 -2
- package/hls-proxy/cookies.js +17 -0
- package/hls-proxy/manifest_parser.js +489 -489
- package/hls-proxy/proxy.js +5 -1
- package/hls-proxy/segment_cache.js +2 -1
- package/hls-proxy/timers.js +44 -0
- package/hls-proxy/utils.js +157 -157
- package/package.json +3 -2
package/hls-proxy/proxy.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const request = require('@warren-bank/node-request').request
|
|
2
|
+
const cookies = require('./cookies')
|
|
2
3
|
const parser = require('./manifest_parser')
|
|
4
|
+
const timers = require('./timers')
|
|
3
5
|
const utils = require('./utils')
|
|
4
6
|
|
|
5
7
|
const get_middleware = function(params) {
|
|
@@ -70,7 +72,7 @@ const get_middleware = function(params) {
|
|
|
70
72
|
debug(1, 'proxying:', url)
|
|
71
73
|
debug(3, 'm3u8:', (is_m3u8 ? 'true' : 'false'))
|
|
72
74
|
|
|
73
|
-
request(options, '', {binary: !is_m3u8, stream: !is_m3u8})
|
|
75
|
+
request(options, '', {binary: !is_m3u8, stream: !is_m3u8, cookieJar: cookies.getCookieJar()})
|
|
74
76
|
.then(({redirects, response}) => {
|
|
75
77
|
debug(2, 'proxied response:', {status_code: response.statusCode, headers: response.headers, redirects})
|
|
76
78
|
|
|
@@ -93,6 +95,8 @@ const get_middleware = function(params) {
|
|
|
93
95
|
})
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
timers.initialize_timers(params)
|
|
99
|
+
|
|
96
100
|
return middleware
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const request = require('@warren-bank/node-request').request
|
|
2
|
+
const cookies = require('./cookies')
|
|
2
3
|
const utils = require('./utils')
|
|
3
4
|
|
|
4
5
|
module.exports = function(params) {
|
|
@@ -175,7 +176,7 @@ module.exports = function(params) {
|
|
|
175
176
|
ts[index] = {key: get_privatekey_from_url(url), databuffer: false}
|
|
176
177
|
|
|
177
178
|
let options = get_request_options(url, /* is_m3u8= */ false, referer_url)
|
|
178
|
-
promise = request(options, '', {binary: true, stream: false})
|
|
179
|
+
promise = request(options, '', {binary: true, stream: false, cookieJar: cookies.getCookieJar()})
|
|
179
180
|
.then(({redirects, response}) => {
|
|
180
181
|
debug(1, `prefetch (complete, ${response.length} bytes):`, debug_url)
|
|
181
182
|
debug(2, 'prefetch response:', {status_code: response.statusCode, headers: response.headers, redirects})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const request = require('@warren-bank/node-request').request
|
|
2
|
+
const cookies = require('./cookies')
|
|
3
|
+
const utils = require('./utils')
|
|
4
|
+
|
|
5
|
+
const initialize_timers = function(params) {
|
|
6
|
+
const {hooks} = params
|
|
7
|
+
|
|
8
|
+
if (hooks && (hooks instanceof Object) && hooks.request_intervals && (typeof hooks.request_intervals === 'function')) {
|
|
9
|
+
cookies.useCookieJar()
|
|
10
|
+
|
|
11
|
+
const get_request_options = utils.get_request_options.bind(null, params)
|
|
12
|
+
|
|
13
|
+
const request_wrapper = function(url, POST_data, user_config) {
|
|
14
|
+
const options = get_request_options(url, /* is_m3u8= */ false, /* referer_url= */ null)
|
|
15
|
+
const config = Object.assign(
|
|
16
|
+
{},
|
|
17
|
+
(user_config || {}),
|
|
18
|
+
{
|
|
19
|
+
cookieJar: cookies.getCookieJar()
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return request(options, POST_data, config)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const add_request_interval = function(delay_ms, callback) {
|
|
27
|
+
if (!delay_ms || (typeof delay_ms !== 'number') || isNaN(delay_ms) || (delay_ms < 0)) {
|
|
28
|
+
callback(request_wrapper)
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
setInterval(
|
|
32
|
+
callback.bind(null, request_wrapper),
|
|
33
|
+
delay_ms
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
hooks.request_intervals(add_request_interval)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
initialize_timers
|
|
44
|
+
}
|
package/hls-proxy/utils.js
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
const parse_url = require('@warren-bank/url').parse
|
|
2
|
-
|
|
3
|
-
const regexs = {
|
|
4
|
-
req_url: new RegExp('^(
|
|
5
|
-
origin: new RegExp('^(https?://[^/]+)(?:/.*)?$', 'i')
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// btoa
|
|
9
|
-
const base64_encode = function(str) {
|
|
10
|
-
return Buffer.from(str, 'binary').toString('base64')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// atob
|
|
14
|
-
const base64_decode = function(str) {
|
|
15
|
-
return Buffer.from(str, 'base64').toString('binary')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const parse_req_url = function(params, req) {
|
|
19
|
-
const {is_secure, host} = params
|
|
20
|
-
|
|
21
|
-
const result = {redirected_base_url: '', url_type: '', url: '', referer_url: ''}
|
|
22
|
-
|
|
23
|
-
const matches = regexs.req_url.exec(req.url)
|
|
24
|
-
|
|
25
|
-
if (matches) {
|
|
26
|
-
result.redirected_base_url = `${ is_secure ? 'https' : 'http' }://${host || req.headers.host}${matches[1] || ''}`
|
|
27
|
-
|
|
28
|
-
if (matches[3])
|
|
29
|
-
result.url_type = matches[3].toLowerCase().trim()
|
|
30
|
-
|
|
31
|
-
let url, url_lc, index
|
|
32
|
-
|
|
33
|
-
url = base64_decode( matches[2] ).trim()
|
|
34
|
-
url_lc = url.toLowerCase()
|
|
35
|
-
index = url_lc.indexOf('http')
|
|
36
|
-
|
|
37
|
-
if (index === 0) {
|
|
38
|
-
index = url_lc.indexOf('|http')
|
|
39
|
-
|
|
40
|
-
if (index > 0) {
|
|
41
|
-
result.referer_url = url.substring(index + 1, url.length)
|
|
42
|
-
|
|
43
|
-
url = url.substring(0, index).trim()
|
|
44
|
-
}
|
|
45
|
-
result.url = url
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return result
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const get_content_type = function(url_type) {
|
|
53
|
-
let content_type
|
|
54
|
-
switch(url_type) {
|
|
55
|
-
case 'm3u8':
|
|
56
|
-
content_type = 'application/x-mpegurl'
|
|
57
|
-
break
|
|
58
|
-
case 'ts':
|
|
59
|
-
content_type = 'video/MP2T'
|
|
60
|
-
break
|
|
61
|
-
case 'json':
|
|
62
|
-
content_type = 'application/json'
|
|
63
|
-
break
|
|
64
|
-
case 'key':
|
|
65
|
-
case 'other':
|
|
66
|
-
default:
|
|
67
|
-
content_type = 'application/octet-stream'
|
|
68
|
-
break
|
|
69
|
-
}
|
|
70
|
-
return content_type
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const add_CORS_headers = function(res) {
|
|
74
|
-
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
75
|
-
res.setHeader('Access-Control-Allow-Methods', '*')
|
|
76
|
-
res.setHeader('Access-Control-Allow-Headers', '*')
|
|
77
|
-
res.setHeader('Access-Control-Allow-Credentials', 'true')
|
|
78
|
-
res.setHeader('Access-Control-Max-Age', '86400')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const debug = function() {
|
|
82
|
-
const args = [...arguments]
|
|
83
|
-
const params = args.shift()
|
|
84
|
-
const verbosity = args.shift()
|
|
85
|
-
const append_LF = true
|
|
86
|
-
|
|
87
|
-
const {debug_level} = params
|
|
88
|
-
|
|
89
|
-
if (append_LF) args.push("\n")
|
|
90
|
-
|
|
91
|
-
if (debug_level >= verbosity) {
|
|
92
|
-
console.log.apply(console.log, args)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const get_request_options = function(params, url, is_m3u8, referer_url) {
|
|
97
|
-
const {req_headers, req_options, hooks} = params
|
|
98
|
-
|
|
99
|
-
const additional_req_options = (hooks && (hooks instanceof Object) && hooks.add_request_options && (typeof hooks.add_request_options === 'function'))
|
|
100
|
-
? hooks.add_request_options(url, is_m3u8)
|
|
101
|
-
: null
|
|
102
|
-
|
|
103
|
-
const additional_req_headers = (hooks && (hooks instanceof Object) && hooks.add_request_headers && (typeof hooks.add_request_headers === 'function'))
|
|
104
|
-
? hooks.add_request_headers(url, is_m3u8)
|
|
105
|
-
: null
|
|
106
|
-
|
|
107
|
-
if (!req_options && !additional_req_options && !req_headers && !additional_req_headers && !referer_url) return url
|
|
108
|
-
|
|
109
|
-
const request_options = Object.assign(
|
|
110
|
-
{},
|
|
111
|
-
parse_url(url),
|
|
112
|
-
(req_options || {}),
|
|
113
|
-
(additional_req_options || {})
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
request_options.headers = Object.assign(
|
|
117
|
-
{},
|
|
118
|
-
(( req_options && req_options.headers) ? req_options.headers : {}),
|
|
119
|
-
((additional_req_options && additional_req_options.headers) ? additional_req_options.headers : {}),
|
|
120
|
-
(req_headers || {}),
|
|
121
|
-
(additional_req_headers || {}),
|
|
122
|
-
(referer_url ? {"referer": referer_url, "origin": referer_url.replace(regexs.origin, '$1')} : {})
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
return request_options
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const should_prefetch_url = function(params, url, url_type) {
|
|
129
|
-
const {hooks, cache_segments} = params
|
|
130
|
-
|
|
131
|
-
let do_prefetch = !!url && !!cache_segments
|
|
132
|
-
|
|
133
|
-
if (do_prefetch) {
|
|
134
|
-
do_prefetch = (url_type === 'ts') || (url_type === 'key')
|
|
135
|
-
|
|
136
|
-
if (hooks && (hooks instanceof Object) && hooks.prefetch && (typeof hooks.prefetch === 'function')) {
|
|
137
|
-
const override_prefetch = hooks.prefetch(url, url_type)
|
|
138
|
-
|
|
139
|
-
if ((typeof override_prefetch === 'boolean') && (override_prefetch !== do_prefetch)) {
|
|
140
|
-
debug(params, 3, 'prefetch override:', (override_prefetch ? 'allow' : 'deny'), url)
|
|
141
|
-
do_prefetch = override_prefetch
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return do_prefetch
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
module.exports = {
|
|
149
|
-
base64_encode,
|
|
150
|
-
base64_decode,
|
|
151
|
-
parse_req_url,
|
|
152
|
-
get_content_type,
|
|
153
|
-
add_CORS_headers,
|
|
154
|
-
debug,
|
|
155
|
-
get_request_options,
|
|
156
|
-
should_prefetch_url
|
|
157
|
-
}
|
|
1
|
+
const parse_url = require('@warren-bank/url').parse
|
|
2
|
+
|
|
3
|
+
const regexs = {
|
|
4
|
+
req_url: new RegExp('^(.*?)/([a-zA-Z0-9\\+/=]+)(?:[\\._]([^/\\?#]*))?(?:[\\?#].*)?$'),
|
|
5
|
+
origin: new RegExp('^(https?://[^/]+)(?:/.*)?$', 'i')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// btoa
|
|
9
|
+
const base64_encode = function(str) {
|
|
10
|
+
return Buffer.from(str, 'binary').toString('base64')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// atob
|
|
14
|
+
const base64_decode = function(str) {
|
|
15
|
+
return Buffer.from(str, 'base64').toString('binary')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const parse_req_url = function(params, req) {
|
|
19
|
+
const {is_secure, host} = params
|
|
20
|
+
|
|
21
|
+
const result = {redirected_base_url: '', url_type: '', url: '', referer_url: ''}
|
|
22
|
+
|
|
23
|
+
const matches = regexs.req_url.exec(req.url)
|
|
24
|
+
|
|
25
|
+
if (matches) {
|
|
26
|
+
result.redirected_base_url = `${ is_secure ? 'https' : 'http' }://${host || req.headers.host}${matches[1] || ''}`
|
|
27
|
+
|
|
28
|
+
if (matches[3])
|
|
29
|
+
result.url_type = matches[3].toLowerCase().trim()
|
|
30
|
+
|
|
31
|
+
let url, url_lc, index
|
|
32
|
+
|
|
33
|
+
url = base64_decode( matches[2] ).trim()
|
|
34
|
+
url_lc = url.toLowerCase()
|
|
35
|
+
index = url_lc.indexOf('http')
|
|
36
|
+
|
|
37
|
+
if (index === 0) {
|
|
38
|
+
index = url_lc.indexOf('|http')
|
|
39
|
+
|
|
40
|
+
if (index > 0) {
|
|
41
|
+
result.referer_url = url.substring(index + 1, url.length)
|
|
42
|
+
|
|
43
|
+
url = url.substring(0, index).trim()
|
|
44
|
+
}
|
|
45
|
+
result.url = url
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const get_content_type = function(url_type) {
|
|
53
|
+
let content_type
|
|
54
|
+
switch(url_type) {
|
|
55
|
+
case 'm3u8':
|
|
56
|
+
content_type = 'application/x-mpegurl'
|
|
57
|
+
break
|
|
58
|
+
case 'ts':
|
|
59
|
+
content_type = 'video/MP2T'
|
|
60
|
+
break
|
|
61
|
+
case 'json':
|
|
62
|
+
content_type = 'application/json'
|
|
63
|
+
break
|
|
64
|
+
case 'key':
|
|
65
|
+
case 'other':
|
|
66
|
+
default:
|
|
67
|
+
content_type = 'application/octet-stream'
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
return content_type
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const add_CORS_headers = function(res) {
|
|
74
|
+
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
75
|
+
res.setHeader('Access-Control-Allow-Methods', '*')
|
|
76
|
+
res.setHeader('Access-Control-Allow-Headers', '*')
|
|
77
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true')
|
|
78
|
+
res.setHeader('Access-Control-Max-Age', '86400')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const debug = function() {
|
|
82
|
+
const args = [...arguments]
|
|
83
|
+
const params = args.shift()
|
|
84
|
+
const verbosity = args.shift()
|
|
85
|
+
const append_LF = true
|
|
86
|
+
|
|
87
|
+
const {debug_level} = params
|
|
88
|
+
|
|
89
|
+
if (append_LF) args.push("\n")
|
|
90
|
+
|
|
91
|
+
if (debug_level >= verbosity) {
|
|
92
|
+
console.log.apply(console.log, args)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const get_request_options = function(params, url, is_m3u8, referer_url) {
|
|
97
|
+
const {req_headers, req_options, hooks} = params
|
|
98
|
+
|
|
99
|
+
const additional_req_options = (hooks && (hooks instanceof Object) && hooks.add_request_options && (typeof hooks.add_request_options === 'function'))
|
|
100
|
+
? hooks.add_request_options(url, is_m3u8)
|
|
101
|
+
: null
|
|
102
|
+
|
|
103
|
+
const additional_req_headers = (hooks && (hooks instanceof Object) && hooks.add_request_headers && (typeof hooks.add_request_headers === 'function'))
|
|
104
|
+
? hooks.add_request_headers(url, is_m3u8)
|
|
105
|
+
: null
|
|
106
|
+
|
|
107
|
+
if (!req_options && !additional_req_options && !req_headers && !additional_req_headers && !referer_url) return url
|
|
108
|
+
|
|
109
|
+
const request_options = Object.assign(
|
|
110
|
+
{},
|
|
111
|
+
parse_url(url),
|
|
112
|
+
(req_options || {}),
|
|
113
|
+
(additional_req_options || {})
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
request_options.headers = Object.assign(
|
|
117
|
+
{},
|
|
118
|
+
(( req_options && req_options.headers) ? req_options.headers : {}),
|
|
119
|
+
((additional_req_options && additional_req_options.headers) ? additional_req_options.headers : {}),
|
|
120
|
+
(req_headers || {}),
|
|
121
|
+
(additional_req_headers || {}),
|
|
122
|
+
(referer_url ? {"referer": referer_url, "origin": referer_url.replace(regexs.origin, '$1')} : {})
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return request_options
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const should_prefetch_url = function(params, url, url_type) {
|
|
129
|
+
const {hooks, cache_segments} = params
|
|
130
|
+
|
|
131
|
+
let do_prefetch = !!url && !!cache_segments
|
|
132
|
+
|
|
133
|
+
if (do_prefetch) {
|
|
134
|
+
do_prefetch = (url_type === 'ts') || (url_type === 'key')
|
|
135
|
+
|
|
136
|
+
if (hooks && (hooks instanceof Object) && hooks.prefetch && (typeof hooks.prefetch === 'function')) {
|
|
137
|
+
const override_prefetch = hooks.prefetch(url, url_type)
|
|
138
|
+
|
|
139
|
+
if ((typeof override_prefetch === 'boolean') && (override_prefetch !== do_prefetch)) {
|
|
140
|
+
debug(params, 3, 'prefetch override:', (override_prefetch ? 'allow' : 'deny'), url)
|
|
141
|
+
do_prefetch = override_prefetch
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return do_prefetch
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
base64_encode,
|
|
150
|
+
base64_decode,
|
|
151
|
+
parse_req_url,
|
|
152
|
+
get_content_type,
|
|
153
|
+
add_CORS_headers,
|
|
154
|
+
debug,
|
|
155
|
+
get_request_options,
|
|
156
|
+
should_prefetch_url
|
|
157
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warren-bank/hls-proxy",
|
|
3
3
|
"description": "Node.js server to proxy HLS video streams",
|
|
4
|
-
"version": "3.1
|
|
4
|
+
"version": "3.2.1",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "node hls-proxy/bin/hlsd.js",
|
|
7
7
|
"sudo": "sudo node hls-proxy/bin/hlsd.js"
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@warren-bank/node-process-argv": "^1.2.1",
|
|
14
14
|
"@warren-bank/node-request": "^2.0.10",
|
|
15
|
-
"@warren-bank/url": "^3.1.0"
|
|
15
|
+
"@warren-bank/url": "^3.1.0",
|
|
16
|
+
"tough-cookie": "^3.0.1"
|
|
16
17
|
},
|
|
17
18
|
"license": "GPL-2.0",
|
|
18
19
|
"author": {
|