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 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
- if (Array.isArray(this.baseUrls)) this.baseUrls.push({ value });
23
- else this.baseUrls = [{ value }];
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]?.value;
137
- const representationBaseUrl = representation.baseUrls?.[0]?.value;
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?.map((url) => url.value)?.[0];
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
- if (!Array.isArray(segmentTemplate['SegmentTimeline']['S']))
193
- segmentTemplate['SegmentTimeline']['S'] = [segmentTemplate['SegmentTimeline']['S']];
193
+ let s = segmentTemplate['SegmentTimeline']?.['S'];
194
+ if (!!s && !Array.isArray(segmentTemplate['SegmentTimeline']?.['S']))
195
+ s = [segmentTemplate['SegmentTimeline']?.['S']];
194
196
 
195
- for (const segment of segmentTemplate['SegmentTimeline']['S']) {
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
- module.exports = { parseDuration, getHeightByWidth, getQualityLabel, getClosest };
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.2",
4
- "author": "Vitaly Gashkov <vitalygashkov@bk.ru>",
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": [
@@ -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 };