@warren-bank/hls-proxy 3.2.3 → 3.2.5

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 CHANGED
@@ -600,7 +600,8 @@ curl --silent --insecure "$URL"
600
600
  * internal `proxy` module exports an Object containing event listeners to process requests that can be either:
601
601
  - added to an instance of [`http.Server`](https://nodejs.org/api/http.html#class-httpserver)
602
602
  - added to an [`Express.js`](https://github.com/expressjs/express) application as middleware to handle a custom route
603
- * important limitation: since `/` is a valid character in a base64 encoded URL, the path for a custom route needs to end with a character that is not allowed in base64 encoding (ex: `'/proxy_/*'`)
603
+ * important requirement: the path for a custom route needs to include exactly one unnamed [parameter](https://expressjs.com/en/guide/routing.html#route-parameters) that matches the base64 encoded URL and (optionally) a file extension (ex: `'/proxy/*'`)
604
+ * the use of nested routers is supported
604
605
  - system requirements:
605
606
  * Node.js v16.0.0 and higher
606
607
  - required features: [`Proxy` constructor](https://node.green/#ES2015-built-ins-Proxy-constructor-requires-new), [`Proxy` 'apply' handler](https://node.green/#ES2015-built-ins-Proxy--apply--handler), [`Reflect.apply`](https://node.green/#ES2015-built-ins-Reflect-Reflect-apply), [`RegExp` 'd' flag](https://node.green/#ES2022-features-RegExp-Match-Indices---hasIndices-----d--flag-)
@@ -0,0 +1,32 @@
1
+ const get_full_req_url = function(req) {
2
+ return req.originalUrl || req.url
3
+ }
4
+
5
+ const has_req_param = function(req, key) {
6
+ return (req.params && (typeof req.params === 'object') && req.params[key])
7
+ }
8
+
9
+ const get_proxy_req_url = function(req) {
10
+ const key = "0"
11
+ return has_req_param(req, key)
12
+ ? `/${req.params[key]}`
13
+ : req.url
14
+ }
15
+
16
+ const get_base_req_url = function(req) {
17
+ let base_url = ''
18
+ const key = "0"
19
+
20
+ if (req.path && has_req_param(req, key)) {
21
+ base_url = req.baseUrl || ''
22
+ base_url += req.path.substring(0, (req.path.length - req.params[key].length - 1))
23
+ }
24
+
25
+ return base_url
26
+ }
27
+
28
+ module.exports = {
29
+ get_full_req_url,
30
+ get_proxy_req_url,
31
+ get_base_req_url
32
+ }
@@ -16,9 +16,6 @@ const url_location_landmarks = {
16
16
  '#EXT-X-RENDITION-REPORT:',
17
17
  '#EXT-X-DATERANGE:',
18
18
  '#EXT-X-CONTENT-STEERING:'
19
- ],
20
- next_line: [
21
- '#EXT-X-STREAM-INF:'
22
19
  ]
23
20
  },
24
21
  ts: {
@@ -26,26 +23,20 @@ const url_location_landmarks = {
26
23
  '#EXT-X-MAP:',
27
24
  '#EXT-X-PART:',
28
25
  '#EXT-X-PRELOAD-HINT:'
29
- ],
30
- next_line: [
31
- '#EXTINF:'
32
26
  ]
33
27
  },
34
28
  json: {
35
29
  same_line: [
36
30
  '#EXT-X-SESSION-DATA:'
37
- ],
38
- next_line: []
31
+ ]
39
32
  },
40
33
  key: {
41
34
  same_line: [
42
35
  '#EXT-X-KEY:'
43
- ],
44
- next_line: []
36
+ ]
45
37
  },
46
38
  other: {
47
- same_line: [],
48
- next_line: []
39
+ same_line: []
49
40
  }
50
41
  }
51
42
 
