dasha 2.3.2 → 2.3.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/lib/manifest.js +21 -10
- package/lib/processor.js +11 -4
- package/lib/utils.js +7 -1
- package/package.json +2 -2
- package/types/manifest.d.ts +6 -2
package/lib/manifest.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { parseXml } = require('./xml');
|
|
4
4
|
const { processManifest } = require('./processor');
|
|
5
|
-
const { getQualityLabel, getHeightByWidth, getClosest } = require('./utils');
|
|
5
|
+
const { getQualityLabel, getHeightByWidth, getClosest, sanitizeBaseUrl } = require('./utils');
|
|
6
6
|
const { CONTENT_TYPE, KEY_SYSTEMS } = require('./constants');
|
|
7
7
|
|
|
8
8
|
const isUrl = (value) => {
|
|
@@ -19,8 +19,9 @@ class Manifest {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
addBaseUrl(value) {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const sanitizedValue = sanitizeBaseUrl(value);
|
|
23
|
+
if (Array.isArray(this.baseUrls)) this.baseUrls.push(sanitizedValue);
|
|
24
|
+
else this.baseUrls = [sanitizedValue];
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
getVideoTrack(height) {
|
|
@@ -133,8 +134,8 @@ class Manifest {
|
|
|
133
134
|
const tracks = [];
|
|
134
135
|
for (const representation of selectedRepresentations) {
|
|
135
136
|
const adaptationSet = adaptationSets.find((a) => a.representations.includes(representation));
|
|
136
|
-
const baseUrl = this.baseUrls?.[0]
|
|
137
|
-
const representationBaseUrl = representation.baseUrls?.[0]
|
|
137
|
+
const baseUrl = this.baseUrls?.[0];
|
|
138
|
+
const representationBaseUrl = representation.baseUrls?.[0];
|
|
138
139
|
const url = isUrl(representationBaseUrl)
|
|
139
140
|
? representationBaseUrl
|
|
140
141
|
: `${baseUrl || ''}${representationBaseUrl || ''}`;
|
|
@@ -177,7 +178,7 @@ class Manifest {
|
|
|
177
178
|
const segmentTemplate = representation.segmentTemplate || adaptationSet.segmentTemplate;
|
|
178
179
|
const baseUrls = representation.baseUrls || this.baseUrls;
|
|
179
180
|
const isBaseUrlRequired = !segmentTemplate.media.includes(`https://`);
|
|
180
|
-
const baseUrl = baseUrls?.
|
|
181
|
+
const baseUrl = baseUrls?.[0];
|
|
181
182
|
const initTemplate = segmentTemplate.initialization;
|
|
182
183
|
const initUrl = initTemplate
|
|
183
184
|
?.replace(/\$Bandwidth\$/i, representation.bandwidth)
|
|
@@ -187,12 +188,14 @@ class Manifest {
|
|
|
187
188
|
|
|
188
189
|
const mediaTemplate = segmentTemplate.media.replace(/\$Bandwidth\$/i, representation.bandwidth);
|
|
189
190
|
let time = 0;
|
|
190
|
-
let index = parseInt(segmentTemplate.startNumber) || 0;
|
|
191
|
+
let index = parseInt(segmentTemplate.startNumber || segmentTemplate.start) || 0;
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
let s = segmentTemplate['SegmentTimeline']?.['S'];
|
|
194
|
+
if (!!s && !Array.isArray(segmentTemplate['SegmentTimeline']?.['S']))
|
|
195
|
+
s = [segmentTemplate['SegmentTimeline']?.['S']];
|
|
194
196
|
|
|
195
|
-
for (
|
|
197
|
+
for (let i = 0; i < (s?.length || segmentTemplate.duration); i++) {
|
|
198
|
+
const segment = s?.[i] || {};
|
|
196
199
|
const repeats = parseInt(segment.r || '0') + 1;
|
|
197
200
|
if (segment.t) time = parseInt(segment.t);
|
|
198
201
|
const duration = parseInt(segment.d || segmentTemplate.timescale || '0');
|
|
@@ -215,6 +218,14 @@ class Manifest {
|
|
|
215
218
|
let pssh = null;
|
|
216
219
|
for (const period of this.periods)
|
|
217
220
|
for (const adaptationSet of period.adaptationSets) {
|
|
221
|
+
for (const representation of adaptationSet.representations) {
|
|
222
|
+
if (!representation.contentProtections) continue;
|
|
223
|
+
for (const contentProtection of representation.contentProtections)
|
|
224
|
+
if (contentProtection.cencPssh && contentProtection.schemeIdUri === schemeIdUri) {
|
|
225
|
+
pssh = contentProtection.cencPssh;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
218
229
|
if (!adaptationSet.contentProtections) continue;
|
|
219
230
|
for (const contentProtection of adaptationSet.contentProtections)
|
|
220
231
|
if (contentProtection.cencPssh && contentProtection.schemeIdUri === schemeIdUri) {
|
package/lib/processor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { parseDuration } = require('./utils');
|
|
1
|
+
const { parseDuration, sanitizeBaseUrl } = require('./utils');
|
|
2
2
|
|
|
3
3
|
const processElement = (element, type) => {
|
|
4
4
|
if (!element) return;
|
|
@@ -15,9 +15,13 @@ const processElement = (element, type) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
const processRepresentation = (representation) => {
|
|
18
|
+
const contentProtectionsList = processElement(representation?.['ContentProtection'], 'array');
|
|
19
|
+
const contentProtections = contentProtectionsList?.map((c) => processContentProtection(c));
|
|
18
20
|
return {
|
|
19
21
|
id: representation['id'],
|
|
20
|
-
baseUrls: processElement(representation['BaseURL'], 'array')
|
|
22
|
+
baseUrls: processElement(representation['BaseURL'], 'array')?.map((url) =>
|
|
23
|
+
sanitizeBaseUrl(url.value)
|
|
24
|
+
),
|
|
21
25
|
codecs: representation['codecs'],
|
|
22
26
|
bandwidth: processElement(representation['bandwidth'], 'number'),
|
|
23
27
|
frameRate: representation['frameRate'],
|
|
@@ -28,6 +32,7 @@ const processRepresentation = (representation) => {
|
|
|
28
32
|
sar: representation['sar'],
|
|
29
33
|
startWithSAP: representation['startWithSAP'],
|
|
30
34
|
segmentTemplate: representation['SegmentTemplate'],
|
|
35
|
+
contentProtections,
|
|
31
36
|
};
|
|
32
37
|
};
|
|
33
38
|
|
|
@@ -50,7 +55,9 @@ const processAdaptationSet = (adaptationSet) => {
|
|
|
50
55
|
return {
|
|
51
56
|
id: adaptationSet['id'],
|
|
52
57
|
group: processElement(adaptationSet['group'], 'number'),
|
|
53
|
-
baseUrls: processElement(adaptationSet['BaseURL'], 'array')
|
|
58
|
+
baseUrls: processElement(adaptationSet['BaseURL'], 'array')?.map((url) =>
|
|
59
|
+
sanitizeBaseUrl(url.value)
|
|
60
|
+
),
|
|
54
61
|
segmentAlignment: adaptationSet['segmentAlignment'],
|
|
55
62
|
lang: adaptationSet['lang'],
|
|
56
63
|
maxWidth: processElement(adaptationSet['maxWidth'], 'number'),
|
|
@@ -82,7 +89,7 @@ const processManifest = (parsedXml) => {
|
|
|
82
89
|
if (!periodsList.length) return null;
|
|
83
90
|
return {
|
|
84
91
|
periods: periodsList.map((p) => processPeriod(p)),
|
|
85
|
-
baseUrls: processElement(mpd['BaseURL'], 'array'),
|
|
92
|
+
baseUrls: processElement(mpd['BaseURL'], 'array')?.map((url) => sanitizeBaseUrl(url.value)),
|
|
86
93
|
locations: processElement(mpd['Location'], 'array'),
|
|
87
94
|
profiles: mpd['profiles'],
|
|
88
95
|
mediaPresentationDuration: processElement(mpd['mediaPresentationDuration'], 'duration'),
|
package/lib/utils.js
CHANGED
|
@@ -54,4 +54,10 @@ const getClosest = (value, array) =>
|
|
|
54
54
|
|
|
55
55
|
const getQualityLabel = (videoWidth) => getHeightByWidth(videoWidth) + 'p';
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const sanitizeBaseUrl = (value) => {
|
|
58
|
+
if (!value.includes('.mpd')) return value;
|
|
59
|
+
const manifestFilename = value.split('.mpd')[0].split('/').at(-1) + '.mpd';
|
|
60
|
+
return value.replace(manifestFilename, '');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
module.exports = { parseDuration, getHeightByWidth, getQualityLabel, getClosest, sanitizeBaseUrl };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dasha",
|
|
3
|
-
"version": "2.3.
|
|
4
|
-
"author": "Vitaly Gashkov <vitalygashkov@
|
|
3
|
+
"version": "2.3.4",
|
|
4
|
+
"author": "Vitaly Gashkov <vitalygashkov@vk.com>",
|
|
5
5
|
"description": "MPD-manifest parser for MPEG DASH",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"keywords": [
|
package/types/manifest.d.ts
CHANGED
|
@@ -34,9 +34,13 @@ interface VideoTrack extends MediaTrack {
|
|
|
34
34
|
interface AudioTrack extends MediaTrack {
|
|
35
35
|
type: 'audio';
|
|
36
36
|
audioSampleRate: number;
|
|
37
|
+
language: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
interface SubtitleTrack {
|
|
40
|
+
interface SubtitleTrack extends MediaTrack {
|
|
41
|
+
type: 'text';
|
|
42
|
+
language: string;
|
|
43
|
+
}
|
|
40
44
|
|
|
41
45
|
interface Segment {
|
|
42
46
|
url: string;
|
|
@@ -45,4 +49,4 @@ interface Segment {
|
|
|
45
49
|
|
|
46
50
|
declare const parseManifest: (text: string) => Manifest | null;
|
|
47
51
|
|
|
48
|
-
export { parseManifest, Manifest, VideoTrack, AudioTrack };
|
|
52
|
+
export { parseManifest, Manifest, VideoTrack, AudioTrack, SubtitleTrack };
|