@pixwel/pixwel-sdk 1.1.7 → 1.1.9
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/coverage/clover.xml +469 -3
- package/coverage/coverage-final.json +10 -1
- package/coverage/lcov-report/Ass.js.html +1 -1
- package/coverage/lcov-report/Asset.js.html +25 -25
- package/coverage/lcov-report/Filename.js.html +209 -242
- package/coverage/lcov-report/Ingest.js.html +578 -116
- package/coverage/lcov-report/IngestFile.js.html +1 -1
- package/coverage/lcov-report/IngestFileDrop.js.html +3 -3
- package/coverage/lcov-report/Order.js.html +62 -26
- package/coverage/lcov-report/Srt.js.html +1 -1
- package/coverage/lcov-report/Sub.js.html +1 -1
- package/coverage/lcov-report/TimeFormat.js.html +1 -1
- package/coverage/lcov-report/index.html +128 -11
- package/coverage/lcov.info +967 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +0 -9
- package/dist/src/Filename.js +13 -20
- package/dist/src/Ingest.js +241 -88
- package/dist/src/Order.js +12 -0
- package/index.js +0 -1
- package/package.json +32 -26
- package/src/Filename.js +13 -24
- package/src/Ingest.js +241 -91
- package/src/Order.js +13 -1
- package/test/Filename.spec.js +74 -38
- package/test/Ingest.spec.js +306 -68
- package/test/mocks/mediainfo.mock.js +89 -66
- package/test/mocks/mocks.js +5 -1
- package/test/mocks/prores.mock.js +119 -70
- package/.vscode/launch.json +0 -0
- package/dist/src/IngestFileDrop.js +0 -178
- package/src/IngestFileDrop.js +0 -157
- package/src/docs.json +0 -1
package/src/Filename.js
CHANGED
|
@@ -24,19 +24,19 @@ export default class Filename {
|
|
|
24
24
|
mediaType = ""
|
|
25
25
|
) {
|
|
26
26
|
if (!asset.project) {
|
|
27
|
-
throw "missing asset.project";
|
|
27
|
+
throw new Error("missing asset.project");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
if (asset.project && !asset.project.filePrefix) {
|
|
31
|
-
throw "missing asset.project.filePrefix";
|
|
31
|
+
throw new Error("missing asset.project.filePrefix");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (!asset.name) {
|
|
35
|
-
throw "missing asset.name";
|
|
35
|
+
throw new Error("missing asset.name");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (!asset.type) {
|
|
39
|
-
throw "missing asset.type";
|
|
39
|
+
throw new Error("missing asset.type");
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
let defaults = {
|
|
@@ -61,39 +61,25 @@ export default class Filename {
|
|
|
61
61
|
options = _.merge(defaults, options);
|
|
62
62
|
|
|
63
63
|
if (!options.language) {
|
|
64
|
-
throw "missing options.language";
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
!options.tags.SubDub.sub &&
|
|
69
|
-
!options.tags.SubDub.dub &&
|
|
70
|
-
!options.tags.Text
|
|
71
|
-
) {
|
|
72
|
-
throw "missing options.tags.Text or options.tags.SubDub";
|
|
64
|
+
throw new Error("missing options.language");
|
|
73
65
|
}
|
|
74
66
|
|
|
75
67
|
if (mediaType && mediaType == "video" && !options.tags.Codec) {
|
|
76
|
-
throw "missing options.tags.Codec";
|
|
68
|
+
throw new Error("missing options.tags.Codec");
|
|
77
69
|
}
|
|
78
70
|
|
|
79
71
|
if (mediaType && mediaType == "video" && !options.tags.Resolution) {
|
|
80
|
-
throw "missing options.tags.Resolution";
|
|
72
|
+
throw new Error("missing options.tags.Resolution");
|
|
81
73
|
}
|
|
82
74
|
|
|
83
75
|
if (mediaType && mediaType == "video" && !options.tags.Framerate) {
|
|
84
|
-
throw "missing options.tags.Framerate";
|
|
76
|
+
throw new Error("missing options.tags.Framerate");
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
if (mediaType == "video" && !options.tags["Frame Mode"]) {
|
|
88
|
-
throw "missing options.tags[Frame Mode]";
|
|
80
|
+
throw new Error("missing options.tags[Frame Mode]");
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
// currently, slurpee's filename.ts will render this, but we can add this back in later when
|
|
92
|
-
// filename.ts starts to rely on Filename.render
|
|
93
|
-
// if ((options.tags.SubDub.dub || options.tags.SubDub.sub) && options.tags.Text) {
|
|
94
|
-
// throw 'conflicting options supplied: SubDub and Text';
|
|
95
|
-
// }
|
|
96
|
-
|
|
97
83
|
function assetType(asset, types) {
|
|
98
84
|
if (/trailer/.test(asset.type)) {
|
|
99
85
|
return false;
|
|
@@ -101,7 +87,7 @@ export default class Filename {
|
|
|
101
87
|
|
|
102
88
|
let matchingType = _.find(types, { name: asset.type });
|
|
103
89
|
if (!matchingType) {
|
|
104
|
-
throw `Asset type "${asset.type}" could not be found
|
|
90
|
+
throw new Error(`Asset type "${asset.type}" could not be found`);
|
|
105
91
|
}
|
|
106
92
|
|
|
107
93
|
return (
|
|
@@ -154,6 +140,9 @@ export default class Filename {
|
|
|
154
140
|
}
|
|
155
141
|
|
|
156
142
|
function locale(country, language) {
|
|
143
|
+
if (language.includes('-')) {
|
|
144
|
+
country = '';
|
|
145
|
+
}
|
|
157
146
|
return [language, country]
|
|
158
147
|
.filter(str => {
|
|
159
148
|
return !!str && str !== "WW";
|
package/src/Ingest.js
CHANGED
|
@@ -6,25 +6,26 @@ import _ from "lodash";
|
|
|
6
6
|
*/
|
|
7
7
|
export default class Ingest {
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @param
|
|
11
|
-
* @
|
|
9
|
+
* Maps mediainfo metadata into ingest Tag format
|
|
10
|
+
* @param {*} info
|
|
11
|
+
* @param {*} mediaType
|
|
12
12
|
*/
|
|
13
|
-
static
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Usage: "",
|
|
13
|
+
static mapFromMediainfo(info, mediaType) {
|
|
14
|
+
let schema = {
|
|
15
|
+
Aspect: null,
|
|
16
|
+
Standard: null,
|
|
17
|
+
Resolution: null,
|
|
18
|
+
Codec: null,
|
|
19
|
+
"Frame Mode": null,
|
|
20
|
+
Text: null,
|
|
21
|
+
Usage: null,
|
|
23
22
|
Extra: [],
|
|
24
|
-
Framerate:
|
|
25
|
-
Duration:
|
|
23
|
+
Framerate: null,
|
|
24
|
+
Duration: null
|
|
26
25
|
};
|
|
27
26
|
|
|
27
|
+
let tags = {};
|
|
28
|
+
|
|
28
29
|
if (!info.File) {
|
|
29
30
|
throw "missing info.File";
|
|
30
31
|
}
|
|
@@ -34,34 +35,34 @@ export default class Ingest {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
let general;
|
|
37
|
-
if (!Array.isArray(info[
|
|
38
|
-
general = info[
|
|
38
|
+
if (!Array.isArray(info["File"]["track"])) {
|
|
39
|
+
general = info["File"]["track"];
|
|
39
40
|
} else {
|
|
40
|
-
general = _.find(info[
|
|
41
|
+
general = _.find(info["File"]["track"], { _type: "General" });
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
switch (mediaType) {
|
|
44
|
-
case
|
|
45
|
-
mediaType =
|
|
45
|
+
case "video":
|
|
46
|
+
mediaType = "Video";
|
|
46
47
|
break;
|
|
47
|
-
case
|
|
48
|
-
mediaType =
|
|
48
|
+
case "image":
|
|
49
|
+
mediaType = "Image";
|
|
49
50
|
break;
|
|
50
51
|
default:
|
|
51
|
-
if (general.Format && general.Format ==
|
|
52
|
-
tags.Usage =
|
|
52
|
+
if (general.Format && general.Format == "MXF") {
|
|
53
|
+
tags.Usage = "broadcast";
|
|
53
54
|
}
|
|
54
55
|
return tags;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
let mediainfoDefaults = {
|
|
58
|
-
|
|
59
|
+
DisplayAspectRatio: "",
|
|
59
60
|
Format: "",
|
|
60
61
|
Standard: "",
|
|
61
62
|
Height: "",
|
|
62
63
|
Width: "",
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
FrameRate: "",
|
|
65
|
+
ScanType: ""
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
let media = _.find(info["File"]["track"], {
|
|
@@ -71,21 +72,41 @@ export default class Ingest {
|
|
|
71
72
|
|
|
72
73
|
let width = parseInt(media["Width"].replace(" ", "").match(/[0-9 ]+/));
|
|
73
74
|
let height = parseInt(media["Height"].replace(" ", "").match(/[0-9 ]+/));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
let Resolution = width && height ? `${width}x${height}` : null;
|
|
76
|
+
if (Resolution) {
|
|
77
|
+
tags.Resolution = Resolution;
|
|
78
|
+
}
|
|
77
79
|
|
|
78
|
-
let
|
|
79
|
-
|
|
80
|
+
let Standard = media["Standard"] ? media["Standard"].toLowerCase() : null;
|
|
81
|
+
if (Standard) {
|
|
82
|
+
tags.Standard = Standard;
|
|
83
|
+
}
|
|
80
84
|
|
|
81
|
-
let framerate = media["
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
let framerate = media["FrameRate"];
|
|
86
|
+
switch (framerate) {
|
|
87
|
+
case "23.976":
|
|
88
|
+
framerate = "23.98";
|
|
89
|
+
break;
|
|
90
|
+
case "24.000":
|
|
91
|
+
framerate = "24";
|
|
92
|
+
break;
|
|
93
|
+
case "25.000":
|
|
94
|
+
framerate = "25";
|
|
95
|
+
break;
|
|
96
|
+
case "29.970":
|
|
97
|
+
framerate = "29.97";
|
|
98
|
+
break;
|
|
99
|
+
case "50.000":
|
|
100
|
+
framerate = "50";
|
|
101
|
+
break;
|
|
102
|
+
case "59.940":
|
|
103
|
+
framerate = "59.94";
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
framerate = parseFloat(framerate).toString();
|
|
107
|
+
framerate = framerate != "NaN" ? framerate : null;
|
|
108
|
+
if (framerate) {
|
|
109
|
+
tags.Framerate = framerate;
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
if (!tags.Standard) {
|
|
@@ -103,16 +124,18 @@ export default class Ingest {
|
|
|
103
124
|
}
|
|
104
125
|
}
|
|
105
126
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
let Codec;
|
|
128
|
+
switch (media["Format"]) {
|
|
129
|
+
case "AVC":
|
|
130
|
+
Codec = "h264";
|
|
131
|
+
break;
|
|
132
|
+
case "ProRes":
|
|
133
|
+
Codec = "prores";
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (Codec) {
|
|
137
|
+
tags.Codec = Codec;
|
|
138
|
+
}
|
|
116
139
|
|
|
117
140
|
if (tags.Codec == "h264") {
|
|
118
141
|
tags.Usage = "online";
|
|
@@ -122,68 +145,195 @@ export default class Ingest {
|
|
|
122
145
|
tags.Usage = "broadcast";
|
|
123
146
|
}
|
|
124
147
|
|
|
125
|
-
if (general && general[
|
|
126
|
-
tags.Usage =
|
|
148
|
+
if (general && general["Format"] == "MXF") {
|
|
149
|
+
tags.Usage = "broadcast";
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
switch (media["DisplayAspectRatio"]) {
|
|
153
|
+
case "1.000":
|
|
154
|
+
tags.Aspect = "1:1";
|
|
155
|
+
break;
|
|
156
|
+
case "1.347":
|
|
157
|
+
tags.Aspect = "4:3";
|
|
158
|
+
break;
|
|
159
|
+
case "1.756":
|
|
160
|
+
tags.Aspect = "16:9";
|
|
161
|
+
break;
|
|
162
|
+
case "1.778":
|
|
163
|
+
tags.Aspect = "16:9";
|
|
164
|
+
break;
|
|
165
|
+
case "1.366":
|
|
166
|
+
tags.Aspect = "4:3";
|
|
167
|
+
break;
|
|
168
|
+
case "0.562":
|
|
169
|
+
tags.Aspect = "9:16";
|
|
170
|
+
break;
|
|
143
171
|
}
|
|
144
172
|
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
})();
|
|
173
|
+
let ScanType = media["ScanType"].toLowerCase() || null;
|
|
174
|
+
if (ScanType) {
|
|
175
|
+
tags["Frame Mode"] = ScanType;
|
|
176
|
+
}
|
|
150
177
|
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
let ingest = {
|
|
179
|
+
selected: {
|
|
180
|
+
download: {
|
|
181
|
+
tags: tags
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
156
185
|
|
|
157
|
-
|
|
186
|
+
return ingest;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Maps parsed filename data to a partial ingest record for later merging
|
|
190
|
+
* @param {*} filename
|
|
191
|
+
* @param {*} allTags
|
|
192
|
+
*/
|
|
193
|
+
static mapFromFilename(filename, allTags) {
|
|
194
|
+
let tags = {
|
|
195
|
+
SubDub: {
|
|
196
|
+
sub: null,
|
|
197
|
+
dub: null
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
filename.file.tags.forEach(tag => {
|
|
201
|
+
let foundTag = _.find(allTags, { name: tag });
|
|
202
|
+
if (foundTag) {
|
|
203
|
+
if (foundTag.type == 'SubDub') {
|
|
204
|
+
if (foundTag.name == 'sub') {
|
|
205
|
+
tags.SubDub.sub = true;
|
|
206
|
+
}
|
|
207
|
+
if (foundTag.name == 'dub') {
|
|
208
|
+
tags.SubDub.dub = true;
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
tags[foundTag.type] = foundTag.name;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
tags.Extra = filename.extra;
|
|
216
|
+
let ingest = {
|
|
217
|
+
selected: {
|
|
218
|
+
download: {
|
|
219
|
+
tags: tags
|
|
220
|
+
},
|
|
221
|
+
preview: {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
if (filename.file.tags.includes('sub') && filename.file.tags.includes('dub')) {
|
|
226
|
+
ingest.selected.preview.type = 'both';
|
|
227
|
+
} else if (filename.file.tags.includes('sub')) {
|
|
228
|
+
ingest.selected.preview.type = 'sub';
|
|
229
|
+
} else if (filename.file.tags.includes('dub')) {
|
|
230
|
+
ingest.selected.preview.type = 'dub';
|
|
231
|
+
}
|
|
232
|
+
if (filename.file.language) {
|
|
233
|
+
ingest.selected.preview.language = filename.file.language;
|
|
234
|
+
ingest.selected.download.language = filename.file.language;
|
|
235
|
+
}
|
|
236
|
+
if (filename.file.country) {
|
|
237
|
+
ingest.selected.download.country = filename.file.country;
|
|
238
|
+
}
|
|
239
|
+
return ingest;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Assembles Ingest based on weighted rules
|
|
243
|
+
* @param {*} context
|
|
244
|
+
* @param {*} filename
|
|
245
|
+
* @param {*} mediainfo
|
|
246
|
+
* @param {*} order
|
|
247
|
+
*/
|
|
248
|
+
static assemble(original, filename, mediainfo, order, watermarks = []) {
|
|
158
249
|
|
|
159
|
-
|
|
250
|
+
let defaults = {
|
|
160
251
|
workRequest: order._id,
|
|
161
252
|
selected: {
|
|
162
253
|
download: {
|
|
163
254
|
enabled: true,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
255
|
+
tags: {
|
|
256
|
+
Extra: []
|
|
257
|
+
},
|
|
167
258
|
embargo: {
|
|
168
259
|
enabled: false
|
|
169
260
|
}
|
|
170
261
|
},
|
|
171
262
|
preview: {
|
|
172
|
-
enabled:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
type: previewType,
|
|
176
|
-
watermark: watermark,
|
|
263
|
+
enabled: false,
|
|
264
|
+
revision: 'Final',
|
|
265
|
+
type: 'none',
|
|
177
266
|
embargo: {
|
|
178
267
|
enabled: false
|
|
179
268
|
}
|
|
180
269
|
},
|
|
181
270
|
thumbnail: {
|
|
182
|
-
enabled: false
|
|
271
|
+
enabled: false,
|
|
272
|
+
position: 50
|
|
183
273
|
}
|
|
184
|
-
}
|
|
185
|
-
context: context
|
|
274
|
+
}
|
|
186
275
|
};
|
|
276
|
+
|
|
277
|
+
let ingest = _.merge(defaults, original, filename, mediainfo);
|
|
278
|
+
|
|
279
|
+
if (original.asset && original.asset.creative) {
|
|
280
|
+
ingest.selected.preview.revision = 'Unfinished';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!original.context.asset && filename.asset && filename.asset._id) {
|
|
284
|
+
ingest.selected.asset = filename.asset._id;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (ingest.workRequest) {
|
|
288
|
+
ingest.selected.project = ingest.context.project;
|
|
289
|
+
ingest.selected.asset = ingest.context.asset;
|
|
290
|
+
ingest.selected.assetType = ingest.context.assetType;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (ingest.workRequest) {
|
|
294
|
+
ingest.selected.download.tags.Extra = [];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (original.context.tag) {
|
|
298
|
+
ingest.selected.download.tags.Extra = [original.context.tag];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (order.territory) {
|
|
302
|
+
ingest.selected.download.country = order.territory;
|
|
303
|
+
}
|
|
304
|
+
if (order.language) {
|
|
305
|
+
ingest.selected.preview.language = order.language;
|
|
306
|
+
ingest.selected.download.language = order.language;
|
|
307
|
+
}
|
|
308
|
+
if (order._id) {
|
|
309
|
+
ingest.selected.download.tags.Text = 'txtd';
|
|
310
|
+
}
|
|
311
|
+
if (order.isSub() || order.isDub()) {
|
|
312
|
+
ingest.selected.download.tags.SubDub = {
|
|
313
|
+
sub: order.isSub(),
|
|
314
|
+
dub: order.isDub()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (order.isSub()) {
|
|
318
|
+
ingest.selected.preview.type = "sub";
|
|
319
|
+
}
|
|
320
|
+
if (order.isDub()) {
|
|
321
|
+
ingest.selected.preview.type = "dub";
|
|
322
|
+
}
|
|
323
|
+
if (order.isSub() && order.isDub()) {
|
|
324
|
+
ingest.selected.preview.type = "both";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
ingest.selected.preview.watermark = watermarks[0] ? watermarks[0]._id : "";
|
|
328
|
+
|
|
329
|
+
let tags = ingest.selected.download.tags;
|
|
330
|
+
ingest.selected.preview.enabled =
|
|
331
|
+
!!(tags.Codec == "prores" &&
|
|
332
|
+
tags.Resolution.split("x")[1] == "1080" &&
|
|
333
|
+
(tags.Framerate == "23.98" || tags.Framerate == "25") &&
|
|
334
|
+
watermarks.length) ||
|
|
335
|
+
false;
|
|
336
|
+
|
|
337
|
+
return ingest;
|
|
187
338
|
}
|
|
188
339
|
}
|
|
189
|
-
|
package/src/Order.js
CHANGED
|
@@ -14,18 +14,30 @@ export default class Order {
|
|
|
14
14
|
};
|
|
15
15
|
Object.assign(this, schema, data);
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Determines if an order should be classified as 'dubbed' based on its dng values
|
|
19
|
+
*/
|
|
17
20
|
isDub() {
|
|
18
21
|
if (!this.dng) {
|
|
19
22
|
return null;
|
|
20
23
|
}
|
|
24
|
+
if (this.dng.graphics && this.dng.graphics.includes('Dedicated / Localized')) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
21
27
|
return _
|
|
22
28
|
.flattenDeep(Object.entries(this.dng))
|
|
23
29
|
.includes("Dedicated / Localized");
|
|
24
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Determines in an order should be classified as 'subbed' based on its dng values
|
|
33
|
+
*/
|
|
25
34
|
isSub() {
|
|
26
35
|
if (!this.dng) {
|
|
27
36
|
return null;
|
|
28
37
|
}
|
|
29
|
-
|
|
38
|
+
if (this.dng === 'Graphics Only') return false;
|
|
39
|
+
return _
|
|
40
|
+
.flattenDeep(Object.entries(this.dng))
|
|
41
|
+
.includes("Subtitled");
|
|
30
42
|
}
|
|
31
43
|
}
|