dasha 4.0.0-alpha.5 → 4.0.0-alpha.6
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 +580 -206
- package/dist/dasha.d.cts +139 -36
- package/dist/dasha.d.mts +139 -36
- package/dist/dasha.mjs +571 -205
- package/package.json +1 -1
package/dist/dasha.cjs
CHANGED
|
@@ -32,6 +32,65 @@ node_path = __toESM(node_path);
|
|
|
32
32
|
let temporal_polyfill = require("temporal-polyfill");
|
|
33
33
|
let __xmldom_xmldom = require("@xmldom/xmldom");
|
|
34
34
|
|
|
35
|
+
//#region lib/shared/codec.ts
|
|
36
|
+
/**
|
|
37
|
+
* List of known video codecs, ordered by encoding preference.
|
|
38
|
+
* @group Codecs
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
const VIDEO_CODECS = [
|
|
42
|
+
"avc",
|
|
43
|
+
"hevc",
|
|
44
|
+
"vp8",
|
|
45
|
+
"vp9",
|
|
46
|
+
"av1",
|
|
47
|
+
"vc1"
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* List of known video dynamic ranges.
|
|
51
|
+
* @group Codecs
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
const VIDEO_DYNAMIC_RANGES = [
|
|
55
|
+
"sdr",
|
|
56
|
+
"hlg",
|
|
57
|
+
"hdr10",
|
|
58
|
+
"hdr10+",
|
|
59
|
+
"dv"
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* List of known audio codecs, ordered by encoding preference.
|
|
63
|
+
* @group Codecs
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
const AUDIO_CODECS = [
|
|
67
|
+
"aac",
|
|
68
|
+
"opus",
|
|
69
|
+
"mp3",
|
|
70
|
+
"vorbis",
|
|
71
|
+
"flac",
|
|
72
|
+
"alac",
|
|
73
|
+
"ac3",
|
|
74
|
+
"eac3",
|
|
75
|
+
"dts"
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* List of known subtitle codecs, ordered by encoding preference.
|
|
79
|
+
* @group Codecs
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
const SUBTITLE_CODECS = [
|
|
83
|
+
"srt",
|
|
84
|
+
"vtt",
|
|
85
|
+
"ttml",
|
|
86
|
+
"dfxp",
|
|
87
|
+
"ssa",
|
|
88
|
+
"ass",
|
|
89
|
+
"stpp",
|
|
90
|
+
"wvtt"
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
35
94
|
//#region lib/shared/encrypt-method.ts
|
|
36
95
|
const ENCRYPT_METHODS = {
|
|
37
96
|
NONE: "none",
|
|
@@ -116,99 +175,166 @@ var MediaSegment = class MediaSegment {
|
|
|
116
175
|
};
|
|
117
176
|
|
|
118
177
|
//#endregion
|
|
119
|
-
//#region lib/shared/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
178
|
+
//#region lib/shared/playlist.ts
|
|
179
|
+
var Playlist = class {
|
|
180
|
+
url = "";
|
|
181
|
+
isLive = false;
|
|
182
|
+
refreshIntervalMs = 15e3;
|
|
183
|
+
get totalDuration() {
|
|
184
|
+
let result = 0;
|
|
185
|
+
for (const part of this.mediaParts) for (const segment of part.mediaSegments) result += segment.duration;
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
targetDuration;
|
|
189
|
+
mediaInit;
|
|
190
|
+
mediaParts = [];
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region lib/shared/role-type.ts
|
|
195
|
+
const ROLE_TYPE = {
|
|
196
|
+
Subtitle: 0,
|
|
197
|
+
Main: 1,
|
|
198
|
+
Alternate: 2,
|
|
199
|
+
Supplementary: 3,
|
|
200
|
+
Commentary: 4,
|
|
201
|
+
Dub: 5,
|
|
202
|
+
Description: 6,
|
|
203
|
+
Sign: 7,
|
|
204
|
+
Metadata: 8,
|
|
205
|
+
ForcedSubtitle: 9
|
|
125
206
|
};
|
|
126
207
|
|
|
127
208
|
//#endregion
|
|
128
|
-
//#region lib/shared/stream-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
209
|
+
//#region lib/shared/stream-info.ts
|
|
210
|
+
/**
|
|
211
|
+
* List of all stream types.
|
|
212
|
+
* @group Miscellaneous
|
|
213
|
+
* @public
|
|
214
|
+
*/
|
|
215
|
+
const ALL_STREAM_TYPES = [
|
|
216
|
+
"video",
|
|
217
|
+
"audio",
|
|
218
|
+
"subtitle"
|
|
219
|
+
];
|
|
220
|
+
var StreamInfo = class {
|
|
221
|
+
codec;
|
|
222
|
+
languageCode;
|
|
223
|
+
bitrate;
|
|
133
224
|
name;
|
|
225
|
+
url = "";
|
|
226
|
+
originalUrl = "";
|
|
227
|
+
playlist;
|
|
134
228
|
default;
|
|
135
229
|
skippedDuration;
|
|
136
|
-
bandwidth;
|
|
137
|
-
codecs = null;
|
|
138
|
-
resolution;
|
|
139
|
-
frameRate;
|
|
140
|
-
channels = null;
|
|
141
|
-
extension = null;
|
|
142
230
|
role;
|
|
143
231
|
videoRange;
|
|
144
232
|
characteristics;
|
|
145
233
|
publishTime;
|
|
234
|
+
groupId = null;
|
|
146
235
|
audioId;
|
|
147
236
|
videoId;
|
|
148
237
|
subtitleId;
|
|
149
238
|
periodId = null;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
239
|
+
extension = null;
|
|
240
|
+
/**
|
|
241
|
+
* @deprecated Use `codec`
|
|
242
|
+
*/
|
|
243
|
+
codecs = null;
|
|
244
|
+
/**
|
|
245
|
+
* @deprecated Use `numberOfChannels` in `AudioStreamInfo`
|
|
246
|
+
*/
|
|
247
|
+
channels = null;
|
|
248
|
+
/**
|
|
249
|
+
* @deprecated Use `width` and `height` in `VideoStreamInfo`
|
|
250
|
+
*/
|
|
251
|
+
resolution;
|
|
252
|
+
/**
|
|
253
|
+
* @deprecated Use `bitrate`
|
|
254
|
+
*/
|
|
255
|
+
bandwidth;
|
|
153
256
|
get segmentsCount() {
|
|
154
257
|
return this.playlist?.mediaParts.reduce((sum, part) => sum + part.mediaSegments.length, 0) ?? 0;
|
|
155
258
|
}
|
|
259
|
+
};
|
|
260
|
+
var VideoStreamInfo = class extends StreamInfo {
|
|
261
|
+
codec;
|
|
262
|
+
width;
|
|
263
|
+
height;
|
|
264
|
+
frameRate;
|
|
265
|
+
dynamicRange;
|
|
266
|
+
dolbyVisionProfile;
|
|
267
|
+
get type() {
|
|
268
|
+
return "video";
|
|
269
|
+
}
|
|
270
|
+
constructor(info) {
|
|
271
|
+
super();
|
|
272
|
+
this.codec = info?.codec;
|
|
273
|
+
}
|
|
156
274
|
toShortString() {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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();
|
|
275
|
+
const prefix = `Vid `;
|
|
276
|
+
const bitrate = this.bitrate ? `${this.bitrate / 1e3} Kbps` : "";
|
|
277
|
+
return `${prefix} | ${[
|
|
278
|
+
this.width ? `${this.width}x${this.height}` : "",
|
|
279
|
+
bitrate,
|
|
280
|
+
this.groupId,
|
|
281
|
+
this.frameRate,
|
|
282
|
+
this.codec ?? this.codecs,
|
|
283
|
+
this.videoRange,
|
|
284
|
+
this.role
|
|
285
|
+
].filter(Boolean).join(" | ")}`.trim();
|
|
196
286
|
}
|
|
197
287
|
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
288
|
+
var AudioStreamInfo = class extends StreamInfo {
|
|
289
|
+
codec;
|
|
290
|
+
numberOfChannels;
|
|
291
|
+
sampleRate;
|
|
292
|
+
atmos;
|
|
293
|
+
descriptive;
|
|
294
|
+
joc;
|
|
295
|
+
get type() {
|
|
296
|
+
return "audio";
|
|
297
|
+
}
|
|
298
|
+
constructor(info) {
|
|
299
|
+
super();
|
|
300
|
+
this.codec = info?.codec;
|
|
301
|
+
}
|
|
302
|
+
toShortString() {
|
|
303
|
+
const prefix = `Aud `;
|
|
304
|
+
const bitrate = this.bitrate ? `${this.bitrate / 1e3} Kbps` : "";
|
|
305
|
+
const channels = this.numberOfChannels || this.channels ? `${this.numberOfChannels ?? this.channels}CH` : "";
|
|
306
|
+
return `${prefix} | ${[
|
|
307
|
+
this.groupId,
|
|
308
|
+
bitrate,
|
|
309
|
+
this.name,
|
|
310
|
+
this.codec ?? this.codecs,
|
|
311
|
+
this.languageCode,
|
|
312
|
+
channels,
|
|
313
|
+
this.role
|
|
314
|
+
].filter(Boolean).join(" | ")}`.trim();
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var SubtitleStreamInfo = class extends StreamInfo {
|
|
318
|
+
codec;
|
|
319
|
+
cc;
|
|
320
|
+
sdh;
|
|
321
|
+
forced;
|
|
322
|
+
get type() {
|
|
323
|
+
return "subtitle";
|
|
324
|
+
}
|
|
325
|
+
constructor(info) {
|
|
326
|
+
super();
|
|
327
|
+
this.codec = info?.codec;
|
|
328
|
+
}
|
|
329
|
+
toShortString() {
|
|
330
|
+
return `Sub | ${[
|
|
331
|
+
this.groupId,
|
|
332
|
+
this.languageCode,
|
|
333
|
+
this.name,
|
|
334
|
+
this.codecs,
|
|
335
|
+
this.role
|
|
336
|
+
].filter(Boolean).join(" | ")}`.trim();
|
|
337
|
+
}
|
|
212
338
|
};
|
|
213
339
|
|
|
214
340
|
//#endregion
|
|
@@ -453,22 +579,7 @@ const distinctBy = (array, callbackfn) => {
|
|
|
453
579
|
return true;
|
|
454
580
|
});
|
|
455
581
|
};
|
|
456
|
-
|
|
457
|
-
//#endregion
|
|
458
|
-
//#region lib/shared/playlist.ts
|
|
459
|
-
var Playlist = class {
|
|
460
|
-
url = "";
|
|
461
|
-
isLive = false;
|
|
462
|
-
refreshIntervalMs = 15e3;
|
|
463
|
-
get totalDuration() {
|
|
464
|
-
let result = 0;
|
|
465
|
-
for (const part of this.mediaParts) for (const segment of part.mediaSegments) result += segment.duration;
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
targetDuration;
|
|
469
|
-
mediaInit;
|
|
470
|
-
mediaParts = [];
|
|
471
|
-
};
|
|
582
|
+
const parseMimes = (codecs) => codecs.toLowerCase().split(",").map((codec) => codec.trim().split(".")[0]);
|
|
472
583
|
|
|
473
584
|
//#endregion
|
|
474
585
|
//#region lib/dash/dash-utils.ts
|
|
@@ -482,8 +593,242 @@ const parseRange = (range) => {
|
|
|
482
593
|
return [startRange, end - startRange + 1];
|
|
483
594
|
};
|
|
484
595
|
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region lib/dash/dash-video.ts
|
|
598
|
+
const PRIMARIES = {
|
|
599
|
+
Unspecified: 0,
|
|
600
|
+
BT_709: 1,
|
|
601
|
+
BT_601_625: 5,
|
|
602
|
+
BT_601_525: 6,
|
|
603
|
+
BT_2020_and_2100: 9,
|
|
604
|
+
SMPTE_ST_2113_and_EG_4321: 12
|
|
605
|
+
};
|
|
606
|
+
const TRANSFER = {
|
|
607
|
+
Unspecified: 0,
|
|
608
|
+
BT_709: 1,
|
|
609
|
+
BT_601: 6,
|
|
610
|
+
BT_2020: 14,
|
|
611
|
+
BT_2100: 15,
|
|
612
|
+
BT_2100_PQ: 16,
|
|
613
|
+
BT_2100_HLG: 18
|
|
614
|
+
};
|
|
615
|
+
const MATRIX = {
|
|
616
|
+
RGB: 0,
|
|
617
|
+
YCbCr_BT_709: 1,
|
|
618
|
+
YCbCr_BT_601_625: 5,
|
|
619
|
+
YCbCr_BT_601_525: 6,
|
|
620
|
+
YCbCr_BT_2020_and_2100: 9,
|
|
621
|
+
ICtCp_BT_2100: 14
|
|
622
|
+
};
|
|
623
|
+
const parseVideoCodecFromMime = (mime) => {
|
|
624
|
+
const target = mime.toLowerCase().trim().split(".")[0];
|
|
625
|
+
const avc = [
|
|
626
|
+
"avc1",
|
|
627
|
+
"avc2",
|
|
628
|
+
"avc3",
|
|
629
|
+
"dva1",
|
|
630
|
+
"dvav"
|
|
631
|
+
];
|
|
632
|
+
const hevc = [
|
|
633
|
+
"hev1",
|
|
634
|
+
"hev2",
|
|
635
|
+
"hev3",
|
|
636
|
+
"hvc1",
|
|
637
|
+
"hvc2",
|
|
638
|
+
"hvc3",
|
|
639
|
+
"dvh1",
|
|
640
|
+
"dvhe",
|
|
641
|
+
"lhv1",
|
|
642
|
+
"lhe1"
|
|
643
|
+
];
|
|
644
|
+
const vc1 = ["vc-1"];
|
|
645
|
+
const vp8 = ["vp08", "vp8"];
|
|
646
|
+
const vp9 = ["vp09", "vp9"];
|
|
647
|
+
const av1 = ["av01"];
|
|
648
|
+
if (avc.includes(target)) return "avc";
|
|
649
|
+
if (hevc.includes(target)) return "hevc";
|
|
650
|
+
if (vc1.includes(target)) return "vc1";
|
|
651
|
+
if (vp8.includes(target)) return "vp8";
|
|
652
|
+
if (vp9.includes(target)) return "vp9";
|
|
653
|
+
if (av1.includes(target)) return "av1";
|
|
654
|
+
throw new Error(`The MIME ${mime} is not supported as video codec`);
|
|
655
|
+
};
|
|
656
|
+
const parseDynamicRangeFromCicp = (primaries, transfer, matrix) => {
|
|
657
|
+
if (transfer == 5) transfer = TRANSFER.BT_601;
|
|
658
|
+
if (primaries == PRIMARIES.Unspecified && transfer == TRANSFER.Unspecified && matrix == MATRIX.RGB) return "sdr";
|
|
659
|
+
else if ([PRIMARIES.BT_601_625, PRIMARIES.BT_601_525].includes(primaries)) return "sdr";
|
|
660
|
+
else if (TRANSFER.BT_2100_PQ === transfer) return "hdr10";
|
|
661
|
+
else if (TRANSFER.BT_2100_HLG === transfer) return "hlg";
|
|
662
|
+
else return "sdr";
|
|
663
|
+
};
|
|
664
|
+
const parseVideoCodec = (codecs) => {
|
|
665
|
+
for (const codec of codecs.toLowerCase().split(",")) {
|
|
666
|
+
const mime = codec.trim().split(".")[0];
|
|
667
|
+
try {
|
|
668
|
+
return parseVideoCodecFromMime(mime);
|
|
669
|
+
} catch (e) {
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
throw new Error(`No MIME types matched any supported Video Codecs in ${codecs}`);
|
|
674
|
+
};
|
|
675
|
+
const tryParseVideoCodec = (codecs) => {
|
|
676
|
+
try {
|
|
677
|
+
return parseVideoCodec(codecs);
|
|
678
|
+
} catch (e) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
const parseDynamicRange = (codecs, supplementalProps = [], essentialProps = []) => {
|
|
683
|
+
if ([
|
|
684
|
+
"dva1",
|
|
685
|
+
"dvav",
|
|
686
|
+
"dvhe",
|
|
687
|
+
"dvh1"
|
|
688
|
+
].some((value) => codecs.startsWith(value))) return "dv";
|
|
689
|
+
const primariesScheme = "urn:mpeg:mpegB:cicp:ColourPrimaries";
|
|
690
|
+
const transferScheme = "urn:mpeg:mpegB:cicp:TransferCharacteristics";
|
|
691
|
+
const matrixScheme = "urn:mpeg:mpegB:cicp:MatrixCoefficients";
|
|
692
|
+
const allProps = [...essentialProps, ...supplementalProps];
|
|
693
|
+
const getValues = (schemeIdUri) => allProps.filter((prop) => prop.schemeIdUri === schemeIdUri).map((prop) => parseInt(prop.value));
|
|
694
|
+
return parseDynamicRangeFromCicp(getValues(primariesScheme).reduce((acc, current) => acc + current, 0), getValues(transferScheme).reduce((acc, current) => acc + current, 0), getValues(matrixScheme).reduce((acc, current) => acc + current, 0));
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region lib/dash/dash-subtitle.ts
|
|
699
|
+
const parseSubtitleCodecFromMime = (mime) => {
|
|
700
|
+
switch (mime.toLowerCase().trim().split(".")[0]) {
|
|
701
|
+
case "srt":
|
|
702
|
+
case "x-subrip": return "srt";
|
|
703
|
+
case "ssa": return "ssa";
|
|
704
|
+
case "ass": return "ass";
|
|
705
|
+
case "ttml": return "ttml";
|
|
706
|
+
case "vtt": return "vtt";
|
|
707
|
+
case "stpp": return "stpp";
|
|
708
|
+
case "wvtt": return "wvtt";
|
|
709
|
+
default: throw new Error(`The MIME ${mime} is not supported as subtitle codec`);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
const parseSubtitleCodec = (codecs) => {
|
|
713
|
+
const mimes = parseMimes(codecs);
|
|
714
|
+
for (const mime of mimes) try {
|
|
715
|
+
return parseSubtitleCodecFromMime(mime);
|
|
716
|
+
} catch (e) {
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
throw new Error(`No MIME types matched any supported Subtitle Codecs in ${codecs}`);
|
|
720
|
+
};
|
|
721
|
+
const tryParseSubtitleCodec = (codecs) => {
|
|
722
|
+
try {
|
|
723
|
+
return parseSubtitleCodec(codecs);
|
|
724
|
+
} catch (e) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
const checkIsClosedCaption = (roles = []) => {
|
|
729
|
+
for (const role of roles) if (role.schemeIdUri === "urn:mpeg:dash:role:2011" && role.value === "caption") return true;
|
|
730
|
+
return false;
|
|
731
|
+
};
|
|
732
|
+
const checkIsSdh = (accessibilities = []) => {
|
|
733
|
+
for (const accessibility of accessibilities) {
|
|
734
|
+
const { schemeIdUri, value } = accessibility;
|
|
735
|
+
if (schemeIdUri === "urn:tva:metadata:cs:AudioPurposeCS:2007" && value === "2") return true;
|
|
736
|
+
}
|
|
737
|
+
return false;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region lib/dash/dash-audio.ts
|
|
742
|
+
const parseAudioCodecFromMime = (mime) => {
|
|
743
|
+
switch (mime.toLowerCase().trim().split(".")[0]) {
|
|
744
|
+
case "mp4a": return "aac";
|
|
745
|
+
case "ac-3": return "ac3";
|
|
746
|
+
case "ec-3": return "eac3";
|
|
747
|
+
case "opus": return "opus";
|
|
748
|
+
case "dtsc": return "dts";
|
|
749
|
+
case "alac": return "alac";
|
|
750
|
+
case "flac": return "flac";
|
|
751
|
+
default: throw new Error(`The MIME ${mime} is not supported as audio codec`);
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
const parseAudioCodec = (codecs) => {
|
|
755
|
+
const mimes = parseMimes(codecs);
|
|
756
|
+
for (const mime of mimes) try {
|
|
757
|
+
return parseAudioCodecFromMime(mime);
|
|
758
|
+
} catch (e) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
throw new Error(`No MIME types matched any supported Audio Codecs in ${codecs}`);
|
|
762
|
+
};
|
|
763
|
+
const tryParseAudioCodec = (codecs) => {
|
|
764
|
+
try {
|
|
765
|
+
return parseAudioCodec(codecs);
|
|
766
|
+
} catch (e) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
const getDolbyDigitalPlusComplexityIndex = (supplementalProps = []) => {
|
|
771
|
+
const targetScheme = "tag:dolby.com,2018:dash:EC3_ExtensionComplexityIndex:2018";
|
|
772
|
+
for (const prop of supplementalProps) if (prop.schemeIdUri === targetScheme) return parseInt(prop.value);
|
|
773
|
+
};
|
|
774
|
+
const checkIsDescriptive = (accessibilities = []) => {
|
|
775
|
+
for (const accessibility of accessibilities) {
|
|
776
|
+
const { schemeIdUri, value } = accessibility;
|
|
777
|
+
if (schemeIdUri == "urn:mpeg:dash:role:2011" && value === "descriptive" || schemeIdUri == "urn:tva:metadata:cs:AudioPurposeCS:2007" && value === "1") return true;
|
|
778
|
+
}
|
|
779
|
+
return false;
|
|
780
|
+
};
|
|
781
|
+
const parseChannels = (channels) => {
|
|
782
|
+
const isDigit = (char) => char >= "0" && char <= "9";
|
|
783
|
+
if (typeof channels === "string") {
|
|
784
|
+
if (channels.toUpperCase() == "A000") return 2;
|
|
785
|
+
else if (channels.toUpperCase() == "F801") return 5.1;
|
|
786
|
+
else if (isDigit(channels.replace("ch", "").replace(".", "")[0])) return parseFloat(channels.replace("ch", ""));
|
|
787
|
+
throw new Error(`Unsupported audio channels value, '${channels}'`);
|
|
788
|
+
}
|
|
789
|
+
return parseFloat(channels);
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
//#endregion
|
|
793
|
+
//#region lib/shared/pipe.ts
|
|
794
|
+
const pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);
|
|
795
|
+
|
|
485
796
|
//#endregion
|
|
486
797
|
//#region lib/dash/dash-extractor.ts
|
|
798
|
+
const getStreamInfoByCodecs = (codecs) => {
|
|
799
|
+
const videoCodec = tryParseVideoCodec(codecs);
|
|
800
|
+
if (videoCodec) return new VideoStreamInfo({ codec: videoCodec });
|
|
801
|
+
const audioCodec = tryParseAudioCodec(codecs);
|
|
802
|
+
if (audioCodec) return new AudioStreamInfo({ codec: audioCodec });
|
|
803
|
+
const subtitleCodec = tryParseSubtitleCodec(codecs);
|
|
804
|
+
if (subtitleCodec) return new SubtitleStreamInfo({ codec: subtitleCodec });
|
|
805
|
+
return new VideoStreamInfo();
|
|
806
|
+
};
|
|
807
|
+
const selectNonEmpty = (args) => {
|
|
808
|
+
for (const element of args.elements) {
|
|
809
|
+
const results = element.getElementsByTagName(args.tag);
|
|
810
|
+
if (results.length) return results;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
const toSchemeValueArray = (elements) => {
|
|
814
|
+
const results = [];
|
|
815
|
+
if (!elements) return results;
|
|
816
|
+
for (const element of elements) {
|
|
817
|
+
const schemeIdUri = element.getAttribute("schemeIdUri");
|
|
818
|
+
const value = element.getAttribute("value");
|
|
819
|
+
results.push({
|
|
820
|
+
schemeIdUri,
|
|
821
|
+
value
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
return results;
|
|
825
|
+
};
|
|
826
|
+
const getTagAttrs = (tag, ...elements) => {
|
|
827
|
+
return pipe(selectNonEmpty, toSchemeValueArray)({
|
|
828
|
+
tag,
|
|
829
|
+
elements
|
|
830
|
+
});
|
|
831
|
+
};
|
|
487
832
|
var DashExtractor = class DashExtractor {
|
|
488
833
|
static #DEFAULT_METHOD = ENCRYPT_METHODS.CENC;
|
|
489
834
|
get extractorType() {
|
|
@@ -513,11 +858,10 @@ var DashExtractor = class DashExtractor {
|
|
|
513
858
|
return Number(d.toFixed(3));
|
|
514
859
|
}
|
|
515
860
|
async extractStreams(rawText) {
|
|
516
|
-
const
|
|
861
|
+
const streamInfos = [];
|
|
517
862
|
this.#mpdContent = rawText;
|
|
518
863
|
const mpdElement = new __xmldom_xmldom.DOMParser().parseFromString(this.#mpdContent, "text/xml").getElementsByTagName("MPD")[0];
|
|
519
864
|
const isLive = mpdElement.getAttribute("type") === "dynamic";
|
|
520
|
-
mpdElement.getAttribute("maxSegmentDuration");
|
|
521
865
|
const availabilityStartTime = mpdElement.getAttribute("availabilityStartTime");
|
|
522
866
|
const timeShiftBufferDepth = mpdElement.getAttribute("timeShiftBufferDepth") || "PT1M";
|
|
523
867
|
const publishTime = mpdElement.getAttribute("publishTime");
|
|
@@ -545,49 +889,58 @@ var DashExtractor = class DashExtractor {
|
|
|
545
889
|
for (const representation of representations) {
|
|
546
890
|
segBaseUrl = this.#extendBaseUrl(representation, segBaseUrl);
|
|
547
891
|
if (!mimeType) mimeType = representation.getAttribute("contentType") || representation.getAttribute("mimeType") || "";
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
else if (
|
|
566
|
-
|
|
892
|
+
const codecParameterString = representation.getAttribute("codecs") || adaptationSet.getAttribute("codecs");
|
|
893
|
+
const widthParameterString = representation.getAttribute("width");
|
|
894
|
+
const heightParameterString = representation.getAttribute("height");
|
|
895
|
+
const roles = getTagAttrs("Role", representation, adaptationSet);
|
|
896
|
+
const supplementalProps = getTagAttrs("SupplementalProperty", representation, adaptationSet);
|
|
897
|
+
const essentialProps = getTagAttrs("EssentialProperty", representation, adaptationSet);
|
|
898
|
+
const accessibilities = getTagAttrs("Accessibility", representation, adaptationSet);
|
|
899
|
+
const channelsString = getTagAttrs("AudioChannelConfiguration", adaptationSet, representation)[0]?.value;
|
|
900
|
+
const streamInfo = getStreamInfoByCodecs(codecParameterString) ?? new VideoStreamInfo();
|
|
901
|
+
const bitrate = Number(representation.getAttribute("bandwidth") ?? "");
|
|
902
|
+
streamInfo.languageCode = this.#filterLanguage(representation.getAttribute("lang") || adaptationSet.getAttribute("lang"));
|
|
903
|
+
if (streamInfo.type === "video") {
|
|
904
|
+
streamInfo.bitrate = bitrate;
|
|
905
|
+
streamInfo.width = Number(widthParameterString);
|
|
906
|
+
streamInfo.height = Number(heightParameterString);
|
|
907
|
+
streamInfo.frameRate = frameRate || this.#getFrameRate(representation);
|
|
908
|
+
if (supplementalProps && essentialProps) streamInfo.dynamicRange = parseDynamicRange(codecParameterString, supplementalProps, essentialProps);
|
|
909
|
+
} else if (streamInfo.type === "audio") {
|
|
910
|
+
streamInfo.bitrate = bitrate;
|
|
911
|
+
if (accessibilities) streamInfo.descriptive = checkIsDescriptive(accessibilities);
|
|
912
|
+
if (supplementalProps) streamInfo.joc = getDolbyDigitalPlusComplexityIndex(supplementalProps);
|
|
913
|
+
if (channelsString) {
|
|
914
|
+
streamInfo.numberOfChannels = parseChannels(channelsString);
|
|
915
|
+
streamInfo.channels = channelsString;
|
|
916
|
+
}
|
|
917
|
+
} else if (streamInfo.type === "subtitle") {
|
|
918
|
+
if (roles) streamInfo.cc = checkIsClosedCaption(roles);
|
|
919
|
+
if (accessibilities) streamInfo.sdh = checkIsSdh(accessibilities);
|
|
920
|
+
}
|
|
921
|
+
streamInfo.url = this.#mpdUrl;
|
|
922
|
+
streamInfo.originalUrl = this.#parserConfig.originalUrl;
|
|
923
|
+
streamInfo.playlist = new Playlist();
|
|
924
|
+
streamInfo.playlist.mediaParts.push(new MediaPart());
|
|
925
|
+
streamInfo.periodId = periodId;
|
|
926
|
+
streamInfo.groupId = representation.getAttribute("id");
|
|
927
|
+
streamInfo.codecs = representation.getAttribute("codecs") || adaptationSet.getAttribute("codecs");
|
|
567
928
|
const volumeAdjust = representation.getAttribute("volumeAdjust");
|
|
568
|
-
if (volumeAdjust)
|
|
929
|
+
if (volumeAdjust) streamInfo.groupId = streamInfo.groupId + "-" + volumeAdjust;
|
|
569
930
|
const mType = representation.getAttribute("mimeType") || adaptationSet.getAttribute("mimeType");
|
|
570
931
|
if (mType) {
|
|
571
932
|
const mTypeSplit = mType.split("/");
|
|
572
|
-
|
|
933
|
+
streamInfo.extension = mTypeSplit.length === 2 ? mTypeSplit[1] : null;
|
|
573
934
|
}
|
|
574
|
-
|
|
575
|
-
const role = representation.getElementsByTagName("Role")[0] || adaptationSet.getElementsByTagName("Role")[0];
|
|
935
|
+
const role = roles?.[0];
|
|
576
936
|
if (role) {
|
|
577
|
-
const roleValue = role.
|
|
937
|
+
const roleValue = role.value;
|
|
578
938
|
const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
579
|
-
|
|
580
|
-
streamSpec.role = roleType;
|
|
581
|
-
if (roleType === ROLE_TYPE.Subtitle) {
|
|
582
|
-
streamSpec.mediaType = MEDIA_TYPES.SUBTITLES;
|
|
583
|
-
if (mType?.includes("ttml")) streamSpec.extension = "ttml";
|
|
584
|
-
} else if (roleType === ROLE_TYPE.ForcedSubtitle) streamSpec.mediaType = MEDIA_TYPES.SUBTITLES;
|
|
939
|
+
streamInfo.role = ROLE_TYPE[roleValue.split("-").map(capitalize).join("")];
|
|
585
940
|
}
|
|
586
|
-
|
|
587
|
-
if (timeShiftBufferDepth)
|
|
588
|
-
|
|
589
|
-
if (audioChannelConfiguration) streamSpec.channels = audioChannelConfiguration.getAttribute("value");
|
|
590
|
-
if (publishTime) streamSpec.publishTime = new Date(publishTime);
|
|
941
|
+
streamInfo.playlist.isLive = isLive;
|
|
942
|
+
if (timeShiftBufferDepth) streamInfo.playlist.refreshIntervalMs = temporal_polyfill.Temporal.Duration.from(timeShiftBufferDepth).total("milliseconds") / 2;
|
|
943
|
+
if (publishTime) streamInfo.publishTime = new Date(publishTime);
|
|
591
944
|
const segmentBaseElement = representation.getElementsByTagName("SegmentBase")[0];
|
|
592
945
|
if (segmentBaseElement) {
|
|
593
946
|
const initialization = segmentBaseElement.getElementsByTagName("Initialization")[0];
|
|
@@ -598,7 +951,7 @@ var DashExtractor = class DashExtractor {
|
|
|
598
951
|
mediaSegment.index = 0;
|
|
599
952
|
mediaSegment.url = segBaseUrl;
|
|
600
953
|
mediaSegment.duration = periodDurationSeconds;
|
|
601
|
-
|
|
954
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(mediaSegment);
|
|
602
955
|
} else {
|
|
603
956
|
const initUrl = combineUrl(segBaseUrl, sourceUrl);
|
|
604
957
|
const initRange = initialization.getAttribute("range");
|
|
@@ -610,7 +963,7 @@ var DashExtractor = class DashExtractor {
|
|
|
610
963
|
initSegment.startRange = start;
|
|
611
964
|
initSegment.expectLength = expect;
|
|
612
965
|
}
|
|
613
|
-
|
|
966
|
+
streamInfo.playlist.mediaInit = initSegment;
|
|
614
967
|
}
|
|
615
968
|
}
|
|
616
969
|
}
|
|
@@ -630,7 +983,7 @@ var DashExtractor = class DashExtractor {
|
|
|
630
983
|
initSegment.startRange = start;
|
|
631
984
|
initSegment.expectLength = expect;
|
|
632
985
|
}
|
|
633
|
-
|
|
986
|
+
streamInfo.playlist.mediaInit = initSegment;
|
|
634
987
|
}
|
|
635
988
|
const segmentUrls = segmentList.getElementsByTagName("SegmentURL");
|
|
636
989
|
const timescaleStr = segmentList.getAttribute("timescale") || "1";
|
|
@@ -649,7 +1002,7 @@ var DashExtractor = class DashExtractor {
|
|
|
649
1002
|
segment.startRange = start;
|
|
650
1003
|
segment.expectLength = expect;
|
|
651
1004
|
}
|
|
652
|
-
|
|
1005
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(segment);
|
|
653
1006
|
}
|
|
654
1007
|
}
|
|
655
1008
|
const segmentTemplateElementsOuter = adaptationSet.getElementsByTagName("SegmentTemplate");
|
|
@@ -658,8 +1011,8 @@ var DashExtractor = class DashExtractor {
|
|
|
658
1011
|
const segmentTemplate = segmentTemplateElements[0] || segmentTemplateElementsOuter[0];
|
|
659
1012
|
const segmentTemplateOuter = segmentTemplateElementsOuter[0] || segmentTemplateElements[0];
|
|
660
1013
|
const varDic = {};
|
|
661
|
-
varDic[DASH_TAGS.TemplateRepresentationID] =
|
|
662
|
-
varDic[DASH_TAGS.TemplateBandwidth] =
|
|
1014
|
+
varDic[DASH_TAGS.TemplateRepresentationID] = streamInfo.groupId;
|
|
1015
|
+
varDic[DASH_TAGS.TemplateBandwidth] = bitrate;
|
|
663
1016
|
const presentationTimeOffsetStr = segmentTemplate.getAttribute("presentationTimeOffset") || segmentTemplateOuter.getAttribute("presentationTimeOffset") || "0";
|
|
664
1017
|
const timescaleStr = segmentTemplate.getAttribute("timescale") || segmentTemplateOuter.getAttribute("timescale") || "1";
|
|
665
1018
|
const durationStr = segmentTemplate.getAttribute("duration") || segmentTemplateOuter.getAttribute("duration");
|
|
@@ -671,7 +1024,7 @@ var DashExtractor = class DashExtractor {
|
|
|
671
1024
|
const mediaSegment = new MediaSegment();
|
|
672
1025
|
mediaSegment.index = -1;
|
|
673
1026
|
mediaSegment.url = initUrl;
|
|
674
|
-
|
|
1027
|
+
streamInfo.playlist.mediaInit = mediaSegment;
|
|
675
1028
|
}
|
|
676
1029
|
const mediaTemplate = segmentTemplate.getAttribute("media") || segmentTemplateOuter.getAttribute("media");
|
|
677
1030
|
const segmentTimeline = segmentTemplate.getElementsByTagName("SegmentTimeline")[0];
|
|
@@ -698,7 +1051,7 @@ var DashExtractor = class DashExtractor {
|
|
|
698
1051
|
if (hasTime) mediaSegment.nameFromVar = currentTime.toString();
|
|
699
1052
|
mediaSegment.duration = _duration / timescale;
|
|
700
1053
|
mediaSegment.index = segIndex++;
|
|
701
|
-
|
|
1054
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(mediaSegment);
|
|
702
1055
|
if (_repeatCount < 0) _repeatCount = Math.ceil(periodDurationSeconds * timescale / _duration) - 1;
|
|
703
1056
|
for (let i = 0; i < _repeatCount; i++) {
|
|
704
1057
|
currentTime += _duration;
|
|
@@ -711,7 +1064,7 @@ var DashExtractor = class DashExtractor {
|
|
|
711
1064
|
_mediaSegment.index = segIndex++;
|
|
712
1065
|
_mediaSegment.duration = _duration / timescale;
|
|
713
1066
|
if (_hashTime) _mediaSegment.nameFromVar = currentTime.toString();
|
|
714
|
-
|
|
1067
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(_mediaSegment);
|
|
715
1068
|
}
|
|
716
1069
|
currentTime += _duration;
|
|
717
1070
|
}
|
|
@@ -740,16 +1093,16 @@ var DashExtractor = class DashExtractor {
|
|
|
740
1093
|
if (hasNumber) mediaSegment.nameFromVar = index.toString();
|
|
741
1094
|
mediaSegment.index = isLive ? index : segIndex;
|
|
742
1095
|
mediaSegment.duration = duration / timescale;
|
|
743
|
-
|
|
1096
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(mediaSegment);
|
|
744
1097
|
}
|
|
745
1098
|
}
|
|
746
1099
|
}
|
|
747
|
-
if (
|
|
1100
|
+
if (streamInfo.playlist.mediaParts[0].mediaSegments.length === 0) {
|
|
748
1101
|
const mediaSegment = new MediaSegment();
|
|
749
1102
|
mediaSegment.index = 0;
|
|
750
1103
|
mediaSegment.url = segBaseUrl;
|
|
751
1104
|
mediaSegment.duration = periodDurationSeconds;
|
|
752
|
-
|
|
1105
|
+
streamInfo.playlist.mediaParts[0].mediaSegments.push(mediaSegment);
|
|
753
1106
|
}
|
|
754
1107
|
const adaptationSetProtections = adaptationSet.getElementsByTagName("ContentProtection");
|
|
755
1108
|
const representationProtections = representation.getElementsByTagName("ContentProtection");
|
|
@@ -769,69 +1122,69 @@ var DashExtractor = class DashExtractor {
|
|
|
769
1122
|
else if (schemeIdUri?.includes(playreadySystemId)) encryptInfo.drm.playready = drmData;
|
|
770
1123
|
else continue;
|
|
771
1124
|
}
|
|
772
|
-
if (
|
|
773
|
-
const segments =
|
|
1125
|
+
if (streamInfo.playlist.mediaInit) streamInfo.playlist.mediaInit.encryptInfo = encryptInfo;
|
|
1126
|
+
const segments = streamInfo.playlist.mediaParts[0].mediaSegments;
|
|
774
1127
|
for (const segment of segments) if (!segment.encryptInfo) segment.encryptInfo = encryptInfo;
|
|
775
1128
|
}
|
|
776
|
-
const _index =
|
|
777
|
-
if (_index > -1) if (isLive) {} else if (
|
|
778
|
-
const startIndex =
|
|
779
|
-
const segments =
|
|
1129
|
+
const _index = streamInfos.findIndex((item) => item.type === streamInfo.type && item.periodId !== streamInfo.periodId && item.groupId === streamInfo.groupId && (item.type === "video" && streamInfo.type === "video" ? item.width === streamInfo.width && item.height === streamInfo.height : true));
|
|
1130
|
+
if (_index > -1) if (isLive) {} else if (streamInfos[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).url !== streamInfo.playlist.mediaParts[0].mediaSegments.at(-1)?.url) {
|
|
1131
|
+
const startIndex = streamInfos[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).index + 1;
|
|
1132
|
+
const segments = streamInfo.playlist.mediaParts[0].mediaSegments;
|
|
780
1133
|
for (const segment of segments) segment.index += startIndex;
|
|
781
1134
|
const mediaPart = new MediaPart();
|
|
782
|
-
mediaPart.mediaSegments =
|
|
783
|
-
|
|
784
|
-
} else
|
|
1135
|
+
mediaPart.mediaSegments = streamInfos[_index].playlist.mediaParts[0].mediaSegments;
|
|
1136
|
+
streamInfos[_index].playlist.mediaParts.push(mediaPart);
|
|
1137
|
+
} else streamInfos[_index].playlist.mediaParts.at(-1).mediaSegments.at(-1).duration += streamInfo.playlist.mediaParts[0].mediaSegments.reduce((sum, segment) => sum + segment.duration, 0);
|
|
785
1138
|
else {
|
|
786
|
-
if (
|
|
787
|
-
if (
|
|
788
|
-
|
|
1139
|
+
if (streamInfo.type === "subtitle" && streamInfo.extension === "mp4") streamInfo.extension = "m4s";
|
|
1140
|
+
if (streamInfo.type !== "subtitle" && (streamInfo.extension == null || streamInfo.playlist.mediaParts.reduce((sum, part) => sum + part.mediaSegments.length, 0) > 1)) streamInfo.extension = "m4s";
|
|
1141
|
+
streamInfos.push(streamInfo);
|
|
789
1142
|
}
|
|
790
1143
|
segBaseUrl = representationsBaseUrl;
|
|
791
1144
|
}
|
|
792
1145
|
segBaseUrl = adaptationSetsBaseUrl;
|
|
793
1146
|
}
|
|
794
1147
|
}
|
|
795
|
-
const audioList =
|
|
796
|
-
const subtitleList =
|
|
797
|
-
const videoList =
|
|
1148
|
+
const audioList = streamInfos.filter((stream) => stream.type === "audio");
|
|
1149
|
+
const subtitleList = streamInfos.filter((stream) => stream.type === "subtitle");
|
|
1150
|
+
const videoList = streamInfos.filter((stream) => stream.type === "video");
|
|
798
1151
|
for (const video of videoList) {
|
|
799
|
-
const audioGroupId = audioList.toSorted((a, b$1) => (b$1.
|
|
800
|
-
const subtitleGroupId = subtitleList.toSorted((a, b$1) => (b$1.
|
|
1152
|
+
const audioGroupId = audioList.toSorted((a, b$1) => (b$1.bitrate || 0) - (a.bitrate || 0)).at(0)?.groupId;
|
|
1153
|
+
const subtitleGroupId = subtitleList.toSorted((a, b$1) => (b$1.bitrate || 0) - (a.bitrate || 0)).at(0)?.groupId;
|
|
801
1154
|
if (audioGroupId) video.audioId = audioGroupId;
|
|
802
1155
|
if (subtitleGroupId) video.subtitleId = subtitleGroupId;
|
|
803
1156
|
}
|
|
804
|
-
return
|
|
1157
|
+
return streamInfos;
|
|
805
1158
|
}
|
|
806
1159
|
#filterLanguage(v) {
|
|
807
1160
|
if (!v) return;
|
|
808
1161
|
return v;
|
|
809
1162
|
}
|
|
810
|
-
async refreshPlayList(
|
|
811
|
-
if (!
|
|
1163
|
+
async refreshPlayList(streamInfos) {
|
|
1164
|
+
if (!streamInfos.length) return;
|
|
812
1165
|
const response = await fetch(this.#parserConfig.url, this.#parserConfig.headers).catch(() => fetch(this.#parserConfig.originalUrl, this.#parserConfig.headers));
|
|
813
1166
|
const rawText = await response.text();
|
|
814
1167
|
const url = response.url;
|
|
815
1168
|
this.#parserConfig.url = url;
|
|
816
1169
|
this.#setInitUrl();
|
|
817
1170
|
const newStreams = await this.extractStreams(rawText);
|
|
818
|
-
for (const
|
|
819
|
-
let results = newStreams.filter((n) => n.toShortString() ===
|
|
820
|
-
if (!results.length) results = newStreams.filter((n) => n.playlist?.mediaInit?.url ===
|
|
821
|
-
if (results.length)
|
|
1171
|
+
for (const streamInfo of streamInfos) {
|
|
1172
|
+
let results = newStreams.filter((n) => n.toShortString() === streamInfo.toShortString());
|
|
1173
|
+
if (!results.length) results = newStreams.filter((n) => n.playlist?.mediaInit?.url === streamInfo.playlist?.mediaInit?.url);
|
|
1174
|
+
if (results.length) streamInfo.playlist.mediaParts = results.at(0).playlist.mediaParts;
|
|
822
1175
|
}
|
|
823
|
-
await this.#processUrl(
|
|
1176
|
+
await this.#processUrl(streamInfos);
|
|
824
1177
|
}
|
|
825
|
-
async #processUrl(
|
|
826
|
-
for (const spec of
|
|
1178
|
+
async #processUrl(streamInfos) {
|
|
1179
|
+
for (const spec of streamInfos) {
|
|
827
1180
|
const playlist = spec.playlist;
|
|
828
1181
|
if (!playlist) continue;
|
|
829
1182
|
if (playlist.mediaInit) playlist.mediaInit.url = this.preProcessUrl(playlist.mediaInit.url);
|
|
830
1183
|
for (const part of playlist.mediaParts) for (const segment of part.mediaSegments) segment.url = this.preProcessUrl(segment.url);
|
|
831
1184
|
}
|
|
832
1185
|
}
|
|
833
|
-
async fetchPlayList(
|
|
834
|
-
this.#processUrl(
|
|
1186
|
+
async fetchPlayList(streamInfos) {
|
|
1187
|
+
this.#processUrl(streamInfos);
|
|
835
1188
|
}
|
|
836
1189
|
preProcessUrl(url) {
|
|
837
1190
|
for (const processor of this.#parserConfig.urlProcessors) if (processor.canProcess(this.extractorType, url, this.#parserConfig)) url = processor.process(url, this.#parserConfig);
|
|
@@ -890,62 +1243,75 @@ var HlsExtractor = class {
|
|
|
890
1243
|
}
|
|
891
1244
|
async #parseMasterList() {
|
|
892
1245
|
this.#masterM3u8Flag = true;
|
|
893
|
-
const
|
|
1246
|
+
const streamInfos = [];
|
|
894
1247
|
let expectPlaylist = false;
|
|
895
|
-
let
|
|
1248
|
+
let streamInfo = new VideoStreamInfo();
|
|
896
1249
|
const lines = this.#m3u8Content.split("\n");
|
|
897
1250
|
for (const line of lines) {
|
|
898
1251
|
if (!line.trim()) continue;
|
|
899
1252
|
if (line.startsWith(HLS_TAGS.extXStreamInf)) {
|
|
900
|
-
|
|
901
|
-
|
|
1253
|
+
streamInfo = new VideoStreamInfo();
|
|
1254
|
+
streamInfo.originalUrl = this.parserConfig.originalUrl;
|
|
902
1255
|
const bandwidth = getAttribute(line, "AVERAGE-BANDWIDTH") || getAttribute(line, "BANDWIDTH");
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1256
|
+
streamInfo.bitrate = Number(bandwidth || 0);
|
|
1257
|
+
streamInfo.codecs = getAttribute(line, "CODECS");
|
|
1258
|
+
const resolution = getAttribute(line, "RESOLUTION");
|
|
1259
|
+
const [widthString, heightString] = resolution.split("x");
|
|
1260
|
+
streamInfo.width = parseInt(widthString);
|
|
1261
|
+
streamInfo.height = parseInt(heightString);
|
|
1262
|
+
streamInfo.resolution = resolution;
|
|
906
1263
|
const frameRate = getAttribute(line, "FRAME-RATE");
|
|
907
|
-
if (frameRate)
|
|
1264
|
+
if (frameRate) streamInfo.frameRate = Number(frameRate);
|
|
908
1265
|
const audioId = getAttribute(line, "AUDIO");
|
|
909
|
-
if (audioId)
|
|
1266
|
+
if (audioId) streamInfo.audioId = audioId;
|
|
910
1267
|
const videoId = getAttribute(line, "VIDEO");
|
|
911
|
-
if (videoId)
|
|
1268
|
+
if (videoId) streamInfo.videoId = videoId;
|
|
912
1269
|
const subtitleId = getAttribute(line, "SUBTITLES");
|
|
913
|
-
if (subtitleId)
|
|
1270
|
+
if (subtitleId) streamInfo.subtitleId = subtitleId;
|
|
914
1271
|
const videoRange = getAttribute(line, "VIDEO-RANGE");
|
|
915
|
-
if (videoRange)
|
|
916
|
-
if (
|
|
1272
|
+
if (videoRange) streamInfo.videoRange = videoRange;
|
|
1273
|
+
if (streamInfo.codecs && streamInfo.audioId) streamInfo.codecs = streamInfo.codecs.split(",")[0];
|
|
917
1274
|
expectPlaylist = true;
|
|
918
1275
|
} else if (line.startsWith(HLS_TAGS.extXMedia)) {
|
|
919
|
-
|
|
920
|
-
const
|
|
921
|
-
if (
|
|
922
|
-
if (
|
|
1276
|
+
streamInfo = new VideoStreamInfo();
|
|
1277
|
+
const type = getAttribute(line, "TYPE");
|
|
1278
|
+
if (type === "VIDEO") streamInfo = new VideoStreamInfo();
|
|
1279
|
+
if (type === "AUDIO") streamInfo = new AudioStreamInfo();
|
|
1280
|
+
if (type === "SUBTITLES") streamInfo = new SubtitleStreamInfo();
|
|
1281
|
+
if (type === "CLOSED-CAPTIONS") {
|
|
1282
|
+
streamInfo = new SubtitleStreamInfo();
|
|
1283
|
+
streamInfo.cc = true;
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
923
1286
|
let url = getAttribute(line, "URI");
|
|
924
1287
|
if (!url) continue;
|
|
925
1288
|
url = combineUrl(this.#baseUrl, url);
|
|
926
|
-
|
|
1289
|
+
streamInfo.url = this.preProcessUrl(url);
|
|
927
1290
|
const groupId = getAttribute(line, "GROUP-ID");
|
|
928
|
-
if (groupId)
|
|
1291
|
+
if (groupId) streamInfo.groupId = groupId;
|
|
929
1292
|
const language = getAttribute(line, "LANGUAGE");
|
|
930
|
-
if (language)
|
|
1293
|
+
if (language) streamInfo.languageCode = language;
|
|
931
1294
|
const name = getAttribute(line, "NAME");
|
|
932
|
-
if (name)
|
|
1295
|
+
if (name) streamInfo.name = name;
|
|
933
1296
|
const defaultFlag = getAttribute(line, "DEFAULT");
|
|
934
|
-
if (defaultFlag)
|
|
935
|
-
const
|
|
936
|
-
if (
|
|
1297
|
+
if (defaultFlag) streamInfo.default = defaultFlag.toLowerCase() === "yes";
|
|
1298
|
+
const channelsString = getAttribute(line, "CHANNELS");
|
|
1299
|
+
if (channelsString) {
|
|
1300
|
+
streamInfo.channels = channelsString;
|
|
1301
|
+
if (streamInfo.type === "audio") streamInfo.numberOfChannels = parseFloat(channelsString);
|
|
1302
|
+
}
|
|
937
1303
|
const characteristics = getAttribute(line, "CHARACTERISTICS");
|
|
938
|
-
if (characteristics)
|
|
939
|
-
|
|
1304
|
+
if (characteristics) streamInfo.characteristics = characteristics.split(",").at(-1)?.split(".").at(-1);
|
|
1305
|
+
streamInfos.push(streamInfo);
|
|
940
1306
|
} else if (line.startsWith("#")) continue;
|
|
941
1307
|
else if (expectPlaylist) {
|
|
942
1308
|
const url = combineUrl(this.#baseUrl, line);
|
|
943
|
-
|
|
1309
|
+
streamInfo.url = this.preProcessUrl(url);
|
|
944
1310
|
expectPlaylist = false;
|
|
945
|
-
|
|
1311
|
+
streamInfos.push(streamInfo);
|
|
946
1312
|
}
|
|
947
1313
|
}
|
|
948
|
-
return
|
|
1314
|
+
return streamInfos;
|
|
949
1315
|
}
|
|
950
1316
|
async #parseList() {
|
|
951
1317
|
let hasAd = false;
|
|
@@ -1069,7 +1435,7 @@ var HlsExtractor = class {
|
|
|
1069
1435
|
this.preProcessContent();
|
|
1070
1436
|
if (this.#m3u8Content.includes(HLS_TAGS.extXStreamInf)) return this.#parseMasterList().then((lists) => distinctBy(lists, (list) => list.url));
|
|
1071
1437
|
const playlist = await this.#parseList();
|
|
1072
|
-
const streamSpec = new
|
|
1438
|
+
const streamSpec = new VideoStreamInfo();
|
|
1073
1439
|
streamSpec.url = this.parserConfig.url;
|
|
1074
1440
|
streamSpec.playlist = playlist;
|
|
1075
1441
|
streamSpec.extension = playlist.mediaInit ? "mp4" : "ts";
|
|
@@ -1115,7 +1481,7 @@ var HlsExtractor = class {
|
|
|
1115
1481
|
const newPlaylist = await this.#parseList();
|
|
1116
1482
|
if (list.playlist?.mediaInit) list.playlist.mediaParts = newPlaylist.mediaParts;
|
|
1117
1483
|
else list.playlist = newPlaylist;
|
|
1118
|
-
if (list.
|
|
1484
|
+
if (list.type === "subtitle") {
|
|
1119
1485
|
const a = list.playlist.mediaParts.some((part) => part.mediaSegments.some((segment) => segment.url.includes(".ttml")));
|
|
1120
1486
|
const b$1 = list.playlist.mediaParts.some((part) => part.mediaSegments.some((segment) => segment.url.includes(".vtt") || segment.url.includes(".webvtt")));
|
|
1121
1487
|
if (a) list.extension = "ttml";
|
|
@@ -1123,8 +1489,8 @@ var HlsExtractor = class {
|
|
|
1123
1489
|
} else list.extension = list.playlist.mediaInit ? "m4s" : "ts";
|
|
1124
1490
|
}
|
|
1125
1491
|
}
|
|
1126
|
-
async refreshPlayList(
|
|
1127
|
-
await this.fetchPlayList(
|
|
1492
|
+
async refreshPlayList(streamInfos) {
|
|
1493
|
+
await this.fetchPlayList(streamInfos);
|
|
1128
1494
|
}
|
|
1129
1495
|
};
|
|
1130
1496
|
|
|
@@ -1184,15 +1550,18 @@ var StreamExtractor = class {
|
|
|
1184
1550
|
async extractStreams() {
|
|
1185
1551
|
return this.#extractor.extractStreams(this.#rawText);
|
|
1186
1552
|
}
|
|
1187
|
-
async fetchPlayList(
|
|
1188
|
-
return this.#extractor.fetchPlayList(
|
|
1553
|
+
async fetchPlayList(streamInfos) {
|
|
1554
|
+
return this.#extractor.fetchPlayList(streamInfos);
|
|
1189
1555
|
}
|
|
1190
|
-
async refreshPlayList(
|
|
1191
|
-
return this.#extractor.refreshPlayList(
|
|
1556
|
+
async refreshPlayList(streamInfos) {
|
|
1557
|
+
return this.#extractor.refreshPlayList(streamInfos);
|
|
1192
1558
|
}
|
|
1193
1559
|
};
|
|
1194
1560
|
|
|
1195
1561
|
//#endregion
|
|
1562
|
+
exports.ALL_STREAM_TYPES = ALL_STREAM_TYPES;
|
|
1563
|
+
exports.AUDIO_CODECS = AUDIO_CODECS;
|
|
1564
|
+
exports.AudioStreamInfo = AudioStreamInfo;
|
|
1196
1565
|
exports.DASH_TAGS = DASH_TAGS;
|
|
1197
1566
|
exports.DashExtractor = DashExtractor;
|
|
1198
1567
|
exports.DefaultDashContentProcessor = DefaultDashContentProcessor;
|
|
@@ -1204,12 +1573,17 @@ exports.EXTRACTOR_TYPES = EXTRACTOR_TYPES;
|
|
|
1204
1573
|
exports.EncryptInfo = EncryptInfo;
|
|
1205
1574
|
exports.HLS_TAGS = HLS_TAGS;
|
|
1206
1575
|
exports.HlsExtractor = HlsExtractor;
|
|
1207
|
-
exports.MEDIA_TYPES = MEDIA_TYPES;
|
|
1208
1576
|
exports.MediaPart = MediaPart;
|
|
1209
1577
|
exports.MediaSegment = MediaSegment;
|
|
1210
1578
|
exports.ParserConfig = ParserConfig;
|
|
1579
|
+
exports.Playlist = Playlist;
|
|
1211
1580
|
exports.ROLE_TYPE = ROLE_TYPE;
|
|
1581
|
+
exports.SUBTITLE_CODECS = SUBTITLE_CODECS;
|
|
1212
1582
|
exports.StreamExtractor = StreamExtractor;
|
|
1213
|
-
exports.
|
|
1583
|
+
exports.StreamInfo = StreamInfo;
|
|
1584
|
+
exports.SubtitleStreamInfo = SubtitleStreamInfo;
|
|
1585
|
+
exports.VIDEO_CODECS = VIDEO_CODECS;
|
|
1586
|
+
exports.VIDEO_DYNAMIC_RANGES = VIDEO_DYNAMIC_RANGES;
|
|
1587
|
+
exports.VideoStreamInfo = VideoStreamInfo;
|
|
1214
1588
|
exports.getRange = getRange;
|
|
1215
1589
|
exports.parseRange = parseRange;
|