dasha 4.0.0-alpha.2 → 4.0.0-alpha.4

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/dist/dasha.cjs CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  //#region rolldown:runtime
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
@@ -22,33 +21,44 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
21
  }) : target, mod));
23
22
 
24
23
  //#endregion
25
- const node_fs = __toESM(require("node:fs"));
26
- const node_fs_promises = __toESM(require("node:fs/promises"));
27
- const node_url = __toESM(require("node:url"));
28
- const node_path = __toESM(require("node:path"));
29
- const temporal_polyfill = __toESM(require("temporal-polyfill"));
30
- const __xmldom_xmldom = __toESM(require("@xmldom/xmldom"));
31
- const node_crypto = __toESM(require("node:crypto"));
24
+ let node_crypto = require("node:crypto");
25
+ node_crypto = __toESM(node_crypto);
26
+ let node_fs = require("node:fs");
27
+ let barsic = require("barsic");
28
+ let node_fs_promises = require("node:fs/promises");
29
+ let node_url = require("node:url");
30
+ let node_path = require("node:path");
31
+ node_path = __toESM(node_path);
32
+ let temporal_polyfill = require("temporal-polyfill");
33
+ let __xmldom_xmldom = require("@xmldom/xmldom");
32
34
 
33
- //#region lib/shared/media-type.ts
34
- const MEDIA_TYPES = {
35
- VIDEO: "video",
36
- AUDIO: "audio",
37
- SUBTITLES: "subtitle",
38
- CLOSED_CAPTIONS: "closed-captions"
35
+ //#region lib/shared/encrypt-method.ts
36
+ const ENCRYPT_METHODS = {
37
+ NONE: "none",
38
+ AES_128: "aes-128",
39
+ AES_128_ECB: "aes-128-ecb",
40
+ SAMPLE_AES: "sample-aes",
41
+ SAMPLE_AES_CTR: "sample-aes-ctr",
42
+ CENC: "cenc",
43
+ CHACHA20: "chacha20",
44
+ UNKNOWN: "unknown"
39
45
  };
40
46
 
41
47
  //#endregion
42
- //#region lib/shared/encrypt-method.ts
43
- const ENCRYPT_METHODS = {
44
- NONE: 0,
45
- AES_128: 1,
46
- AES_128_ECB: 2,
47
- SAMPLE_AES: 3,
48
- SAMPLE_AES_CTR: 4,
49
- CENC: 5,
50
- CHACHA20: 6,
51
- UNKNOWN: 7
48
+ //#region lib/shared/encrypt-info.ts
49
+ var EncryptInfo = class {
50
+ method = ENCRYPT_METHODS.NONE;
51
+ key;
52
+ iv;
53
+ drm;
54
+ constructor(method) {
55
+ this.method = this.parseMethod(method);
56
+ this.drm = {};
57
+ }
58
+ parseMethod(method) {
59
+ if (method) return ENCRYPT_METHODS[method.replace("-", "_")];
60
+ else return ENCRYPT_METHODS.UNKNOWN;
61
+ }
52
62
  };
53
63
 
54
64
  //#endregion
@@ -60,6 +70,132 @@ const EXTRACTOR_TYPES = {
60
70
  MSS: "MSS"
61
71
  };
62
72
 
73
+ //#endregion
74
+ //#region lib/shared/media-part.ts
75
+ var MediaPart = class {
76
+ mediaSegments = [];
77
+ constructor(segments) {
78
+ this.mediaSegments = segments || [];
79
+ }
80
+ };
81
+
82
+ //#endregion
83
+ //#region lib/shared/media-segment.ts
84
+ var MediaSegment = class MediaSegment {
85
+ index = NaN;
86
+ duration = NaN;
87
+ title;
88
+ dateTime;
89
+ startRange;
90
+ get stopRange() {
91
+ return this.startRange !== void 0 && this.expectLength !== void 0 ? this.startRange + this.expectLength - 1 : void 0;
92
+ }
93
+ expectLength;
94
+ encryptInfo = new EncryptInfo();
95
+ get isEncrypted() {
96
+ return this.encryptInfo.method !== ENCRYPT_METHODS.NONE;
97
+ }
98
+ url = "";
99
+ nameFromVar;
100
+ equals(segment) {
101
+ if (segment instanceof MediaSegment) return this.index == segment.index && Math.abs(this.duration - segment.duration) < .001 && this.title == segment.title && this.startRange == segment.startRange && this.stopRange == segment.stopRange && this.expectLength == segment.expectLength && this.url == segment.url;
102
+ else return false;
103
+ }
104
+ getHashCode() {
105
+ const payload = [
106
+ this.index,
107
+ this.duration,
108
+ this.title,
109
+ this.startRange,
110
+ this.stopRange,
111
+ this.expectLength,
112
+ this.url
113
+ ].join("-");
114
+ return node_crypto.default.createHash("md5").update(payload).digest("hex");
115
+ }
116
+ };
117
+
118
+ //#endregion
119
+ //#region lib/shared/media-type.ts
120
+ const MEDIA_TYPES = {
121
+ VIDEO: "video",
122
+ AUDIO: "audio",
123
+ SUBTITLES: "subtitle",
124
+ CLOSED_CAPTIONS: "closed-captions"
125
+ };
126
+
127
+ //#endregion
128
+ //#region lib/shared/stream-spec.ts
129
+ var StreamSpec = class {
130
+ mediaType;
131
+ groupId = null;
132
+ language;
133
+ name;
134
+ default;
135
+ skippedDuration;
136
+ bandwidth;
137
+ codecs = null;
138
+ resolution;
139
+ frameRate;
140
+ channels = null;
141
+ extension = null;
142
+ role;
143
+ videoRange;
144
+ characteristics;
145
+ publishTime;
146
+ audioId;
147
+ videoId;
148
+ subtitleId;
149
+ periodId = null;
150
+ url = "";
151
+ originalUrl = "";
152
+ playlist;
153
+ get segmentsCount() {
154
+ return this.playlist?.mediaParts.reduce((sum, part) => sum + part.mediaSegments.length, 0) ?? 0;
155
+ }
156
+ toShortString() {
157
+ let prefixStr = "";
158
+ let returnStr = "";
159
+ const encStr = "";
160
+ const bandwidth = this.bandwidth ? `${this.bandwidth / 1e3} Kbps` : "";
161
+ const channels = this.channels ? `${this.channels}CH` : "";
162
+ if (this.mediaType === MEDIA_TYPES.AUDIO) {
163
+ prefixStr = `Aud ${encStr}`;
164
+ returnStr = [
165
+ this.groupId,
166
+ bandwidth,
167
+ this.name,
168
+ this.codecs,
169
+ this.language,
170
+ channels,
171
+ this.role
172
+ ].filter(Boolean).join(" | ");
173
+ } else if (this.mediaType === MEDIA_TYPES.SUBTITLES) {
174
+ prefixStr = `Sub ${encStr}`;
175
+ returnStr = [
176
+ this.groupId,
177
+ this.language,
178
+ this.name,
179
+ this.codecs,
180
+ this.role
181
+ ].filter(Boolean).join(" | ");
182
+ } else {
183
+ prefixStr = `Vid ${encStr}`;
184
+ returnStr = [
185
+ this.resolution,
186
+ bandwidth,
187
+ this.groupId,
188
+ this.frameRate,
189
+ this.codecs,
190
+ this.videoRange,
191
+ this.role
192
+ ].filter(Boolean).join(" | ");
193
+ }
194
+ returnStr = `${prefixStr} | ${returnStr}`;
195
+ return returnStr.trim();
196
+ }
197
+ };
198
+
63
199
  //#endregion
64
200
  //#region lib/shared/role-type.ts
65
201
  const ROLE_TYPE = {
@@ -77,17 +213,13 @@ const ROLE_TYPE = {
77
213
 
78
214
  //#endregion
79
215
  //#region lib/processor.ts
80
- var ContentProcessor = class {};
81
- var KeyProcessor = class {};
82
- var UrlProcessor = class {};
83
- var DefaultUrlProcessor = class extends UrlProcessor {
216
+ var DefaultUrlProcessor = class {
84
217
  canProcess(_extractorType, _originalUrl, parserConfig) {
85
218
  return parserConfig.appendUrlParams;
86
219
  }
87
220
  process(url, parserConfig) {
88
221
  if (!url.startsWith("http")) return url;
89
- const urlFromConfig = new URL(parserConfig.url);
90
- const urlFromConfigQuery = urlFromConfig.searchParams;
222
+ const urlFromConfigQuery = new URL(parserConfig.url).searchParams;
91
223
  const oldUrl = new URL(url);
92
224
  const newQuery = oldUrl.searchParams;
93
225
  for (const [key, value] of urlFromConfigQuery) if (newQuery.has(key)) newQuery.set(key, value);
@@ -102,7 +234,7 @@ var DefaultUrlProcessor = class extends UrlProcessor {
102
234
 
103
235
  //#endregion
104
236
  //#region lib/dash/dash-content-processor.ts
105
- var DefaultDashContentProcessor = class extends ContentProcessor {
237
+ var DefaultDashContentProcessor = class {
106
238
  canProcess(extractorType, mpdContent) {
107
239
  if (extractorType !== EXTRACTOR_TYPES.MPEG_DASH) return false;
108
240
  return mpdContent.includes("<mas:") && !mpdContent.includes("xmlns:mas");
@@ -147,7 +279,7 @@ const HLS_TAGS = {
147
279
 
148
280
  //#endregion
149
281
  //#region lib/hls/hls-content-processor.ts
150
- var DefaultHlsContentProcessor = class DefaultHlsContentProcessor extends ContentProcessor {
282
+ var DefaultHlsContentProcessor = class DefaultHlsContentProcessor {
151
283
  static YkDVRegex = /#EXT-X-DISCONTINUITY\s+#EXT-X-MAP:URI="(.*?)",BYTERANGE="(.*?)"/g;
152
284
  static DNSPRegex = /#EXT-X-MAP:URI=".*?BUMPER\/[\s\S]+?#EXT-X-DISCONTINUITY/;
153
285
  static DNSPSubRegex = /#EXTINF:.*?,\s+.*BUMPER.*\s+#EXT-X-DISCONTINUITY/;
@@ -180,24 +312,9 @@ var DefaultHlsContentProcessor = class DefaultHlsContentProcessor extends Conten
180
312
  }
181
313
  };
182
314
 
183
- //#endregion
184
- //#region lib/shared/encrypt-info.ts
185
- var EncryptInfo = class {
186
- method = ENCRYPT_METHODS.NONE;
187
- key;
188
- iv;
189
- constructor(method) {
190
- this.method = this.parseMethod(method);
191
- }
192
- parseMethod(method) {
193
- if (method) return ENCRYPT_METHODS[method.replace("-", "_")];
194
- else return ENCRYPT_METHODS.UNKNOWN;
195
- }
196
- };
197
-
198
315
  //#endregion
199
316
  //#region lib/hls/hls-key-processor.ts
200
- var DefaultHlsKeyProcessor = class extends KeyProcessor {
317
+ var DefaultHlsKeyProcessor = class {
201
318
  canProcess(extractorType) {
202
319
  return extractorType === EXTRACTOR_TYPES.HLS;
203
320
  }
@@ -205,17 +322,16 @@ var DefaultHlsKeyProcessor = class extends KeyProcessor {
205
322
  const iv = this.getAttribute(keyLine, "IV");
206
323
  const method = this.getAttribute(keyLine, "METHOD");
207
324
  const uri = this.getAttribute(keyLine, "URI");
208
- console.debug(`METHOD:${method}, URI:${uri}, IV:${iv}`);
209
325
  const encryptInfo = new EncryptInfo(method);
210
- if (iv) encryptInfo.iv = Buffer.from(iv, "hex");
326
+ if (iv) encryptInfo.iv = barsic.b.hex().encode(iv);
211
327
  if (parserConfig.customIv && parserConfig.customIv.length > 0) encryptInfo.iv = parserConfig.customIv;
212
328
  try {
213
329
  if (parserConfig.customKey && parserConfig.customKey.length > 0) encryptInfo.key = parserConfig.customKey;
214
330
  else if (uri) {
215
331
  const lowerUri = uri.toLowerCase();
216
- if (lowerUri.startsWith("base64:")) encryptInfo.key = Buffer.from(uri.slice(7), "base64");
217
- else if (lowerUri.startsWith("data:;base64,")) encryptInfo.key = Buffer.from(uri.slice(13), "base64");
218
- else if (lowerUri.startsWith("data:text/plain;base64,")) encryptInfo.key = Buffer.from(uri.slice(23), "base64");
332
+ if (lowerUri.startsWith("base64:")) encryptInfo.key = barsic.b.base64().encode(uri.slice(7));
333
+ else if (lowerUri.startsWith("data:;base64,")) encryptInfo.key = barsic.b.base64().encode(uri.slice(13));
334
+ else if (lowerUri.startsWith("data:text/plain;base64,")) encryptInfo.key = barsic.b.base64().encode(uri.slice(23));
219
335
  else if ((0, node_fs.existsSync)(uri)) encryptInfo.key = (0, node_fs.readFileSync)(uri);
220
336
  else {
221
337
  const processedUrl = this.preProcessUrl(new URL(uri, m3u8Url).toString(), parserConfig);
@@ -233,15 +349,15 @@ var DefaultHlsKeyProcessor = class extends KeyProcessor {
233
349
  return encryptInfo;
234
350
  }
235
351
  getAttribute(line, attrName) {
236
- const regex = new RegExp(`${attrName}="([^"]+)"`, "i");
352
+ const regex = new RegExp(`${attrName}=(?:"([^"]+)"|([^,]+))`, "i");
237
353
  const match = line.match(regex);
238
- return match?.[1] ?? null;
354
+ return match?.[1] ?? match?.[2] ?? null;
239
355
  }
240
356
  async fetchKeyWithRetry(url, parserConfig) {
241
357
  let retryCount = parserConfig.keyRetryCount ?? 3;
242
358
  while (retryCount >= 0) try {
243
359
  const response = await fetch(url, { headers: parserConfig.headers });
244
- return Buffer.from(await response.arrayBuffer());
360
+ return new Uint8Array(await response.arrayBuffer());
245
361
  } catch (error) {
246
362
  if (error.message.includes("scheme is not supported")) throw error;
247
363
  console.warn(`Error fetching key: ${error.message}. Retries left: ${retryCount}`);
@@ -276,78 +392,6 @@ var ParserConfig = class {
276
392
  keyRetryCount = 3;
277
393
  };
278
394
 
279
- //#endregion
280
- //#region lib/shared/stream-spec.ts
281
- var StreamSpec = class {
282
- mediaType;
283
- groupId = null;
284
- language;
285
- name;
286
- default;
287
- skippedDuration;
288
- bandwidth;
289
- codecs = null;
290
- resolution;
291
- frameRate;
292
- channels = null;
293
- extension = null;
294
- role;
295
- videoRange;
296
- characteristics;
297
- publishTime;
298
- audioId;
299
- videoId;
300
- subtitleId;
301
- periodId = null;
302
- url = "";
303
- originalUrl = "";
304
- playlist;
305
- get segmentsCount() {
306
- return this.playlist?.mediaParts.reduce((sum, part) => sum + part.mediaSegments.length, 0) ?? 0;
307
- }
308
- toShortString() {
309
- let prefixStr = "";
310
- let returnStr = "";
311
- const encStr = "";
312
- const bandwidth = this.bandwidth ? `${this.bandwidth / 1e3} Kbps` : "";
313
- const channels = this.channels ? `${this.channels}CH` : "";
314
- if (this.mediaType === MEDIA_TYPES.AUDIO) {
315
- prefixStr = `Aud ${encStr}`;
316
- returnStr = [
317
- this.groupId,
318
- bandwidth,
319
- this.name,
320
- this.codecs,
321
- this.language,
322
- channels,
323
- this.role
324
- ].filter(Boolean).join(" | ");
325
- } else if (this.mediaType === MEDIA_TYPES.SUBTITLES) {
326
- prefixStr = `Sub ${encStr}`;
327
- returnStr = [
328
- this.groupId,
329
- this.language,
330
- this.name,
331
- this.codecs,
332
- this.role
333
- ].filter(Boolean).join(" | ");
334
- } else {
335
- prefixStr = `Vid ${encStr}`;
336
- returnStr = [
337
- this.resolution,
338
- bandwidth,
339
- this.groupId,
340
- this.frameRate,
341
- this.codecs,
342
- this.videoRange,
343
- this.role
344
- ].filter(Boolean).join(" | ");
345
- }
346
- returnStr = `${prefixStr} | ${returnStr}`;
347
- return returnStr.trim();
348
- }
349
- };
350
-
351
395
  //#endregion
352
396
  //#region lib/dash/dash-tags.ts
353
397
  const DASH_TAGS = {
@@ -362,8 +406,7 @@ const DASH_TAGS = {
362
406
  const combineUrl = (baseUrl, relativeUrl) => {
363
407
  if (!baseUrl.trim()) return relativeUrl;
364
408
  const url1 = new URL(baseUrl);
365
- const url2 = new URL(relativeUrl, url1);
366
- return url2.toString();
409
+ return new URL(relativeUrl, url1).toString();
367
410
  };
368
411
  const replaceVars = (text, dict) => {
369
412
  let result = text;
@@ -402,7 +445,7 @@ const getAttribute = (line, key = "") => {
402
445
  return result;
403
446
  };
404
447
  const distinctBy = (array, callbackfn) => {
405
- const seen = new Set();
448
+ const seen = /* @__PURE__ */ new Set();
406
449
  return array.filter((item) => {
407
450
  const value = callbackfn(item);
408
451
  if (seen.has(value)) return false;
@@ -427,51 +470,6 @@ var Playlist = class {
427
470
  mediaParts = [];
428
471
  };
429
472
 
430
- //#endregion
431
- //#region lib/shared/media-part.ts
432
- var MediaPart = class {
433
- mediaSegments = [];
434
- constructor(segments) {
435
- this.mediaSegments = segments || [];
436
- }
437
- };
438
-
439
- //#endregion
440
- //#region lib/shared/media-segment.ts
441
- var MediaSegment = class MediaSegment {
442
- index = NaN;
443
- duration = NaN;
444
- title;
445
- dateTime;
446
- startRange;
447
- get stopRange() {
448
- return this.startRange !== void 0 && this.expectLength !== void 0 ? this.startRange + this.expectLength - 1 : void 0;
449
- }
450
- expectLength;
451
- encryptInfo = new EncryptInfo();
452
- get isEncrypted() {
453
- return this.encryptInfo.method !== ENCRYPT_METHODS.NONE;
454
- }
455
- url = "";
456
- nameFromVar;
457
- equals(segment) {
458
- if (segment instanceof MediaSegment) return this.index == segment.index && Math.abs(this.duration - segment.duration) < .001 && this.title == segment.title && this.startRange == segment.startRange && this.stopRange == segment.stopRange && this.expectLength == segment.expectLength && this.url == segment.url;
459
- else return false;
460
- }
461
- getHashCode() {
462
- const payload = [
463
- this.index,
464
- this.duration,
465
- this.title,
466
- this.startRange,
467
- this.stopRange,
468
- this.expectLength,
469
- this.url
470
- ].join("-");
471
- return node_crypto.default.createHash("md5").update(payload).digest("hex");
472
- }
473
- };
474
-
475
473
  //#endregion
476
474
  //#region lib/dash/dash-utils.ts
477
475
  /**
@@ -481,8 +479,7 @@ var MediaSegment = class MediaSegment {
481
479
  */
482
480
  const parseRange = (range) => {
483
481
  const [startRange, end] = range.split("-").map(Number);
484
- const expectLength = end - startRange + 1;
485
- return [startRange, expectLength];
482
+ return [startRange, end - startRange + 1];
486
483
  };
487
484
 
488
485
  //#endregion
@@ -518,11 +515,9 @@ var DashExtractor = class DashExtractor {
518
515
  async extractStreams(rawText) {
519
516
  const streamList = [];
520
517
  this.#mpdContent = rawText;
521
- const document = new __xmldom_xmldom.DOMParser().parseFromString(this.#mpdContent, "text/xml");
522
- const mpdElement = document.getElementsByTagName("MPD")[0];
523
- const type = mpdElement.getAttribute("type");
524
- const isLive = type === "dynamic";
525
- const maxSegmentDuration = mpdElement.getAttribute("maxSegmentDuration");
518
+ const mpdElement = new __xmldom_xmldom.DOMParser().parseFromString(this.#mpdContent, "text/xml").getElementsByTagName("MPD")[0];
519
+ const isLive = mpdElement.getAttribute("type") === "dynamic";
520
+ mpdElement.getAttribute("maxSegmentDuration");
526
521
  const availabilityStartTime = mpdElement.getAttribute("availabilityStartTime");
527
522
  const timeShiftBufferDepth = mpdElement.getAttribute("timeShiftBufferDepth") || "PT1M";
528
523
  const publishTime = mpdElement.getAttribute("publishTime");
@@ -581,8 +576,7 @@ var DashExtractor = class DashExtractor {
581
576
  if (role) {
582
577
  const roleValue = role.getAttribute("value");
583
578
  const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
584
- const roleTypeKey = roleValue.split("-").map(capitalize).join("");
585
- const roleType = ROLE_TYPE[roleTypeKey];
579
+ const roleType = ROLE_TYPE[roleValue.split("-").map(capitalize).join("")];
586
580
  streamSpec.role = roleType;
587
581
  if (roleType === ROLE_TYPE.Subtitle) {
588
582
  streamSpec.mediaType = MEDIA_TYPES.SUBTITLES;
@@ -713,8 +707,7 @@ var DashExtractor = class DashExtractor {
713
707
  varDic[DASH_TAGS.TemplateNumber] = segNumber++;
714
708
  const _hashTime = mediaTemplate?.includes(DASH_TAGS.TemplateTime);
715
709
  const _media = replaceVars(mediaTemplate, varDic);
716
- const _mediaUrl = combineUrl(segBaseUrl, _media);
717
- _mediaSegment.url = _mediaUrl;
710
+ _mediaSegment.url = combineUrl(segBaseUrl, _media);
718
711
  _mediaSegment.index = segIndex++;
719
712
  _mediaSegment.duration = _duration / timescale;
720
713
  if (_hashTime) _mediaSegment.nameFromVar = currentTime.toString();
@@ -758,27 +751,37 @@ var DashExtractor = class DashExtractor {
758
751
  mediaSegment.duration = periodDurationSeconds;
759
752
  streamSpec.playlist.mediaParts[0].mediaSegments.push(mediaSegment);
760
753
  }
761
- const contentProtection = adaptationSet.getElementsByTagName("ContentProtection")[0] || representation.getElementsByTagName("ContentProtection")[0];
762
- if (contentProtection) {
754
+ const adaptationSetProtections = adaptationSet.getElementsByTagName("ContentProtection");
755
+ const representationProtections = representation.getElementsByTagName("ContentProtection");
756
+ const contentProtections = representationProtections[0] ? representationProtections : adaptationSetProtections;
757
+ if (contentProtections.length) {
763
758
  const encryptInfo = new EncryptInfo();
764
759
  encryptInfo.method = DashExtractor.#DEFAULT_METHOD;
760
+ const widevineSystemId = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
761
+ const playreadySystemId = "9a04f079-9840-4286-ab92-e65be0885f95";
762
+ for (const contentProtection of contentProtections) {
763
+ const schemeIdUri = contentProtection.getAttribute("schemeIdUri");
764
+ const drmData = {
765
+ keyId: contentProtection.getAttribute("cenc:default_KID") || void 0,
766
+ pssh: contentProtection.getElementsByTagName("cenc:pssh")[0]?.textContent || void 0
767
+ };
768
+ if (schemeIdUri?.includes(widevineSystemId)) encryptInfo.drm.widevine = drmData;
769
+ else if (schemeIdUri?.includes(playreadySystemId)) encryptInfo.drm.playready = drmData;
770
+ else continue;
771
+ }
765
772
  if (streamSpec.playlist.mediaInit) streamSpec.playlist.mediaInit.encryptInfo = encryptInfo;
766
773
  const segments = streamSpec.playlist.mediaParts[0].mediaSegments;
767
774
  for (const segment of segments) if (!segment.encryptInfo) segment.encryptInfo = encryptInfo;
768
775
  }
769
776
  const _index = streamList.findIndex((item) => item.periodId !== streamSpec.periodId && item.groupId === streamSpec.groupId && item.resolution === streamSpec.resolution && item.mediaType === streamSpec.mediaType);
770
- if (_index > -1) if (isLive) {} else {
771
- const url1 = streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).url;
772
- const url2 = streamSpec.playlist.mediaParts[0].mediaSegments.at(-1)?.url;
773
- if (url1 !== url2) {
774
- const startIndex = streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).index + 1;
775
- const segments = streamSpec.playlist.mediaParts[0].mediaSegments;
776
- for (const segment of segments) segment.index += startIndex;
777
- const mediaPart = new MediaPart();
778
- mediaPart.mediaSegments = streamList[_index].playlist.mediaParts[0].mediaSegments;
779
- streamList[_index].playlist.mediaParts.push(mediaPart);
780
- } else streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).duration += streamSpec.playlist.mediaParts[0].mediaSegments.reduce((sum, segment) => sum + segment.duration, 0);
781
- }
777
+ if (_index > -1) if (isLive) {} else if (streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).url !== streamSpec.playlist.mediaParts[0].mediaSegments.at(-1)?.url) {
778
+ const startIndex = streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).index + 1;
779
+ const segments = streamSpec.playlist.mediaParts[0].mediaSegments;
780
+ for (const segment of segments) segment.index += startIndex;
781
+ const mediaPart = new MediaPart();
782
+ mediaPart.mediaSegments = streamList[_index].playlist.mediaParts[0].mediaSegments;
783
+ streamList[_index].playlist.mediaParts.push(mediaPart);
784
+ } else streamList[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).duration += streamSpec.playlist.mediaParts[0].mediaSegments.reduce((sum, segment) => sum + segment.duration, 0);
782
785
  else {
783
786
  if (streamSpec.mediaType === MEDIA_TYPES.SUBTITLES && streamSpec.extension === "mp4") streamSpec.extension = "m4s";
784
787
  if (streamSpec.mediaType !== MEDIA_TYPES.SUBTITLES && (streamSpec.extension == null || streamSpec.playlist.mediaParts.reduce((sum, part) => sum + part.mediaSegments.length, 0) > 1)) streamSpec.extension = "m4s";
@@ -793,8 +796,8 @@ var DashExtractor = class DashExtractor {
793
796
  const subtitleList = streamList.filter((stream) => stream.mediaType === MEDIA_TYPES.SUBTITLES);
794
797
  const videoList = streamList.filter((stream) => stream.mediaType === MEDIA_TYPES.VIDEO);
795
798
  for (const video of videoList) {
796
- const audioGroupId = audioList.toSorted((a, b) => (b.bandwidth || 0) - (a.bandwidth || 0)).at(0)?.groupId;
797
- const subtitleGroupId = subtitleList.toSorted((a, b) => (b.bandwidth || 0) - (a.bandwidth || 0)).at(0)?.groupId;
799
+ const audioGroupId = audioList.toSorted((a, b$1) => (b$1.bandwidth || 0) - (a.bandwidth || 0)).at(0)?.groupId;
800
+ const subtitleGroupId = subtitleList.toSorted((a, b$1) => (b$1.bandwidth || 0) - (a.bandwidth || 0)).at(0)?.groupId;
798
801
  if (audioGroupId) video.audioId = audioGroupId;
799
802
  if (subtitleGroupId) video.subtitleId = subtitleGroupId;
800
803
  }
@@ -804,17 +807,38 @@ var DashExtractor = class DashExtractor {
804
807
  if (!v) return;
805
808
  return v;
806
809
  }
807
- fetchPlayList(streamSpecs) {
808
- throw new Error("Method not implemented.");
810
+ async refreshPlayList(streamSpecs) {
811
+ if (!streamSpecs.length) return;
812
+ const response = await fetch(this.#parserConfig.url, this.#parserConfig.headers).catch(() => fetch(this.#parserConfig.originalUrl, this.#parserConfig.headers));
813
+ const rawText = await response.text();
814
+ const url = response.url;
815
+ this.#parserConfig.url = url;
816
+ this.#setInitUrl();
817
+ const newStreams = await this.extractStreams(rawText);
818
+ for (const streamSpec of streamSpecs) {
819
+ let results = newStreams.filter((n) => n.toShortString() === streamSpec.toShortString());
820
+ if (!results.length) results = newStreams.filter((n) => n.playlist?.mediaInit?.url === streamSpec.playlist?.mediaInit?.url);
821
+ if (results.length) streamSpec.playlist.mediaParts = results.at(0).playlist.mediaParts;
822
+ }
823
+ await this.#processUrl(streamSpecs);
809
824
  }
810
- refreshPlayList(streamSpecs) {
811
- throw new Error("Method not implemented.");
825
+ async #processUrl(streamSpecs) {
826
+ for (const spec of streamSpecs) {
827
+ const playlist = spec.playlist;
828
+ if (!playlist) continue;
829
+ if (playlist.mediaInit) playlist.mediaInit.url = this.preProcessUrl(playlist.mediaInit.url);
830
+ for (const part of playlist.mediaParts) for (const segment of part.mediaSegments) segment.url = this.preProcessUrl(segment.url);
831
+ }
832
+ }
833
+ async fetchPlayList(streamSpecs) {
834
+ this.#processUrl(streamSpecs);
812
835
  }
813
836
  preProcessUrl(url) {
814
- throw new Error("Method not implemented.");
837
+ for (const processor of this.#parserConfig.urlProcessors) if (processor.canProcess(this.extractorType, url, this.#parserConfig)) url = processor.process(url, this.#parserConfig);
838
+ return url;
815
839
  }
816
840
  preProcessContent() {
817
- throw new Error("Method not implemented.");
841
+ for (const processor of this.#parserConfig.contentProcessors) if (processor.canProcess(this.extractorType, this.#mpdContent, this.#parserConfig)) this.#mpdContent = processor.process(this.#mpdContent, this.#parserConfig);
818
842
  }
819
843
  };
820
844
 
@@ -893,8 +917,7 @@ var HlsExtractor = class {
893
917
  expectPlaylist = true;
894
918
  } else if (line.startsWith(HLS_TAGS.extXMedia)) {
895
919
  streamSpec = new StreamSpec();
896
- const type = getAttribute(line, "TYPE").replace("-", "_");
897
- const mediaType = MEDIA_TYPES[type];
920
+ const mediaType = MEDIA_TYPES[getAttribute(line, "TYPE").replace("-", "_")];
898
921
  if (mediaType) streamSpec.mediaType = mediaType;
899
922
  if (mediaType === MEDIA_TYPES.CLOSED_CAPTIONS) continue;
900
923
  let url = getAttribute(line, "URI");
@@ -945,8 +968,7 @@ var HlsExtractor = class {
945
968
  for (const line of lines) {
946
969
  if (!line.trim()) continue;
947
970
  if (line.startsWith(HLS_TAGS.extXByterange)) {
948
- const p = getAttribute(line);
949
- const [n, o] = getRange(p);
971
+ const [n, o] = getRange(getAttribute(line));
950
972
  segment.expectLength = n;
951
973
  segment.startRange = o || (segments.at(-1)?.startRange || 0) + (segments.at(-1)?.expectLength || 0);
952
974
  expectSegment = true;
@@ -968,9 +990,7 @@ var HlsExtractor = class {
968
990
  mediaParts.push(new MediaPart(segments));
969
991
  segments = [];
970
992
  } else if (line.startsWith(HLS_TAGS.extXKey)) {
971
- const uri = getAttribute(line, "URI");
972
- const uriLast = getAttribute(lastKeyLine, "URI");
973
- if (uri !== uriLast) {
993
+ if (getAttribute(line, "URI") !== getAttribute(lastKeyLine, "URI")) {
974
994
  const parsedInfo = await this.#parseKey(line);
975
995
  currentEncryptInfo.method = parsedInfo.method;
976
996
  currentEncryptInfo.key = parsedInfo.key;
@@ -998,8 +1018,7 @@ var HlsExtractor = class {
998
1018
  mediaSegment.index = -1;
999
1019
  playlist.mediaInit = mediaSegment;
1000
1020
  if (line.includes("BYTERANGE")) {
1001
- const p = getAttribute(line, "BYTERANGE");
1002
- const [n, o] = getRange(p);
1021
+ const [n, o] = getRange(getAttribute(line, "BYTERANGE"));
1003
1022
  mediaSegment.expectLength = n;
1004
1023
  mediaSegment.startRange = o || 0;
1005
1024
  }
@@ -1048,10 +1067,7 @@ var HlsExtractor = class {
1048
1067
  async extractStreams(rawText) {
1049
1068
  this.#m3u8Content = rawText;
1050
1069
  this.preProcessContent();
1051
- if (this.#m3u8Content.includes(HLS_TAGS.extXStreamInf)) {
1052
- console.log("Master m3u8 found");
1053
- return this.#parseMasterList().then((lists) => distinctBy(lists, (list) => list.url));
1054
- }
1070
+ if (this.#m3u8Content.includes(HLS_TAGS.extXStreamInf)) return this.#parseMasterList().then((lists) => distinctBy(lists, (list) => list.url));
1055
1071
  const playlist = await this.#parseList();
1056
1072
  const streamSpec = new StreamSpec();
1057
1073
  streamSpec.url = this.parserConfig.url;
@@ -1061,8 +1077,7 @@ var HlsExtractor = class {
1061
1077
  }
1062
1078
  async #loadM3u8FromUrl(url) {
1063
1079
  if (url.startsWith("file:")) {
1064
- const uri = new URL(url);
1065
- const filePath = uri.pathname;
1080
+ const filePath = new URL(url).pathname;
1066
1081
  this.#m3u8Content = await (0, node_fs_promises.readFile)(filePath, "utf8");
1067
1082
  } else if (url.startsWith("http")) try {
1068
1083
  const response = await fetch(url, { headers: this.parserConfig.headers });
@@ -1102,9 +1117,9 @@ var HlsExtractor = class {
1102
1117
  else list.playlist = newPlaylist;
1103
1118
  if (list.mediaType === MEDIA_TYPES.SUBTITLES) {
1104
1119
  const a = list.playlist.mediaParts.some((part) => part.mediaSegments.some((segment) => segment.url.includes(".ttml")));
1105
- const b = list.playlist.mediaParts.some((part) => part.mediaSegments.some((segment) => segment.url.includes(".vtt") || segment.url.includes(".webvtt")));
1120
+ const b$1 = list.playlist.mediaParts.some((part) => part.mediaSegments.some((segment) => segment.url.includes(".vtt") || segment.url.includes(".webvtt")));
1106
1121
  if (a) list.extension = "ttml";
1107
- if (b) list.extension = "vtt";
1122
+ if (b$1) list.extension = "vtt";
1108
1123
  } else list.extension = list.playlist.mediaInit ? "m4s" : "ts";
1109
1124
  }
1110
1125
  }
@@ -1132,8 +1147,7 @@ var StreamExtractor = class {
1132
1147
  }
1133
1148
  async loadSourceFromUrl(url) {
1134
1149
  if (url.startsWith("file:")) {
1135
- const uri = new URL(url);
1136
- const filePath = uri.pathname;
1150
+ const filePath = new URL(url).pathname;
1137
1151
  this.#rawText = await (0, node_fs_promises.readFile)(filePath, "utf8");
1138
1152
  this.#setUrl(url);
1139
1153
  } else if (url.startsWith("http")) {
@@ -1160,8 +1174,11 @@ var StreamExtractor = class {
1160
1174
  } else if (this.#rawText.includes("</MPD>") && this.#rawText.includes("<MPD")) {
1161
1175
  this.#extractor = new DashExtractor(this.#parserConfig);
1162
1176
  rawType = "mpd";
1163
- } else if (this.#rawText.includes("</SmoothStreamingMedia>") && this.#rawText.includes("<SmoothStreamingMedia")) rawType = "ism";
1164
- else if (rawText === "<RE_LIVE_TS>") {} else throw new Error("Unsupported stream type");
1177
+ } else if (this.#rawText.includes("</SmoothStreamingMedia>") && this.#rawText.includes("<SmoothStreamingMedia")) {
1178
+ rawType = "ism";
1179
+ throw new Error("Smooth Streaming is not supported yet");
1180
+ } else if (rawText === "<RE_LIVE_TS>") throw new Error("Live TS is not supported yet");
1181
+ else throw new Error("Unsupported stream type");
1165
1182
  this.#rawFiles[`raw.${rawType}`] = rawText;
1166
1183
  }
1167
1184
  async extractStreams() {
@@ -1176,13 +1193,23 @@ var StreamExtractor = class {
1176
1193
  };
1177
1194
 
1178
1195
  //#endregion
1179
- exports.ContentProcessor = ContentProcessor
1180
- exports.DefaultUrlProcessor = DefaultUrlProcessor
1181
- exports.ENCRYPT_METHODS = ENCRYPT_METHODS
1182
- exports.EXTRACTOR_TYPES = EXTRACTOR_TYPES
1183
- exports.KeyProcessor = KeyProcessor
1184
- exports.MEDIA_TYPES = MEDIA_TYPES
1185
- exports.ParserConfig = ParserConfig
1186
- exports.ROLE_TYPE = ROLE_TYPE
1187
- exports.StreamExtractor = StreamExtractor
1188
- exports.UrlProcessor = UrlProcessor
1196
+ exports.DASH_TAGS = DASH_TAGS;
1197
+ exports.DashExtractor = DashExtractor;
1198
+ exports.DefaultDashContentProcessor = DefaultDashContentProcessor;
1199
+ exports.DefaultHlsContentProcessor = DefaultHlsContentProcessor;
1200
+ exports.DefaultHlsKeyProcessor = DefaultHlsKeyProcessor;
1201
+ exports.DefaultUrlProcessor = DefaultUrlProcessor;
1202
+ exports.ENCRYPT_METHODS = ENCRYPT_METHODS;
1203
+ exports.EXTRACTOR_TYPES = EXTRACTOR_TYPES;
1204
+ exports.EncryptInfo = EncryptInfo;
1205
+ exports.HLS_TAGS = HLS_TAGS;
1206
+ exports.HlsExtractor = HlsExtractor;
1207
+ exports.MEDIA_TYPES = MEDIA_TYPES;
1208
+ exports.MediaPart = MediaPart;
1209
+ exports.MediaSegment = MediaSegment;
1210
+ exports.ParserConfig = ParserConfig;
1211
+ exports.ROLE_TYPE = ROLE_TYPE;
1212
+ exports.StreamExtractor = StreamExtractor;
1213
+ exports.StreamSpec = StreamSpec;
1214
+ exports.getRange = getRange;
1215
+ exports.parseRange = parseRange;