@@ -148,63 +139,82 @@ const parse_manifest = function(m3u8_content, m3u8_url, referer_url, hooks, cach
148
139
  const extract_embedded_urls = function(m3u8_lines, m3u8_url, referer_url, meta_data) {
149
140
  const embedded_urls = []
150
141
 
151
- let m3u8_line, has_next_m3u8_line, next_m3u8_line, matches, matching_landmark, matching_url
142
+ // one of: ['master','media']
143
+ // determined by the detection of either: ['#EXT-X-STREAM-INF','#EXTINF'], respectively
144
+ // until the type is determined, URI lines are ignored (as per the HLS spec)
145
+ let manifest_type = null
146
+
147
+ let m3u8_line, matches, matching_landmark, matching_url
152
148
 
153
149
  for (let i=0; i < m3u8_lines.length; i++) {
154
- m3u8_line = m3u8_lines[i]
155
- has_next_m3u8_line = ((i+1) < m3u8_lines.length)
156
-
157
- matches = regexs.m3u8_line_landmark.exec(m3u8_line)
158
- if (!matches) continue
159
- matching_landmark = matches[1]
160
-
161
- matches = regexs.m3u8_line_url.exec(m3u8_line)
162
- matching_url = matches
163
- ? matches[1]
164
- : null
165
-
166
- if (meta_data !== null)
167
- extract_meta_data(meta_data, m3u8_line, matching_landmark)
168
-
169
- for (let url_type in url_location_landmarks) {
170
- if (matching_url && (url_location_landmarks[url_type]['same_line'].indexOf(matching_landmark) >= 0)) {
171
- embedded_urls.push({
172
- line_index: i,
173
- url_indices: matches.indices[1],
174
- url_type: url_type,
175
- original_match_url: matching_url,
176
- resolved_match_url: (new URL(matching_url, m3u8_url)).href,
177
- redirected_url: null,
178
- referer_url: referer_url,
179
- encoded_url: null
180
- })
181
- break
150
+ m3u8_line = m3u8_lines[i]
151
+
152
+ if (is_m3u8_line_a_blank(m3u8_line) || is_m3u8_line_a_comment(m3u8_line))
153
+ continue
154
+
155
+ if (is_m3u8_line_a_tag(m3u8_line)) {
156
+ matches = regexs.m3u8_line_landmark.exec(m3u8_line)
157
+ if (!matches) continue
158
+ matching_landmark = matches[1]
159
+
160
+ if (manifest_type === null) {
161
+ if (matching_landmark === '#EXT-X-STREAM-INF:')
162
+ manifest_type = 'master'
163
+ else if (matching_landmark === '#EXTINF:')
164
+ manifest_type = 'media'
182
165
  }
183
- if (has_next_m3u8_line && (url_location_landmarks[url_type]['next_line'].indexOf(matching_landmark) >= 0)) {
184
- next_m3u8_line = m3u8_lines[i+1].trim()
185
166
 
186
- if (next_m3u8_line && (next_m3u8_line[0] !== '#')) {
187
- i++
167
+ if (meta_data !== null)
168
+ extract_meta_data(meta_data, m3u8_line, matching_landmark)
188
169
 
170
+ matches = regexs.m3u8_line_url.exec(m3u8_line)
171
+ if (!matches) continue
172
+ matching_url = matches[1]
173
+
174
+ for (let url_type in url_location_landmarks) {
175
+ if (url_location_landmarks[url_type]['same_line'].indexOf(matching_landmark) >= 0) {
189
176
  embedded_urls.push({
190
177
  line_index: i,
191
- url_indices: null,
178
+ url_indices: matches.indices[1],
192
179
  url_type: url_type,
193
- original_match_url: next_m3u8_line,
194
- resolved_match_url: (new URL(next_m3u8_line, m3u8_url)).href,
180
+ original_match_url: matching_url,
181
+ resolved_match_url: (new URL(matching_url, m3u8_url)).href,
195
182
  redirected_url: null,
196
183
  referer_url: referer_url,
197
184
  encoded_url: null
198
185
  })
186
+ break
199
187
  }
200
- break
201
188
  }
202
189
  }
190
+ else {
191
+ // line is a URI
192
+ if (manifest_type === null) continue
193
+
194
+ const url_type = (manifest_type === 'master') ? 'm3u8' : 'ts'
195
+
196
+ embedded_urls.push({
197
+ line_index: i,
198
+ url_indices: null,
199
+ url_type: url_type,
200
+ original_match_url: m3u8_line,
201
+ resolved_match_url: (new URL(m3u8_line, m3u8_url)).href,
202
+ redirected_url: null,
203
+ referer_url: referer_url,
204
+ encoded_url: null
205
+ })
206
+ }
203
207
  }
204
208
 
205
209
  return embedded_urls
206
210
  }
207
211
 
212
+ const is_m3u8_line_a_blank = (line) => (!line || !line.trim())
213
+
214
+ const is_m3u8_line_a_comment = (line) => ((line.indexOf('#') === 0) && !is_m3u8_line_a_tag(line))
215
+
216
+ const is_m3u8_line_a_tag = (line) => (line.indexOf('#EXT') === 0)
217
+
208
218
  const extract_meta_data = function(meta_data, m3u8_line, matching_landmark) {
209
219
  for (let meta_data_key in meta_data_location_landmarks) {
210
220
  if (meta_data_location_landmarks[meta_data_key]['same_line'].indexOf(matching_landmark) >= 0) {
@@ -1,4 +1,5 @@
1
1
  const parse_url = require('@warren-bank/url').parse
2
+ const expressjs = require('./expressjs_utils')
2
3
 
3
4
  const regexs = {
4
5
  req_url: new RegExp('^(.*?)/([a-zA-Z0-9\\+/=%]+)(?:[\\._]([^/\\?#]*))?(?:[\\?#].*)?$'),
@@ -20,10 +21,10 @@ const parse_req_url = function(params, req) {
20
21
 
21
22
  const result = {redirected_base_url: '', url_type: '', url: '', referer_url: ''}
22
23
 
23
- const matches = regexs.req_url.exec(req.url)
24
+ const matches = regexs.req_url.exec( expressjs.get_proxy_req_url(req) )
24
25
 
25
26
  if (matches) {
26
- result.redirected_base_url = `${ is_secure ? 'https' : 'http' }://${host || req.headers.host}${matches[1] || ''}`
27
+ result.redirected_base_url = `${ is_secure ? 'https' : 'http' }://${host || req.headers.host}${expressjs.get_base_req_url(req) || matches[1] || ''}`
27
28
 
28
29
  if (matches[3])
29
30
  result.url_type = matches[3].toLowerCase().trim()
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.2.3",
4
+ "version": "3.2.5",
5
5
  "scripts": {
6
6
  "start": "node hls-proxy/bin/hlsd.js",
7
7
  "sudo": "sudo node hls-proxy/bin/hlsd.js"