@amazon/vinyl 2.0.0 → 3.0.0
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/index.cjs +603 -317
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +622 -329
- package/dist/index.js.map +4 -4
- package/dist/test/benchmark.es5.js +8 -5
- package/dist/test/benchmark.js +228 -0
- package/dist/test/benchmark.js.map +4 -4
- package/dist/test/integ.es5.js +182 -156
- package/dist/test/integ.js +1853 -796
- package/dist/test/integ.js.map +4 -4
- package/dist/types/current/index.d.ts +4 -0
- package/dist/types/current/index.d.ts.map +1 -1
- package/dist/types/current/index.js +4 -0
- package/dist/types/current/streaming/AudioOptions.d.ts +39 -0
- package/dist/types/current/streaming/AudioOptions.d.ts.map +1 -0
- package/dist/types/current/streaming/AudioOptions.js +15 -0
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.d.ts +2 -2
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.d.ts.map +1 -1
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.js +5 -5
- package/dist/types/current/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/current/text/HtmlTextTrackRenderer.d.ts.map +1 -0
- package/dist/types/current/text/HtmlTextTrackRenderer.js +231 -0
- package/dist/types/current/text/SidecarTextTrackController.d.ts +65 -53
- package/dist/types/current/text/SidecarTextTrackController.d.ts.map +1 -1
- package/dist/types/current/text/SidecarTextTrackController.js +167 -154
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts +5 -0
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts.map +1 -1
- package/dist/types/current/text/SidecarTextTrackLoader.js +13 -7
- package/dist/types/current/text/TextTrack.d.ts +74 -20
- package/dist/types/current/text/TextTrack.d.ts.map +1 -1
- package/dist/types/current/text/TextTrack.js +20 -0
- package/dist/types/current/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/current/text/TextTrackRenderer.d.ts.map +1 -0
- package/dist/types/current/text/TextTrackRenderer.js +5 -0
- package/dist/types/current/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/current/text/mediaTextTrackProvider.d.ts.map +1 -0
- package/dist/types/current/text/mediaTextTrackProvider.js +42 -0
- package/dist/types/current/text/parseWebVtt.d.ts +16 -2
- package/dist/types/current/text/parseWebVtt.d.ts.map +1 -1
- package/dist/types/current/text/parseWebVtt.js +176 -48
- package/dist/types/current/text/pickPreferredTextTrack.d.ts +2 -1
- package/dist/types/current/text/pickPreferredTextTrack.d.ts.map +1 -1
- package/dist/types/current/text/pickPreferredTextTrack.js +6 -3
- package/dist/types/current/track/Track.d.ts +1 -1
- package/dist/types/current/track/dash/createDashFactories.d.ts +21 -10
- package/dist/types/current/track/dash/createDashFactories.d.ts.map +1 -1
- package/dist/types/current/track/dash/createDashFactories.js +9 -21
- package/dist/types/current/track/hls/createHlsFactories.d.ts +21 -10
- package/dist/types/current/track/hls/createHlsFactories.d.ts.map +1 -1
- package/dist/types/current/track/hls/createHlsFactories.js +9 -21
- package/dist/types/current/track/mse/MseTrack.d.ts +1 -2
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +3 -7
- package/dist/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/types/current/vinyl/VinylOptions.d.ts +18 -40
- package/dist/types/current/vinyl/VinylOptions.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylOptions.js +12 -11
- package/dist/types/current/vinyl/VinylPlayer.d.ts +0 -6
- package/dist/types/current/vinyl/VinylPlayer.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylPlayer.js +2 -23
- package/dist/types/current/vinyl/createVinylFactories.d.ts +32 -18
- package/dist/types/current/vinyl/createVinylFactories.d.ts.map +1 -1
- package/dist/types/current/vinyl/createVinylFactories.js +6 -0
- package/dist/types/ts3.7/index.d.ts +4 -0
- package/dist/types/ts3.7/streaming/AudioOptions.d.ts +39 -0
- package/dist/types/ts3.7/streaming/createDefaultMediaTimelineTransformer.d.ts +2 -2
- package/dist/types/ts3.7/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts3.7/text/SidecarTextTrackController.d.ts +65 -53
- package/dist/types/ts3.7/text/SidecarTextTrackLoader.d.ts +5 -0
- package/dist/types/ts3.7/text/TextTrack.d.ts +74 -20
- package/dist/types/ts3.7/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts3.7/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts3.7/text/parseWebVtt.d.ts +16 -2
- package/dist/types/ts3.7/text/pickPreferredTextTrack.d.ts +2 -1
- package/dist/types/ts3.7/track/Track.d.ts +1 -1
- package/dist/types/ts3.7/track/dash/createDashFactories.d.ts +21 -10
- package/dist/types/ts3.7/track/hls/createHlsFactories.d.ts +21 -10
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +1 -2
- package/dist/types/ts3.7/vinyl/VinylOptions.d.ts +18 -40
- package/dist/types/ts3.7/vinyl/VinylPlayer.d.ts +0 -6
- package/dist/types/ts3.7/vinyl/createVinylFactories.d.ts +32 -18
- package/dist/types/ts4.1/index.d.ts +4 -0
- package/dist/types/ts4.1/streaming/AudioOptions.d.ts +39 -0
- package/dist/types/ts4.1/streaming/createDefaultMediaTimelineTransformer.d.ts +2 -2
- package/dist/types/ts4.1/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts4.1/text/SidecarTextTrackController.d.ts +65 -53
- package/dist/types/ts4.1/text/SidecarTextTrackLoader.d.ts +5 -0
- package/dist/types/ts4.1/text/TextTrack.d.ts +74 -20
- package/dist/types/ts4.1/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts4.1/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts4.1/text/parseWebVtt.d.ts +16 -2
- package/dist/types/ts4.1/text/pickPreferredTextTrack.d.ts +2 -1
- package/dist/types/ts4.1/track/Track.d.ts +1 -1
- package/dist/types/ts4.1/track/dash/createDashFactories.d.ts +21 -10
- package/dist/types/ts4.1/track/hls/createHlsFactories.d.ts +21 -10
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +1 -2
- package/dist/types/ts4.1/vinyl/VinylOptions.d.ts +18 -40
- package/dist/types/ts4.1/vinyl/VinylPlayer.d.ts +0 -6
- package/dist/types/ts4.1/vinyl/createVinylFactories.d.ts +32 -18
- package/dist/types/ts4.7/index.d.ts +4 -0
- package/dist/types/ts4.7/streaming/AudioOptions.d.ts +39 -0
- package/dist/types/ts4.7/streaming/createDefaultMediaTimelineTransformer.d.ts +2 -2
- package/dist/types/ts4.7/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts4.7/text/SidecarTextTrackController.d.ts +65 -53
- package/dist/types/ts4.7/text/SidecarTextTrackLoader.d.ts +5 -0
- package/dist/types/ts4.7/text/TextTrack.d.ts +74 -20
- package/dist/types/ts4.7/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts4.7/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts4.7/text/parseWebVtt.d.ts +16 -2
- package/dist/types/ts4.7/text/pickPreferredTextTrack.d.ts +2 -1
- package/dist/types/ts4.7/track/Track.d.ts +1 -1
- package/dist/types/ts4.7/track/dash/createDashFactories.d.ts +21 -10
- package/dist/types/ts4.7/track/hls/createHlsFactories.d.ts +21 -10
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +1 -2
- package/dist/types/ts4.7/vinyl/VinylOptions.d.ts +18 -40
- package/dist/types/ts4.7/vinyl/VinylPlayer.d.ts +0 -6
- package/dist/types/ts4.7/vinyl/createVinylFactories.d.ts +32 -18
- package/dist/unit/types/current/index.d.ts +2 -0
- package/dist/unit/types/current/index.d.ts.map +1 -1
- package/dist/unit/types/current/index.js +2 -0
- package/dist/unit/types/current/streaming/MseTrack.test.js +13 -7
- package/dist/unit/types/current/streaming/createDefaultMediaTimelineTransformer.test.js +8 -4
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.d.ts +2 -0
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.js +231 -0
- package/dist/unit/types/current/text/SidecarTextTrackController.test.js +432 -333
- package/dist/unit/types/current/text/SidecarTextTrackLoader.test.js +29 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.d.ts +2 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.js +86 -0
- package/dist/unit/types/current/text/parseWebVtt.test.js +187 -6
- package/dist/unit/types/current/text/pickPreferredTextTrack.test.js +12 -1
- package/dist/unit/types/current/track/dash/createDashFactories.test.js +1 -2
- package/dist/unit/types/current/track/hls/buildHlsMediaTimeline.test.js +1 -2
- package/dist/unit/types/current/track/hls/createHlsFactories.test.js +1 -2
- package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/unit/types/current/vinyl/VinylPlayerTextTracks.test.js +102 -177
- package/dist/unit/types/current/vinyl/createVinylFactories.test.js +2 -0
- package/dist/vinylTestUtil/index.js +14 -8
- package/dist/vinylTestUtil/index.js.map +2 -2
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.d.ts +3 -2
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.js +5 -2
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.d.ts +3 -3
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.js +3 -3
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.js +3 -2
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.js +3 -2
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/player/MockVinylPlayerDependencies.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts3.7/mock/text/MockTextTrackController.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts3.7/mock/track/dash/createMockDashDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/ts3.7/mock/track/hls/createMockHlsDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/ts4.1/mock/player/MockVinylPlayerDependencies.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts4.1/mock/text/MockTextTrackController.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts4.1/mock/track/dash/createMockDashDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/ts4.1/mock/track/hls/createMockHlsDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/ts4.7/mock/player/MockVinylPlayerDependencies.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts4.7/mock/text/MockTextTrackController.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts4.7/mock/track/dash/createMockDashDependencies.d.ts +1 -2
- package/dist/vinylTestUtil/types/ts4.7/mock/track/hls/createMockHlsDependencies.d.ts +1 -2
- package/docs/USAGE.md +82 -31
- package/package.json +12 -12
package/dist/test/integ.js
CHANGED
|
@@ -15182,7 +15182,7 @@ ${this.lastInitStack}` : "";
|
|
|
15182
15182
|
// src/global/environment.ts
|
|
15183
15183
|
var vinylVersion = parseVersion(
|
|
15184
15184
|
/* istanbul ignore next */
|
|
15185
|
-
"
|
|
15185
|
+
"3.0.0"
|
|
15186
15186
|
);
|
|
15187
15187
|
|
|
15188
15188
|
// src/global/globalRef.ts
|
|
@@ -16425,6 +16425,18 @@ ${this.lastInitStack}` : "";
|
|
|
16425
16425
|
}
|
|
16426
16426
|
};
|
|
16427
16427
|
|
|
16428
|
+
// src/streaming/AudioOptions.ts
|
|
16429
|
+
var defaultAudioOptions = {
|
|
16430
|
+
selection: { language: null, descriptive: false }
|
|
16431
|
+
};
|
|
16432
|
+
var audioSelectionValidator = object({
|
|
16433
|
+
language: string().or(array(string()).readonly()).orNull().optional(),
|
|
16434
|
+
descriptive: boolean().optional()
|
|
16435
|
+
});
|
|
16436
|
+
var audioOptionsValidator = object({
|
|
16437
|
+
selection: audioSelectionValidator.optional()
|
|
16438
|
+
});
|
|
16439
|
+
|
|
16428
16440
|
// src/streaming/SegmentController.ts
|
|
16429
16441
|
var SEGMENT_START_AFFORDANCE = 0.2;
|
|
16430
16442
|
|
|
@@ -18219,7 +18231,7 @@ ${this.lastInitStack}` : "";
|
|
|
18219
18231
|
return canPlayMimeType(deps, quality);
|
|
18220
18232
|
}
|
|
18221
18233
|
async function transformTimeline(timeline) {
|
|
18222
|
-
var _a3;
|
|
18234
|
+
var _a3, _b;
|
|
18223
18235
|
const codecOverrides = deps.options.value.codecOverrides;
|
|
18224
18236
|
let t = filterTimelineQualities(
|
|
18225
18237
|
(quality) => canPlayWithOverrides(quality, codecOverrides),
|
|
@@ -18236,18 +18248,14 @@ ${this.lastInitStack}` : "";
|
|
|
18236
18248
|
throwSamplingRatesUnsupported,
|
|
18237
18249
|
t
|
|
18238
18250
|
);
|
|
18251
|
+
const selection = deps.options.value.audio.selection;
|
|
18239
18252
|
t = filterTimelineQualities(
|
|
18240
|
-
createAudioDescriptionFilter(
|
|
18241
|
-
(_a3 = deps.options.value.preferDescriptiveAudio) != null ? _a3 : false
|
|
18242
|
-
),
|
|
18253
|
+
createAudioDescriptionFilter((_a3 = selection == null ? void 0 : selection.descriptive) != null ? _a3 : false),
|
|
18243
18254
|
throwNoPlayableAudio,
|
|
18244
18255
|
t
|
|
18245
18256
|
);
|
|
18246
18257
|
t = filterTimelineQualities(
|
|
18247
|
-
createLanguageFilter(
|
|
18248
|
-
deps.options.value.preferredAudioLanguage,
|
|
18249
|
-
"audio"
|
|
18250
|
-
),
|
|
18258
|
+
createLanguageFilter((_b = selection == null ? void 0 : selection.language) != null ? _b : null, "audio"),
|
|
18251
18259
|
throwLanguagesUnsupported,
|
|
18252
18260
|
t
|
|
18253
18261
|
);
|
|
@@ -18255,8 +18263,7 @@ ${this.lastInitStack}` : "";
|
|
|
18255
18263
|
}
|
|
18256
18264
|
return combineData({
|
|
18257
18265
|
timeline: deps.mediaTimeline,
|
|
18258
|
-
|
|
18259
|
-
preferDescriptiveAudio: deps.options.pick("preferDescriptiveAudio"),
|
|
18266
|
+
audio: deps.options.pick("audio"),
|
|
18260
18267
|
codecOverrides: deps.options.pick("codecOverrides")
|
|
18261
18268
|
}).map(async ({ timeline }) => {
|
|
18262
18269
|
return transformTimeline(await timeline);
|
|
@@ -19203,9 +19210,201 @@ ${this.lastInitStack}` : "";
|
|
|
19203
19210
|
return out;
|
|
19204
19211
|
}
|
|
19205
19212
|
|
|
19213
|
+
// src/text/HtmlTextTrackRenderer.ts
|
|
19214
|
+
var ROW_EM = 1.5;
|
|
19215
|
+
function computeCueLayout(cue) {
|
|
19216
|
+
const style = {};
|
|
19217
|
+
style.textAlign = cue.align || "center";
|
|
19218
|
+
if (cue.vertical === "rl") style.writingMode = "vertical-rl";
|
|
19219
|
+
else if (cue.vertical === "lr") style.writingMode = "vertical-lr";
|
|
19220
|
+
const size = typeof cue.size === "number" ? cue.size : 100;
|
|
19221
|
+
style.maxWidth = `${size}%`;
|
|
19222
|
+
const line = typeof cue.line === "number" ? cue.line : null;
|
|
19223
|
+
const position = typeof cue.position === "number" ? cue.position : null;
|
|
19224
|
+
if (line == null && position == null) return style;
|
|
19225
|
+
style.position = "absolute";
|
|
19226
|
+
const transforms = [];
|
|
19227
|
+
if (position == null) {
|
|
19228
|
+
style.left = "50%";
|
|
19229
|
+
transforms.push("translateX(-50%)");
|
|
19230
|
+
} else {
|
|
19231
|
+
if (cue.positionAlign === "line-right") {
|
|
19232
|
+
style.right = `${100 - position}%`;
|
|
19233
|
+
} else if (cue.positionAlign === "line-left") {
|
|
19234
|
+
style.left = `${position}%`;
|
|
19235
|
+
} else {
|
|
19236
|
+
style.left = `${position}%`;
|
|
19237
|
+
transforms.push("translateX(-50%)");
|
|
19238
|
+
}
|
|
19239
|
+
}
|
|
19240
|
+
if (line == null) {
|
|
19241
|
+
style.bottom = "0";
|
|
19242
|
+
} else {
|
|
19243
|
+
if (cue.snapToLines === false) {
|
|
19244
|
+
if (cue.lineAlign === "end") {
|
|
19245
|
+
style.bottom = `${100 - line}%`;
|
|
19246
|
+
} else if (cue.lineAlign === "center") {
|
|
19247
|
+
style.top = `${line}%`;
|
|
19248
|
+
transforms.push("translateY(-50%)");
|
|
19249
|
+
} else {
|
|
19250
|
+
style.top = `${line}%`;
|
|
19251
|
+
}
|
|
19252
|
+
} else if (line < 0) {
|
|
19253
|
+
style.bottom = `${(-line - 1) * ROW_EM}em`;
|
|
19254
|
+
} else {
|
|
19255
|
+
style.top = `${line * ROW_EM}em`;
|
|
19256
|
+
}
|
|
19257
|
+
}
|
|
19258
|
+
if (transforms.length > 0) style.transform = transforms.join(" ");
|
|
19259
|
+
return style;
|
|
19260
|
+
}
|
|
19261
|
+
var HTML_ESCAPE = {
|
|
19262
|
+
"&": "&",
|
|
19263
|
+
"<": "<",
|
|
19264
|
+
">": ">",
|
|
19265
|
+
'"': """
|
|
19266
|
+
};
|
|
19267
|
+
function escapeHtml(text) {
|
|
19268
|
+
return text.replace(/["&<>]/g, (ch) => HTML_ESCAPE[ch]);
|
|
19269
|
+
}
|
|
19270
|
+
var PASSTHROUGH_TAGS = /* @__PURE__ */ new Set(["i", "b", "u", "ruby", "rt"]);
|
|
19271
|
+
var SPAN_TAGS = /* @__PURE__ */ new Set(["c", "v", "lang"]);
|
|
19272
|
+
function openTag(inner) {
|
|
19273
|
+
if (/^\d/.test(inner)) return "";
|
|
19274
|
+
const head = inner.split(/\s/, 1)[0];
|
|
19275
|
+
const [name, ...classes] = head.split(".");
|
|
19276
|
+
const annotation = inner.slice(head.length).trim();
|
|
19277
|
+
const classAttr = classes.filter(Boolean).length ? ` class="${escapeHtml(classes.filter(Boolean).join(" "))}"` : "";
|
|
19278
|
+
if (PASSTHROUGH_TAGS.has(name)) return `<${name}${classAttr}>`;
|
|
19279
|
+
if (name === "v") {
|
|
19280
|
+
const title = annotation ? ` title="${escapeHtml(annotation)}"` : "";
|
|
19281
|
+
return `<span${classAttr}${title}>`;
|
|
19282
|
+
}
|
|
19283
|
+
if (name === "lang") {
|
|
19284
|
+
const lang = annotation ? ` lang="${escapeHtml(annotation)}"` : "";
|
|
19285
|
+
return `<span${classAttr}${lang}>`;
|
|
19286
|
+
}
|
|
19287
|
+
if (name === "c") return `<span${classAttr}>`;
|
|
19288
|
+
return "";
|
|
19289
|
+
}
|
|
19290
|
+
function closeTag(name) {
|
|
19291
|
+
if (PASSTHROUGH_TAGS.has(name)) return `</${name}>`;
|
|
19292
|
+
if (SPAN_TAGS.has(name)) return "</span>";
|
|
19293
|
+
return "";
|
|
19294
|
+
}
|
|
19295
|
+
function renderCueText(text) {
|
|
19296
|
+
var _a3;
|
|
19297
|
+
let html = "";
|
|
19298
|
+
for (const token of (_a3 = text.match(/<[^<>]*>|[^<]+|</g)) != null ? _a3 : []) {
|
|
19299
|
+
if (token[0] !== "<" || token === "<") {
|
|
19300
|
+
html += escapeHtml(token).replace(/\n/g, "<br>");
|
|
19301
|
+
} else {
|
|
19302
|
+
const inner = token.slice(1, -1);
|
|
19303
|
+
html += inner[0] === "/" ? closeTag(
|
|
19304
|
+
inner.slice(1).split(/[.\s]/, 1)[0].toLowerCase()
|
|
19305
|
+
) : openTag(inner);
|
|
19306
|
+
}
|
|
19307
|
+
}
|
|
19308
|
+
return html;
|
|
19309
|
+
}
|
|
19310
|
+
function scopeCueRules(css) {
|
|
19311
|
+
return css.replace(/::cue\(([^)]*)\)/g, ".vinyl-text-cue $1").replace(/::cue\b/g, ".vinyl-text-cue");
|
|
19312
|
+
}
|
|
19313
|
+
var HtmlTextTrackRenderer = class {
|
|
19314
|
+
constructor() {
|
|
19315
|
+
/** The overlay container to mount over the video. */
|
|
19316
|
+
__publicField(this, "element");
|
|
19317
|
+
__publicField(this, "track", null);
|
|
19318
|
+
// Holds the authored STYLE-block CSS (::cue rules rewritten to target our
|
|
19319
|
+
// cue elements). A <style> renders nothing itself, so it lives in-container.
|
|
19320
|
+
__publicField(this, "styleElement");
|
|
19321
|
+
__publicField(this, "onCueChange", () => this.renderActiveCues());
|
|
19322
|
+
this.element = document.createElement("div");
|
|
19323
|
+
this.element.className = "vinyl-text-container";
|
|
19324
|
+
Object.assign(this.element.style, {
|
|
19325
|
+
display: "flex",
|
|
19326
|
+
flexDirection: "column",
|
|
19327
|
+
justifyContent: "flex-end",
|
|
19328
|
+
alignItems: "center",
|
|
19329
|
+
pointerEvents: "none"
|
|
19330
|
+
});
|
|
19331
|
+
this.styleElement = document.createElement("style");
|
|
19332
|
+
this.element.appendChild(this.styleElement);
|
|
19333
|
+
}
|
|
19334
|
+
setTextTrack(track2) {
|
|
19335
|
+
var _a3;
|
|
19336
|
+
if (track2 === this.track) return;
|
|
19337
|
+
(_a3 = this.track) == null ? void 0 : _a3.removeEventListener("cuechange", this.onCueChange);
|
|
19338
|
+
this.track = track2;
|
|
19339
|
+
this.styleElement.textContent = "";
|
|
19340
|
+
if (!track2) {
|
|
19341
|
+
this.element.replaceChildren(this.styleElement);
|
|
19342
|
+
return;
|
|
19343
|
+
}
|
|
19344
|
+
track2.addEventListener("cuechange", this.onCueChange);
|
|
19345
|
+
this.renderActiveCues();
|
|
19346
|
+
}
|
|
19347
|
+
setStyles(styles) {
|
|
19348
|
+
this.styleElement.textContent = styles.map(scopeCueRules).join("\n");
|
|
19349
|
+
}
|
|
19350
|
+
dispose() {
|
|
19351
|
+
this.setTextTrack(null);
|
|
19352
|
+
}
|
|
19353
|
+
renderActiveCues() {
|
|
19354
|
+
var _a3;
|
|
19355
|
+
const active = (_a3 = this.track.activeCues) != null ? _a3 : [];
|
|
19356
|
+
const children = [this.styleElement];
|
|
19357
|
+
for (let i = 0; i < active.length; i++) {
|
|
19358
|
+
children.push(this.createCueBox(active[i]));
|
|
19359
|
+
}
|
|
19360
|
+
this.element.replaceChildren(...children);
|
|
19361
|
+
}
|
|
19362
|
+
createCueBox(cue) {
|
|
19363
|
+
var _a3;
|
|
19364
|
+
const box2 = document.createElement("div");
|
|
19365
|
+
box2.className = "vinyl-text-cue";
|
|
19366
|
+
Object.assign(box2.style, computeCueLayout(cue));
|
|
19367
|
+
box2.innerHTML = renderCueText((_a3 = cue.text) != null ? _a3 : "");
|
|
19368
|
+
return box2;
|
|
19369
|
+
}
|
|
19370
|
+
};
|
|
19371
|
+
|
|
19372
|
+
// src/text/mediaTextTrackProvider.ts
|
|
19373
|
+
function createMediaTextTrackProvider(deps) {
|
|
19374
|
+
const { media } = deps;
|
|
19375
|
+
const cache = /* @__PURE__ */ new Map();
|
|
19376
|
+
const isAttached = (track2) => {
|
|
19377
|
+
for (let i = 0; i < media.textTracks.length; i++) {
|
|
19378
|
+
if (media.textTracks[i] === track2) return true;
|
|
19379
|
+
}
|
|
19380
|
+
return false;
|
|
19381
|
+
};
|
|
19382
|
+
const create = (kind, label, language) => {
|
|
19383
|
+
const track2 = media.addTextTrack(kind, label, language);
|
|
19384
|
+
return {
|
|
19385
|
+
track: track2,
|
|
19386
|
+
clear() {
|
|
19387
|
+
if (!track2.cues) return;
|
|
19388
|
+
const all = Array.from(track2.cues);
|
|
19389
|
+
for (const cue of all) track2.removeCue(cue);
|
|
19390
|
+
}
|
|
19391
|
+
};
|
|
19392
|
+
};
|
|
19393
|
+
return {
|
|
19394
|
+
getOrCreate(kind, label, language) {
|
|
19395
|
+
const key = `${kind}-${label}-${language}`;
|
|
19396
|
+
const cached = cache.get(key);
|
|
19397
|
+
if (cached && isAttached(cached.track)) return cached;
|
|
19398
|
+
const ref = create(kind, label, language);
|
|
19399
|
+
cache.set(key, ref);
|
|
19400
|
+
return ref;
|
|
19401
|
+
}
|
|
19402
|
+
};
|
|
19403
|
+
}
|
|
19404
|
+
|
|
19206
19405
|
// src/text/parseWebVtt.ts
|
|
19207
19406
|
var WEBVTT_HEADER = "WEBVTT";
|
|
19208
|
-
var
|
|
19407
|
+
var TIMESTAMP_RE = /^(?:(\d+):)?([0-5]\d):([0-5]\d)\.(\d{3})/;
|
|
19209
19408
|
function parseWebVtt(input) {
|
|
19210
19409
|
if (typeof input !== "string") {
|
|
19211
19410
|
throw new ValidationError(
|
|
@@ -19223,6 +19422,7 @@ ${this.lastInitStack}` : "";
|
|
|
19223
19422
|
);
|
|
19224
19423
|
}
|
|
19225
19424
|
const cues = [];
|
|
19425
|
+
const styles = [];
|
|
19226
19426
|
let i = 1;
|
|
19227
19427
|
while (i < lines.length && lines[i] !== "") i++;
|
|
19228
19428
|
while (i < lines.length) {
|
|
@@ -19230,49 +19430,71 @@ ${this.lastInitStack}` : "";
|
|
|
19230
19430
|
i++;
|
|
19231
19431
|
continue;
|
|
19232
19432
|
}
|
|
19233
|
-
if (lines[i] === "NOTE" || lines[i].startsWith("NOTE ")) {
|
|
19433
|
+
if (lines[i] === "NOTE" || lines[i].startsWith("NOTE ") || lines[i].startsWith("NOTE ") || lines[i] === "REGION") {
|
|
19234
19434
|
i++;
|
|
19235
19435
|
while (i < lines.length && lines[i] !== "") i++;
|
|
19236
19436
|
continue;
|
|
19237
19437
|
}
|
|
19238
|
-
if (lines[i] === "STYLE"
|
|
19438
|
+
if (lines[i] === "STYLE") {
|
|
19239
19439
|
i++;
|
|
19440
|
+
const start = i;
|
|
19240
19441
|
while (i < lines.length && lines[i] !== "") i++;
|
|
19442
|
+
const css = lines.slice(start, i).join("\n").trim();
|
|
19443
|
+
if (css) styles.push(css);
|
|
19241
19444
|
continue;
|
|
19242
19445
|
}
|
|
19243
19446
|
let id = null;
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
if (TIME_RE.test(firstLine)) {
|
|
19247
|
-
timingLine = firstLine;
|
|
19248
|
-
i++;
|
|
19249
|
-
} else {
|
|
19250
|
-
id = firstLine;
|
|
19447
|
+
if (!lines[i].includes("-->")) {
|
|
19448
|
+
id = lines[i];
|
|
19251
19449
|
i++;
|
|
19252
19450
|
if (i >= lines.length) break;
|
|
19253
|
-
timingLine = lines[i];
|
|
19254
|
-
i++;
|
|
19255
19451
|
}
|
|
19256
|
-
const
|
|
19257
|
-
|
|
19452
|
+
const timing = parseTiming(lines[i]);
|
|
19453
|
+
i++;
|
|
19454
|
+
if (!timing) {
|
|
19258
19455
|
while (i < lines.length && lines[i] !== "") i++;
|
|
19259
19456
|
continue;
|
|
19260
19457
|
}
|
|
19261
|
-
const startTime = toSeconds(match[1], match[2], match[3], match[4]);
|
|
19262
|
-
const endTime = toSeconds(match[5], match[6], match[7], match[8]);
|
|
19263
19458
|
const payloadLines = [];
|
|
19264
19459
|
while (i < lines.length && lines[i] !== "") {
|
|
19265
19460
|
payloadLines.push(lines[i]);
|
|
19266
19461
|
i++;
|
|
19267
19462
|
}
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19463
|
+
const text2 = payloadLines.join("\n");
|
|
19464
|
+
cues.push(
|
|
19465
|
+
timing.settings === void 0 ? {
|
|
19466
|
+
id,
|
|
19467
|
+
startTime: timing.startTime,
|
|
19468
|
+
endTime: timing.endTime,
|
|
19469
|
+
text: text2
|
|
19470
|
+
} : {
|
|
19471
|
+
id,
|
|
19472
|
+
startTime: timing.startTime,
|
|
19473
|
+
endTime: timing.endTime,
|
|
19474
|
+
text: text2,
|
|
19475
|
+
settings: timing.settings
|
|
19476
|
+
}
|
|
19477
|
+
);
|
|
19478
|
+
}
|
|
19479
|
+
return { cues, styles };
|
|
19480
|
+
}
|
|
19481
|
+
function parseTiming(line) {
|
|
19482
|
+
const arrow = line.indexOf("-->");
|
|
19483
|
+
if (arrow < 0) return null;
|
|
19484
|
+
const before = line.slice(0, arrow).trim();
|
|
19485
|
+
const start = TIMESTAMP_RE.exec(before);
|
|
19486
|
+
if (!start || start[0].length !== before.length) return null;
|
|
19487
|
+
const rest = line.slice(arrow + 3).trim();
|
|
19488
|
+
const end = TIMESTAMP_RE.exec(rest);
|
|
19489
|
+
if (!end) return null;
|
|
19490
|
+
return {
|
|
19491
|
+
startTime: toSeconds(start[1], start[2], start[3], start[4]),
|
|
19492
|
+
endTime: toSeconds(end[1], end[2], end[3], end[4]),
|
|
19493
|
+
settings: parseCueSettings(rest.slice(end[0].length))
|
|
19494
|
+
};
|
|
19495
|
+
}
|
|
19496
|
+
function toSeconds(hours, minutes, seconds, millis) {
|
|
19497
|
+
return (hours ? Number(hours) * 3600 : 0) + Number(minutes) * 60 + Number(seconds) + Number(millis) / 1e3;
|
|
19276
19498
|
}
|
|
19277
19499
|
function isWebVttHeader(line) {
|
|
19278
19500
|
if (!line.startsWith(WEBVTT_HEADER)) return false;
|
|
@@ -19280,18 +19502,80 @@ ${this.lastInitStack}` : "";
|
|
|
19280
19502
|
const next = line.charCodeAt(WEBVTT_HEADER.length);
|
|
19281
19503
|
return next === 32 || next === 9;
|
|
19282
19504
|
}
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19505
|
+
var ALIGN = /* @__PURE__ */ new Set(["start", "center", "end", "left", "right"]);
|
|
19506
|
+
var LINE_ALIGN = /* @__PURE__ */ new Set(["start", "center", "end"]);
|
|
19507
|
+
var POSITION_ALIGN = /* @__PURE__ */ new Set(["line-left", "center", "line-right", "auto"]);
|
|
19508
|
+
var VERTICAL = /* @__PURE__ */ new Set(["rl", "lr"]);
|
|
19509
|
+
function parseCueSettings(text) {
|
|
19510
|
+
if (!text) return void 0;
|
|
19511
|
+
const settings = {};
|
|
19512
|
+
for (const token of text.trim().split(/\s+/)) {
|
|
19513
|
+
const colon = token.indexOf(":");
|
|
19514
|
+
if (colon <= 0) continue;
|
|
19515
|
+
const key = token.slice(0, colon);
|
|
19516
|
+
const value = token.slice(colon + 1);
|
|
19517
|
+
switch (key) {
|
|
19518
|
+
case "align": {
|
|
19519
|
+
const align = value === "middle" ? "center" : value;
|
|
19520
|
+
if (ALIGN.has(align)) settings.align = align;
|
|
19521
|
+
break;
|
|
19522
|
+
}
|
|
19523
|
+
case "vertical":
|
|
19524
|
+
if (VERTICAL.has(value))
|
|
19525
|
+
settings.vertical = value;
|
|
19526
|
+
break;
|
|
19527
|
+
case "size": {
|
|
19528
|
+
const size = parsePercent(value);
|
|
19529
|
+
if (size != null) settings.size = size;
|
|
19530
|
+
break;
|
|
19531
|
+
}
|
|
19532
|
+
case "line":
|
|
19533
|
+
parseLine(value, settings);
|
|
19534
|
+
break;
|
|
19535
|
+
case "position":
|
|
19536
|
+
parsePosition(value, settings);
|
|
19537
|
+
break;
|
|
19538
|
+
}
|
|
19539
|
+
}
|
|
19540
|
+
return Object.keys(settings).length > 0 ? settings : void 0;
|
|
19541
|
+
}
|
|
19542
|
+
function parseLine(value, settings) {
|
|
19543
|
+
const [pos, align] = value.split(",");
|
|
19544
|
+
if (pos.endsWith("%")) {
|
|
19545
|
+
const percent = parsePercent(pos);
|
|
19546
|
+
if (percent == null) return;
|
|
19547
|
+
settings.line = percent;
|
|
19548
|
+
settings.snapToLines = false;
|
|
19549
|
+
} else {
|
|
19550
|
+
const n = Number(pos);
|
|
19551
|
+
if (!Number.isInteger(n)) return;
|
|
19552
|
+
settings.line = n;
|
|
19553
|
+
settings.snapToLines = true;
|
|
19554
|
+
}
|
|
19555
|
+
const lineAlign = align === "middle" ? "center" : align;
|
|
19556
|
+
if (lineAlign && LINE_ALIGN.has(lineAlign))
|
|
19557
|
+
settings.lineAlign = lineAlign;
|
|
19558
|
+
}
|
|
19559
|
+
function parsePosition(value, settings) {
|
|
19560
|
+
const [pos, align] = value.split(",");
|
|
19561
|
+
const percent = parsePercent(pos);
|
|
19562
|
+
if (percent == null) return;
|
|
19563
|
+
settings.position = percent;
|
|
19564
|
+
const positionAlign = align === "middle" ? "center" : align;
|
|
19565
|
+
if (positionAlign && POSITION_ALIGN.has(positionAlign))
|
|
19566
|
+
settings.positionAlign = positionAlign;
|
|
19567
|
+
}
|
|
19568
|
+
function parsePercent(value) {
|
|
19569
|
+
if (!value.endsWith("%")) return null;
|
|
19570
|
+
const n = Number(value.slice(0, -1));
|
|
19571
|
+
return Number.isFinite(n) ? n : null;
|
|
19289
19572
|
}
|
|
19290
19573
|
|
|
19291
19574
|
// src/text/pickPreferredTextTrack.ts
|
|
19292
19575
|
function resolvePreferredTextLanguages(preferred) {
|
|
19293
19576
|
if (typeof preferred === "string") return [preferred];
|
|
19294
19577
|
if (preferred) return preferred;
|
|
19578
|
+
if (typeof navigator !== "undefined") return [...navigator.languages];
|
|
19295
19579
|
return [];
|
|
19296
19580
|
}
|
|
19297
19581
|
function pickPreferredTextTrack(tracks, preferred) {
|
|
@@ -19329,7 +19613,7 @@ ${this.lastInitStack}` : "";
|
|
|
19329
19613
|
var IDLE_POLL_SECONDS = 0.25;
|
|
19330
19614
|
async function loadWebVttCues(uri, options) {
|
|
19331
19615
|
var _a3, _b, _c;
|
|
19332
|
-
const { abort, requestInit, variables, onCues, getCurrentTime } = options;
|
|
19616
|
+
const { abort, requestInit, variables, onCues, onStyles, getCurrentTime } = options;
|
|
19333
19617
|
const lookAhead = (_a3 = options.lookAhead) != null ? _a3 : DEFAULT_TEXT_LOOK_AHEAD;
|
|
19334
19618
|
const lookBehind = (_b = options.lookBehind) != null ? _b : DEFAULT_TEXT_LOOK_BEHIND;
|
|
19335
19619
|
const init = requestInit != null ? requestInit : void 0;
|
|
@@ -19340,6 +19624,7 @@ ${this.lastInitStack}` : "";
|
|
|
19340
19624
|
abort,
|
|
19341
19625
|
variables: variables != null ? variables : void 0,
|
|
19342
19626
|
onCues,
|
|
19627
|
+
onStyles,
|
|
19343
19628
|
getCurrentTime,
|
|
19344
19629
|
lookAhead,
|
|
19345
19630
|
lookBehind
|
|
@@ -19356,13 +19641,15 @@ ${this.lastInitStack}` : "";
|
|
|
19356
19641
|
abort,
|
|
19357
19642
|
variables: variables != null ? variables : void 0,
|
|
19358
19643
|
onCues,
|
|
19644
|
+
onStyles,
|
|
19359
19645
|
getCurrentTime,
|
|
19360
19646
|
lookAhead,
|
|
19361
19647
|
lookBehind
|
|
19362
19648
|
});
|
|
19363
19649
|
}
|
|
19364
|
-
const
|
|
19365
|
-
if (cues.length > 0) onCues(cues);
|
|
19650
|
+
const doc = parseWebVtt(body);
|
|
19651
|
+
if (doc.cues.length > 0) onCues(doc.cues);
|
|
19652
|
+
if (onStyles && doc.styles.length > 0) onStyles(doc.styles);
|
|
19366
19653
|
}
|
|
19367
19654
|
function looksLikeMediaPlaylist(uri) {
|
|
19368
19655
|
const path = uri.split(/[?#]/)[0];
|
|
@@ -19402,8 +19689,9 @@ ${this.lastInitStack}` : "";
|
|
|
19402
19689
|
abort: ctx.abort
|
|
19403
19690
|
});
|
|
19404
19691
|
const body = await res.text();
|
|
19405
|
-
const
|
|
19406
|
-
if (cues.length > 0) ctx.onCues(cues);
|
|
19692
|
+
const doc = parseWebVtt(body);
|
|
19693
|
+
if (doc.cues.length > 0) ctx.onCues(doc.cues);
|
|
19694
|
+
if (ctx.onStyles && doc.styles.length > 0) ctx.onStyles(doc.styles);
|
|
19407
19695
|
}
|
|
19408
19696
|
}
|
|
19409
19697
|
function pickInWindow(pending2, ranges, ctx) {
|
|
@@ -19457,22 +19745,45 @@ ${this.lastInitStack}` : "";
|
|
|
19457
19745
|
});
|
|
19458
19746
|
|
|
19459
19747
|
// src/text/SidecarTextTrackController.ts
|
|
19748
|
+
function resolveSelectedTrack(tracks, options) {
|
|
19749
|
+
var _a3, _b, _c, _d;
|
|
19750
|
+
const mode = (_a3 = options.enabled) != null ? _a3 : "forced";
|
|
19751
|
+
if (mode === "off") return null;
|
|
19752
|
+
const selection = (_b = options.selection) != null ? _b : {};
|
|
19753
|
+
if (selection.id != null) {
|
|
19754
|
+
return (_c = tracks.find((t) => t.id === selection.id)) != null ? _c : null;
|
|
19755
|
+
}
|
|
19756
|
+
let pool = tracks;
|
|
19757
|
+
if (selection.kind != null) {
|
|
19758
|
+
pool = pool.filter((t) => t.kind === selection.kind);
|
|
19759
|
+
}
|
|
19760
|
+
const forced = (_d = selection.forced) != null ? _d : mode === "forced";
|
|
19761
|
+
pool = pool.filter((t) => t.forced === forced);
|
|
19762
|
+
return pickPreferredTextTrack(pool, selection.language);
|
|
19763
|
+
}
|
|
19460
19764
|
var SidecarTextTrackController = class extends EventHostImpl {
|
|
19461
19765
|
constructor(deps) {
|
|
19462
|
-
var _a3, _b;
|
|
19463
19766
|
super();
|
|
19464
19767
|
__publicField(this, "deps", deps);
|
|
19768
|
+
// Whether the owning media track is current (rendering allowed). Toggled by
|
|
19769
|
+
// activate()/deactivate() across suspensions such as ad breaks.
|
|
19770
|
+
__publicField(this, "_active", false);
|
|
19465
19771
|
__publicField(this, "_tracks", []);
|
|
19466
|
-
__publicField(this, "
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
__publicField(this, "
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19772
|
+
__publicField(this, "_activeTextTrack", null);
|
|
19773
|
+
// The DOM text track currently rendering cues, or null when hidden. Owned by
|
|
19774
|
+
// the provider (never created/removed here).
|
|
19775
|
+
__publicField(this, "_ref", null);
|
|
19776
|
+
__publicField(this, "loadAbort", createAbortSlot());
|
|
19777
|
+
__publicField(this, "disposer", createDisposer());
|
|
19778
|
+
const { add } = this.disposer;
|
|
19779
|
+
add(deps.options.onData(() => this.refreshSelection()));
|
|
19780
|
+
add(
|
|
19781
|
+
deps.textTracks.onData((tracksPromise) => {
|
|
19782
|
+
tracksPromise.then((tracks) => this.setTracks(tracks)).catch((error) => {
|
|
19783
|
+
logError(this, "text tracks promise rejected", error);
|
|
19784
|
+
});
|
|
19785
|
+
})
|
|
19786
|
+
);
|
|
19476
19787
|
}
|
|
19477
19788
|
get [Symbol.toStringTag]() {
|
|
19478
19789
|
return "SidecarTextTrackController";
|
|
@@ -19481,120 +19792,89 @@ ${this.lastInitStack}` : "";
|
|
|
19481
19792
|
return this._tracks;
|
|
19482
19793
|
}
|
|
19483
19794
|
get activeTextTrack() {
|
|
19795
|
+
return this._activeTextTrack;
|
|
19796
|
+
}
|
|
19797
|
+
get active() {
|
|
19484
19798
|
return this._active;
|
|
19485
19799
|
}
|
|
19800
|
+
get disposed() {
|
|
19801
|
+
return this.disposer.disposed;
|
|
19802
|
+
}
|
|
19486
19803
|
/**
|
|
19487
|
-
* Replaces the discovered
|
|
19488
|
-
* the
|
|
19489
|
-
* selection and emits `activeTextTrackChange`.
|
|
19804
|
+
* Replaces the discovered track list, emits `textTracksChange`, then
|
|
19805
|
+
* re-applies the current selection against the new list.
|
|
19490
19806
|
*/
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
if (equalDeep(this._tracks, tracks)) return;
|
|
19807
|
+
setTracks(tracks) {
|
|
19808
|
+
if (this.disposed) return;
|
|
19494
19809
|
const previous = this._tracks;
|
|
19810
|
+
if (equalDeep(previous, tracks)) return;
|
|
19495
19811
|
this._tracks = tracks;
|
|
19496
19812
|
this.dispatch("textTracksChange", { previous, current: tracks });
|
|
19497
|
-
|
|
19498
|
-
this.selectById(null);
|
|
19499
|
-
}
|
|
19500
|
-
const preferred = (_a3 = this.deps.preferredTextLanguage) == null ? void 0 : _a3.value;
|
|
19501
|
-
if (preferred != null) {
|
|
19502
|
-
this.applyPreferredLanguage(preferred);
|
|
19503
|
-
} else if (!this._userSelected && !this._active) {
|
|
19504
|
-
const forced = this.pickForcedTrack(tracks);
|
|
19505
|
-
if (forced) this.selectById(forced.id);
|
|
19506
|
-
}
|
|
19507
|
-
}
|
|
19508
|
-
setActiveTextTrack(id) {
|
|
19509
|
-
this._userSelected = true;
|
|
19510
|
-
this.selectById(id);
|
|
19511
|
-
}
|
|
19512
|
-
/**
|
|
19513
|
-
* Selects the track best matching the preferred language(s), or clears the
|
|
19514
|
-
* selection when the preference is null or nothing matches.
|
|
19515
|
-
*/
|
|
19516
|
-
applyPreferredLanguage(preferred) {
|
|
19517
|
-
var _a3;
|
|
19518
|
-
const target = pickPreferredTextTrack(this._tracks, preferred);
|
|
19519
|
-
this.setActiveTextTrack((_a3 = target == null ? void 0 : target.id) != null ? _a3 : null);
|
|
19813
|
+
this.refreshSelection();
|
|
19520
19814
|
}
|
|
19521
19815
|
/**
|
|
19522
|
-
*
|
|
19523
|
-
*
|
|
19816
|
+
* Recomputes the selection from the current options and track list,
|
|
19817
|
+
* emitting `activeTextTrackChange` on change and (re)rendering when active.
|
|
19524
19818
|
*/
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
const
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
const
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
current: target
|
|
19539
|
-
});
|
|
19540
|
-
if (target) this.startLoad(target);
|
|
19541
|
-
}
|
|
19542
|
-
/**
|
|
19543
|
-
* The forced track to auto-select, or null. Prefers the default track's
|
|
19544
|
-
* language, else the first forced track.
|
|
19545
|
-
*/
|
|
19546
|
-
pickForcedTrack(tracks) {
|
|
19547
|
-
var _a3;
|
|
19548
|
-
const forced = tracks.filter((t) => t.forced);
|
|
19549
|
-
if (forced.length === 0) return null;
|
|
19550
|
-
const defaultLang = (_a3 = tracks.find((t) => t.default)) == null ? void 0 : _a3.language;
|
|
19551
|
-
if (defaultLang != null) {
|
|
19552
|
-
const match = forced.find((t) => t.language === defaultLang);
|
|
19553
|
-
if (match) return match;
|
|
19819
|
+
refreshSelection() {
|
|
19820
|
+
const previous = this._activeTextTrack;
|
|
19821
|
+
const target = resolveSelectedTrack(
|
|
19822
|
+
this._tracks,
|
|
19823
|
+
this.deps.options.value
|
|
19824
|
+
);
|
|
19825
|
+
const trackChanged = (target == null ? void 0 : target.id) !== (previous == null ? void 0 : previous.id);
|
|
19826
|
+
if (trackChanged) {
|
|
19827
|
+
this._activeTextTrack = target;
|
|
19828
|
+
this.dispatch("activeTextTrackChange", {
|
|
19829
|
+
previous,
|
|
19830
|
+
current: target
|
|
19831
|
+
});
|
|
19554
19832
|
}
|
|
19555
|
-
return
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
var _a3;
|
|
19559
|
-
(_a3 = this.loadAbort) == null ? void 0 : _a3.abort();
|
|
19560
|
-
this.loadAbort = null;
|
|
19561
|
-
this.clearDomTrack();
|
|
19833
|
+
if (!this._active) return;
|
|
19834
|
+
if (!target) this.hideCurrent();
|
|
19835
|
+
else if (trackChanged || !this._ref) this.startLoad(target);
|
|
19562
19836
|
}
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
this.
|
|
19837
|
+
deactivate() {
|
|
19838
|
+
this._active = false;
|
|
19839
|
+
this.hideCurrent();
|
|
19566
19840
|
}
|
|
19567
|
-
|
|
19568
|
-
|
|
19569
|
-
|
|
19570
|
-
|
|
19571
|
-
dispose() {
|
|
19572
|
-
var _a3, _b;
|
|
19573
|
-
super.dispose();
|
|
19574
|
-
(_a3 = this.preferredLanguageSub) == null ? void 0 : _a3.call(this);
|
|
19575
|
-
(_b = this.loadAbort) == null ? void 0 : _b.abort();
|
|
19576
|
-
this.loadAbort = null;
|
|
19577
|
-
this.clearDomTrack();
|
|
19578
|
-
this._tracks = [];
|
|
19579
|
-
this._active = null;
|
|
19841
|
+
activate() {
|
|
19842
|
+
this._active = true;
|
|
19843
|
+
if (!this._activeTextTrack || this._ref) return;
|
|
19844
|
+
this.startLoad(this._activeTextTrack);
|
|
19580
19845
|
}
|
|
19581
19846
|
startLoad(track2) {
|
|
19582
|
-
|
|
19583
|
-
|
|
19584
|
-
const abort =
|
|
19585
|
-
|
|
19586
|
-
|
|
19847
|
+
var _a3, _b;
|
|
19848
|
+
this.hideCurrent();
|
|
19849
|
+
const abort = this.loadAbort.value;
|
|
19850
|
+
const ref = this.deps.textTrackProvider.getOrCreate(
|
|
19851
|
+
track2.kind,
|
|
19852
|
+
track2.label,
|
|
19853
|
+
(_a3 = track2.language) != null ? _a3 : void 0
|
|
19854
|
+
);
|
|
19855
|
+
const renderer = this.deps.textTrackRenderer;
|
|
19856
|
+
ref.track.mode = renderer ? "hidden" : "showing";
|
|
19857
|
+
ref.clear();
|
|
19858
|
+
this._ref = ref;
|
|
19859
|
+
renderer == null ? void 0 : renderer.setTextTrack(ref.track);
|
|
19587
19860
|
const CueCtor = getVttCueConstructor();
|
|
19588
19861
|
const seen = /* @__PURE__ */ new Set();
|
|
19862
|
+
const styles = /* @__PURE__ */ new Set();
|
|
19863
|
+
const setStyles = (_b = renderer == null ? void 0 : renderer.setStyles) == null ? void 0 : _b.bind(renderer);
|
|
19589
19864
|
loadWebVttCues(track2.uri, {
|
|
19590
19865
|
abort,
|
|
19591
19866
|
requestInit: this.deps.requestInit,
|
|
19592
19867
|
variables: track2.variables,
|
|
19593
|
-
getCurrentTime: () =>
|
|
19868
|
+
getCurrentTime: () => this.deps.playbackController.currentTime,
|
|
19594
19869
|
onCues: (cues) => {
|
|
19595
19870
|
if (abort.aborted()) return;
|
|
19596
|
-
if (CueCtor) this.appendCues(
|
|
19597
|
-
}
|
|
19871
|
+
if (CueCtor) this.appendCues(ref.track, CueCtor, cues, seen);
|
|
19872
|
+
},
|
|
19873
|
+
onStyles: setStyles ? (blocks) => {
|
|
19874
|
+
if (abort.aborted()) return;
|
|
19875
|
+
for (const block of blocks) styles.add(block);
|
|
19876
|
+
setStyles([...styles]);
|
|
19877
|
+
} : void 0
|
|
19598
19878
|
}).catch((error) => {
|
|
19599
19879
|
if (abort.aborted()) return;
|
|
19600
19880
|
this.dispatch("textTrackError", {
|
|
@@ -19603,43 +19883,45 @@ ${this.lastInitStack}` : "";
|
|
|
19603
19883
|
});
|
|
19604
19884
|
});
|
|
19605
19885
|
}
|
|
19606
|
-
ensureDomTrack(media, track2) {
|
|
19607
|
-
var _a3;
|
|
19608
|
-
const dom = media.addTextTrack(
|
|
19609
|
-
track2.kind,
|
|
19610
|
-
track2.label,
|
|
19611
|
-
(_a3 = track2.language) != null ? _a3 : void 0
|
|
19612
|
-
);
|
|
19613
|
-
dom.mode = "showing";
|
|
19614
|
-
this._domTrack = dom;
|
|
19615
|
-
return dom;
|
|
19616
|
-
}
|
|
19617
19886
|
appendCues(dom, CueCtor, cues, seen) {
|
|
19618
|
-
var _a3;
|
|
19619
19887
|
for (const cue of cues) {
|
|
19620
19888
|
const key = `${cue.startTime}|${cue.endTime}|${cue.text}`;
|
|
19621
19889
|
if (seen.has(key)) continue;
|
|
19622
19890
|
seen.add(key);
|
|
19623
19891
|
const domCue = new CueCtor(cue.startTime, cue.endTime, cue.text);
|
|
19624
19892
|
if (cue.id != null) domCue.id = cue.id;
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
applyVttCueStyle(domCue, cueStyle);
|
|
19893
|
+
if (cue.settings && isVttCue(domCue)) {
|
|
19894
|
+
applyVttCueStyle(domCue, cue.settings);
|
|
19628
19895
|
}
|
|
19629
19896
|
dom.addCue(domCue);
|
|
19630
19897
|
}
|
|
19631
19898
|
}
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19638
|
-
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19899
|
+
/**
|
|
19900
|
+
* Clears cues from the current DOM track and hides it, keeping the track for
|
|
19901
|
+
* later reuse via the provider.
|
|
19902
|
+
*/
|
|
19903
|
+
hideCurrent() {
|
|
19904
|
+
var _a3;
|
|
19905
|
+
this.loadAbort.abort();
|
|
19906
|
+
const ref = this._ref;
|
|
19907
|
+
if (!ref) return;
|
|
19908
|
+
(_a3 = this.deps.textTrackRenderer) == null ? void 0 : _a3.setTextTrack(null);
|
|
19909
|
+
ref.clear();
|
|
19910
|
+
ref.track.mode = "disabled";
|
|
19911
|
+
this._ref = null;
|
|
19912
|
+
}
|
|
19913
|
+
/**
|
|
19914
|
+
* Cancels any in-flight load, hides the DOM text track, and resets state.
|
|
19915
|
+
* Called by the host when the underlying media is unloaded.
|
|
19916
|
+
*/
|
|
19917
|
+
dispose() {
|
|
19918
|
+
if (this.disposed) return;
|
|
19919
|
+
super.dispose();
|
|
19920
|
+
this.loadAbort.abort();
|
|
19921
|
+
this.hideCurrent();
|
|
19922
|
+
this.disposer.dispose();
|
|
19923
|
+
this._tracks = [];
|
|
19924
|
+
this._activeTextTrack = null;
|
|
19643
19925
|
}
|
|
19644
19926
|
};
|
|
19645
19927
|
function getVttCueConstructor() {
|
|
@@ -19649,6 +19931,23 @@ ${this.lastInitStack}` : "";
|
|
|
19649
19931
|
}
|
|
19650
19932
|
|
|
19651
19933
|
// src/text/TextTrack.ts
|
|
19934
|
+
var TEXT_TRACK_KINDS = [
|
|
19935
|
+
"subtitles",
|
|
19936
|
+
"captions",
|
|
19937
|
+
"descriptions",
|
|
19938
|
+
"chapters",
|
|
19939
|
+
"metadata"
|
|
19940
|
+
];
|
|
19941
|
+
var textTrackSelectionValidator = object({
|
|
19942
|
+
id: string().orNull().optional(),
|
|
19943
|
+
kind: isOneOf(...TEXT_TRACK_KINDS).orNull().optional(),
|
|
19944
|
+
language: string().or(array(string()).readonly()).orNull().optional(),
|
|
19945
|
+
forced: boolean().orNull().optional()
|
|
19946
|
+
});
|
|
19947
|
+
var textTrackControllerOptionsValidator = object({
|
|
19948
|
+
enabled: isOneOf("on", "off", "forced").orNull().optional(),
|
|
19949
|
+
selection: textTrackSelectionValidator.orNull().optional()
|
|
19950
|
+
});
|
|
19652
19951
|
var ALL_TEXT_TRACK_EVENTS = [
|
|
19653
19952
|
"textTracksChange",
|
|
19654
19953
|
"activeTextTrackChange",
|
|
@@ -20305,7 +20604,7 @@ ${this.lastInitStack}` : "";
|
|
|
20305
20604
|
)
|
|
20306
20605
|
);
|
|
20307
20606
|
add(
|
|
20308
|
-
deps.
|
|
20607
|
+
deps.audio.onData((_value, previous) => {
|
|
20309
20608
|
if (previous !== void 0) this.reloadStreams();
|
|
20310
20609
|
})
|
|
20311
20610
|
);
|
|
@@ -20314,11 +20613,6 @@ ${this.lastInitStack}` : "";
|
|
|
20314
20613
|
if (previous !== void 0) this.reloadStreams();
|
|
20315
20614
|
})
|
|
20316
20615
|
);
|
|
20317
|
-
add(
|
|
20318
|
-
deps.preferDescriptiveAudio.onData((_value, previous) => {
|
|
20319
|
-
if (previous !== void 0) this.reloadStreams();
|
|
20320
|
-
})
|
|
20321
|
-
);
|
|
20322
20616
|
add(
|
|
20323
20617
|
deps.abr.onData((value, previous) => {
|
|
20324
20618
|
if (previous === void 0) return;
|
|
@@ -20525,7 +20819,7 @@ ${this.lastInitStack}` : "";
|
|
|
20525
20819
|
onActivated(loadOptions) {
|
|
20526
20820
|
var _a3;
|
|
20527
20821
|
if (this.disposed) return;
|
|
20528
|
-
(_a3 = this.deps.textTrackController) == null ? void 0 : _a3.
|
|
20822
|
+
(_a3 = this.deps.textTrackController) == null ? void 0 : _a3.activate();
|
|
20529
20823
|
this.deps.playbackSource.disableRemotePlayback = true;
|
|
20530
20824
|
this.streams.forEach((stream) => {
|
|
20531
20825
|
this.deps.drmController.initializeForPlayback(
|
|
@@ -20558,7 +20852,7 @@ ${this.lastInitStack}` : "";
|
|
|
20558
20852
|
trackUri: this.uri,
|
|
20559
20853
|
abort: this.disposeAbort
|
|
20560
20854
|
});
|
|
20561
|
-
(_b = this.deps.textTrackController) == null ? void 0 : _b.
|
|
20855
|
+
(_b = this.deps.textTrackController) == null ? void 0 : _b.deactivate();
|
|
20562
20856
|
this.deps.playbackSource.src = null;
|
|
20563
20857
|
this.deps.playbackSource.load();
|
|
20564
20858
|
this.deps.mediaSourceController.deactivate();
|
|
@@ -21611,9 +21905,8 @@ ${this.lastInitStack}` : "";
|
|
|
21611
21905
|
qualitySelector: (deps2) => new QualitySelectorImpl({
|
|
21612
21906
|
options: deps2.options.pick("abr")
|
|
21613
21907
|
}),
|
|
21614
|
-
|
|
21908
|
+
audio: (deps2) => deps2.options.pick("audio"),
|
|
21615
21909
|
allowedContentTypes: (deps2) => deps2.options.pick("allowedContentTypes"),
|
|
21616
|
-
preferDescriptiveAudio: (deps2) => deps2.options.pick("preferDescriptiveAudio"),
|
|
21617
21910
|
abr: (deps2) => deps2.options.pick("abr"),
|
|
21618
21911
|
manifestTransformed: createDefaultDashManifestTransformer,
|
|
21619
21912
|
mediaTimeline: (deps2) => deps2.manifestTransformed.map(async (manifestPromise) => {
|
|
@@ -21623,26 +21916,21 @@ ${this.lastInitStack}` : "";
|
|
|
21623
21916
|
mediaTimelineTransformed: createDefaultMediaTimelineTransformer,
|
|
21624
21917
|
textTrackController: (deps2) => {
|
|
21625
21918
|
var _a3;
|
|
21626
|
-
|
|
21627
|
-
|
|
21919
|
+
return new SidecarTextTrackController({
|
|
21920
|
+
textTrackProvider: deps2.textTrackProvider,
|
|
21921
|
+
textTrackRenderer: deps2.textTrackRenderer,
|
|
21922
|
+
playbackController: deps2.playbackController,
|
|
21628
21923
|
requestInit: (_a3 = loadOptions.requestInit) != null ? _a3 : void 0,
|
|
21629
|
-
|
|
21630
|
-
|
|
21631
|
-
|
|
21632
|
-
|
|
21633
|
-
});
|
|
21634
|
-
deps2.manifestTransformed.onData((manifestPromise) => {
|
|
21635
|
-
manifestPromise.then((data2) => {
|
|
21636
|
-
controller.setTextTracks(
|
|
21637
|
-
discoverDashTextTracks(
|
|
21924
|
+
options: deps2.options.pick("text"),
|
|
21925
|
+
textTracks: deps2.manifestTransformed.map(
|
|
21926
|
+
(manifestPromise) => manifestPromise.then(
|
|
21927
|
+
(data2) => discoverDashTextTracks(
|
|
21638
21928
|
data2.manifest,
|
|
21639
21929
|
data2.baseUrl
|
|
21640
21930
|
)
|
|
21641
|
-
)
|
|
21642
|
-
|
|
21643
|
-
});
|
|
21931
|
+
)
|
|
21932
|
+
)
|
|
21644
21933
|
});
|
|
21645
|
-
return controller;
|
|
21646
21934
|
}
|
|
21647
21935
|
}));
|
|
21648
21936
|
};
|
|
@@ -23082,9 +23370,8 @@ ${this.lastInitStack}` : "";
|
|
|
23082
23370
|
qualitySelector: (deps2) => new QualitySelectorImpl({
|
|
23083
23371
|
options: deps2.options.pick("abr")
|
|
23084
23372
|
}),
|
|
23085
|
-
|
|
23373
|
+
audio: (deps2) => deps2.options.pick("audio"),
|
|
23086
23374
|
allowedContentTypes: (deps2) => deps2.options.pick("allowedContentTypes"),
|
|
23087
|
-
preferDescriptiveAudio: (deps2) => deps2.options.pick("preferDescriptiveAudio"),
|
|
23088
23375
|
abr: (deps2) => deps2.options.pick("abr"),
|
|
23089
23376
|
mediaTimeline: (deps2) => deps2.manifestTransformed.map(async (manifestPromise) => {
|
|
23090
23377
|
const data2 = await manifestPromise;
|
|
@@ -23093,26 +23380,21 @@ ${this.lastInitStack}` : "";
|
|
|
23093
23380
|
mediaTimelineTransformed: createDefaultMediaTimelineTransformer,
|
|
23094
23381
|
textTrackController: (deps2) => {
|
|
23095
23382
|
var _a3;
|
|
23096
|
-
|
|
23097
|
-
|
|
23383
|
+
return new SidecarTextTrackController({
|
|
23384
|
+
textTrackProvider: deps2.textTrackProvider,
|
|
23385
|
+
textTrackRenderer: deps2.textTrackRenderer,
|
|
23386
|
+
playbackController: deps2.playbackController,
|
|
23098
23387
|
requestInit: (_a3 = loadOptions.requestInit) != null ? _a3 : void 0,
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23102
|
-
|
|
23103
|
-
});
|
|
23104
|
-
deps2.manifestTransformed.onData((manifestPromise) => {
|
|
23105
|
-
manifestPromise.then((data2) => {
|
|
23106
|
-
controller.setTextTracks(
|
|
23107
|
-
discoverHlsTextTracks(
|
|
23388
|
+
options: deps2.options.pick("text"),
|
|
23389
|
+
textTracks: deps2.manifestTransformed.map(
|
|
23390
|
+
(manifestPromise) => manifestPromise.then(
|
|
23391
|
+
(data2) => discoverHlsTextTracks(
|
|
23108
23392
|
data2.mainPlaylist,
|
|
23109
23393
|
data2.baseUrl
|
|
23110
23394
|
)
|
|
23111
|
-
)
|
|
23112
|
-
|
|
23113
|
-
});
|
|
23395
|
+
)
|
|
23396
|
+
)
|
|
23114
23397
|
});
|
|
23115
|
-
return controller;
|
|
23116
23398
|
}
|
|
23117
23399
|
}));
|
|
23118
23400
|
};
|
|
@@ -23682,24 +23964,24 @@ ${this.lastInitStack}` : "";
|
|
|
23682
23964
|
var defaultVinylOptions = {
|
|
23683
23965
|
abr: defaultQualitySelectorImplOptions,
|
|
23684
23966
|
loudnessNormalization: defaultLoudnessNormalizationControllerImplOptions,
|
|
23685
|
-
|
|
23686
|
-
preferredTextLanguage: null,
|
|
23687
|
-
preferDescriptiveAudio: false,
|
|
23967
|
+
audio: defaultAudioOptions,
|
|
23688
23968
|
codecOverrides: {},
|
|
23689
23969
|
allowedContentTypes: null,
|
|
23690
|
-
|
|
23970
|
+
text: {
|
|
23971
|
+
// Forced-only captions in the platform's preferred languages (no
|
|
23972
|
+
// explicit language → navigator.languages).
|
|
23973
|
+
enabled: "forced"
|
|
23974
|
+
}
|
|
23691
23975
|
};
|
|
23692
23976
|
var vinylOptionsValidator = object({
|
|
23693
23977
|
abr: qualitySelectorImplOptionsValidator,
|
|
23694
23978
|
loudnessNormalization: loudnessNormalizationControllerImplOptionsValidator,
|
|
23695
|
-
|
|
23696
|
-
preferredTextLanguage: string().or(array(string()).readonly()).orNull(),
|
|
23697
|
-
preferDescriptiveAudio: boolean(),
|
|
23979
|
+
audio: audioOptionsValidator,
|
|
23698
23980
|
codecOverrides: record(string(), isOneOf("allow", "deny")),
|
|
23699
|
-
allowedContentTypes: array(isOneOf(...RESTRICTABLE_CONTENT_TYPES)).
|
|
23981
|
+
allowedContentTypes: array(isOneOf(...RESTRICTABLE_CONTENT_TYPES)).readonly().describe(
|
|
23700
23982
|
"Allow list of media content types (audio, video) to stream; other media streams are ignored. Text tracks are unaffected."
|
|
23701
23983
|
).orNull(),
|
|
23702
|
-
|
|
23984
|
+
text: textTrackControllerOptionsValidator
|
|
23703
23985
|
});
|
|
23704
23986
|
|
|
23705
23987
|
// src/vinyl/playerConfigFactory.ts
|
|
@@ -23731,6 +24013,14 @@ ${this.lastInitStack}` : "";
|
|
|
23731
24013
|
return patchMediaElement(options.media, patches.media);
|
|
23732
24014
|
},
|
|
23733
24015
|
media: (deps) => deps.patchedMedia.patched,
|
|
24016
|
+
// One provider per media element (the player container creates each
|
|
24017
|
+
// dependency once), so text tracks are reused rather than accumulated.
|
|
24018
|
+
textTrackProvider: (deps) => createMediaTextTrackProvider({ media: deps.media }),
|
|
24019
|
+
// App-provided cue renderer (or null for native rendering).
|
|
24020
|
+
textTrackRenderer: () => {
|
|
24021
|
+
var _a4;
|
|
24022
|
+
return (_a4 = options.textTrackRenderer) != null ? _a4 : null;
|
|
24023
|
+
},
|
|
23734
24024
|
playbackController: (deps) => new PlaybackControllerImpl(deps, options.playbackController),
|
|
23735
24025
|
playbackSource: (deps) => new PlaybackSourceImpl(deps),
|
|
23736
24026
|
loudnessNormalizationController: (deps) => new LoudnessNormalizationControllerImpl({
|
|
@@ -23853,10 +24143,6 @@ ${this.lastInitStack}` : "";
|
|
|
23853
24143
|
const previous = activeTrack;
|
|
23854
24144
|
if (previous === current) return;
|
|
23855
24145
|
activeTrack = current;
|
|
23856
|
-
const prevTextController = previous == null ? void 0 : previous.textTrackController;
|
|
23857
|
-
if (!this.deps.adController.currentAdBreak) {
|
|
23858
|
-
prevTextController == null ? void 0 : prevTextController.setActiveTextTrack(null);
|
|
23859
|
-
}
|
|
23860
24146
|
sub == null ? void 0 : sub();
|
|
23861
24147
|
sub = null;
|
|
23862
24148
|
textSub == null ? void 0 : textSub();
|
|
@@ -24273,16 +24559,6 @@ ${this.lastInitStack}` : "";
|
|
|
24273
24559
|
var _a3, _b, _c;
|
|
24274
24560
|
return (_c = (_b = (_a3 = this.activeTrack) == null ? void 0 : _a3.textTrackController) == null ? void 0 : _b.activeTextTrack) != null ? _c : null;
|
|
24275
24561
|
}
|
|
24276
|
-
/**
|
|
24277
|
-
* Selects the text track with the given id, or clears the active text
|
|
24278
|
-
* track when called with null. No-op when the current track does not
|
|
24279
|
-
* surface text tracks or the id is unknown.
|
|
24280
|
-
*/
|
|
24281
|
-
setActiveTextTrack(id) {
|
|
24282
|
-
var _a3;
|
|
24283
|
-
const controller = (_a3 = this.deps.trackController.activeTrack) == null ? void 0 : _a3.textTrackController;
|
|
24284
|
-
controller == null ? void 0 : controller.setActiveTextTrack(id);
|
|
24285
|
-
}
|
|
24286
24562
|
emitTextTrackChangeEventsFor(previous, current) {
|
|
24287
24563
|
var _a3, _b, _c, _d, _e, _f;
|
|
24288
24564
|
const prevList = (_a3 = previous == null ? void 0 : previous.textTrackController) == null ? void 0 : _a3.textTracks;
|
|
@@ -25910,9 +26186,9 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
25910
26186
|
super(...arguments);
|
|
25911
26187
|
__publicField(this, "textTracks", []);
|
|
25912
26188
|
__publicField(this, "activeTextTrack", null);
|
|
25913
|
-
__publicField(this, "
|
|
25914
|
-
__publicField(this, "
|
|
25915
|
-
__publicField(this, "
|
|
26189
|
+
__publicField(this, "active", false);
|
|
26190
|
+
__publicField(this, "deactivate", spyFactory25("deactivate"));
|
|
26191
|
+
__publicField(this, "activate", spyFactory25("activate"));
|
|
25916
26192
|
}
|
|
25917
26193
|
};
|
|
25918
26194
|
|
|
@@ -25950,9 +26226,10 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
25950
26226
|
mediaQualityMetadataResolver: createSpy5().and.callFake(
|
|
25951
26227
|
() => createEmptyMediaQualityMetadata()
|
|
25952
26228
|
),
|
|
25953
|
-
|
|
26229
|
+
audio: data({
|
|
26230
|
+
selection: { language: null, descriptive: false }
|
|
26231
|
+
}),
|
|
25954
26232
|
allowedContentTypes: data(null),
|
|
25955
|
-
preferDescriptiveAudio: data(false),
|
|
25956
26233
|
abr: data(defaultQualitySelectorImplOptions)
|
|
25957
26234
|
};
|
|
25958
26235
|
}
|
|
@@ -25972,6 +26249,7 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
25972
26249
|
var createSpy6 = jasmine.createSpy;
|
|
25973
26250
|
function createMockVinylDependencies() {
|
|
25974
26251
|
const playbackController = new MockPlaybackController();
|
|
26252
|
+
const media = createMockMedia();
|
|
25975
26253
|
return {
|
|
25976
26254
|
options: data(defaultVinylOptions),
|
|
25977
26255
|
trackController: new MockTrackController(),
|
|
@@ -25979,7 +26257,9 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
25979
26257
|
playbackController,
|
|
25980
26258
|
capabilities: new MockCapabilities(),
|
|
25981
26259
|
drmController: new MockDrmController(),
|
|
25982
|
-
media
|
|
26260
|
+
media,
|
|
26261
|
+
textTrackProvider: createMediaTextTrackProvider({ media }),
|
|
26262
|
+
textTrackRenderer: null,
|
|
25983
26263
|
createDashFactories: (_loadOptions) => externalDependencies(createMockDashDependencies()),
|
|
25984
26264
|
createHlsFactories: (_loadOptions) => externalDependencies(createMockDashDependencies()),
|
|
25985
26265
|
autoResetController: new MockAutoResetController(),
|
|
@@ -26098,9 +26378,10 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
26098
26378
|
mediaQualityMetadataResolver: createSpy7().and.callFake(
|
|
26099
26379
|
() => createEmptyMediaQualityMetadata()
|
|
26100
26380
|
),
|
|
26101
|
-
|
|
26381
|
+
audio: data({
|
|
26382
|
+
selection: { language: null, descriptive: false }
|
|
26383
|
+
}),
|
|
26102
26384
|
allowedContentTypes: data(null),
|
|
26103
|
-
preferDescriptiveAudio: data(false),
|
|
26104
26385
|
abr: data(defaultQualitySelectorImplOptions)
|
|
26105
26386
|
};
|
|
26106
26387
|
}
|
|
@@ -37145,9 +37426,13 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
37145
37426
|
drmController,
|
|
37146
37427
|
mediaTimeline: data(Promise.resolve(timeline)),
|
|
37147
37428
|
options: data({
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37429
|
+
audio: {
|
|
37430
|
+
selection: {
|
|
37431
|
+
language: preferredAudioLanguage,
|
|
37432
|
+
descriptive: preferDescriptiveAudio
|
|
37433
|
+
}
|
|
37434
|
+
},
|
|
37435
|
+
codecOverrides
|
|
37151
37436
|
})
|
|
37152
37437
|
};
|
|
37153
37438
|
}
|
|
@@ -37446,7 +37731,7 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
37446
37731
|
getDuration: () => Promise.resolve(Infinity)
|
|
37447
37732
|
};
|
|
37448
37733
|
const options = data({
|
|
37449
|
-
|
|
37734
|
+
audio: { selection: { language: "en" } }
|
|
37450
37735
|
});
|
|
37451
37736
|
const deps = {
|
|
37452
37737
|
capabilities,
|
|
@@ -37459,7 +37744,7 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
37459
37744
|
});
|
|
37460
37745
|
let result = await transformed.value;
|
|
37461
37746
|
expect(result.periods[0].qualities[0].metadata.qualityId).toBe("en");
|
|
37462
|
-
options.value = {
|
|
37747
|
+
options.value = { audio: { selection: { language: "ja" } } };
|
|
37463
37748
|
result = await transformed.value;
|
|
37464
37749
|
expect(result.periods[0].qualities[0].metadata.qualityId).toBe("ja");
|
|
37465
37750
|
unsub();
|
|
@@ -39209,9 +39494,9 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
39209
39494
|
it("suspends the controller on deactivate and resumes it on activate", () => {
|
|
39210
39495
|
track2 = createTrack();
|
|
39211
39496
|
track2.activate({});
|
|
39212
|
-
expect(deps.textTrackController.
|
|
39497
|
+
expect(deps.textTrackController.activate).toHaveBeenCalled();
|
|
39213
39498
|
track2.deactivate();
|
|
39214
|
-
expect(deps.textTrackController.
|
|
39499
|
+
expect(deps.textTrackController.deactivate).toHaveBeenCalled();
|
|
39215
39500
|
});
|
|
39216
39501
|
it("returns null (not throw) when read after disposal", () => {
|
|
39217
39502
|
const t = track2 = createTrack();
|
|
@@ -39414,18 +39699,24 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
39414
39699
|
expect(getAudioStream().reset).not.toHaveBeenCalled();
|
|
39415
39700
|
});
|
|
39416
39701
|
const allowed = ["audio"];
|
|
39702
|
+
const jaAudio = {
|
|
39703
|
+
selection: { language: "ja" }
|
|
39704
|
+
};
|
|
39705
|
+
const descriptiveAudio = {
|
|
39706
|
+
selection: { descriptive: true }
|
|
39707
|
+
};
|
|
39417
39708
|
for (const scenario of [
|
|
39418
39709
|
{
|
|
39419
|
-
name: "
|
|
39420
|
-
apply: () => deps.
|
|
39710
|
+
name: "audio language",
|
|
39711
|
+
apply: () => deps.audio.value = jaAudio
|
|
39421
39712
|
},
|
|
39422
39713
|
{
|
|
39423
39714
|
name: "allowedContentTypes",
|
|
39424
39715
|
apply: () => deps.allowedContentTypes.value = allowed
|
|
39425
39716
|
},
|
|
39426
39717
|
{
|
|
39427
|
-
name: "
|
|
39428
|
-
apply: () => deps.
|
|
39718
|
+
name: "audio descriptive",
|
|
39719
|
+
apply: () => deps.audio.value = descriptiveAudio
|
|
39429
39720
|
}
|
|
39430
39721
|
]) {
|
|
39431
39722
|
describe(`when ${scenario.name} changes`, () => {
|
|
@@ -40968,6 +41259,342 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
40968
41259
|
});
|
|
40969
41260
|
});
|
|
40970
41261
|
|
|
41262
|
+
// test/unit/text/HtmlTextTrackRenderer.test.ts
|
|
41263
|
+
describe("computeCueLayout", () => {
|
|
41264
|
+
it("flows a cue with no line/position (defaults: center, full width)", () => {
|
|
41265
|
+
expect(computeCueLayout({})).toEqual({
|
|
41266
|
+
textAlign: "center",
|
|
41267
|
+
maxWidth: "100%"
|
|
41268
|
+
});
|
|
41269
|
+
});
|
|
41270
|
+
it("maps align and size", () => {
|
|
41271
|
+
const layout = computeCueLayout({ align: "start", size: 80 });
|
|
41272
|
+
expect(layout.textAlign).toBe("start");
|
|
41273
|
+
expect(layout.maxWidth).toBe("80%");
|
|
41274
|
+
});
|
|
41275
|
+
it("maps vertical writing modes", () => {
|
|
41276
|
+
expect(computeCueLayout({ vertical: "rl" }).writingMode).toBe(
|
|
41277
|
+
"vertical-rl"
|
|
41278
|
+
);
|
|
41279
|
+
expect(computeCueLayout({ vertical: "lr" }).writingMode).toBe(
|
|
41280
|
+
"vertical-lr"
|
|
41281
|
+
);
|
|
41282
|
+
});
|
|
41283
|
+
it("centers horizontally and bottom-anchors when only a line is set", () => {
|
|
41284
|
+
const layout = computeCueLayout({ line: 40, snapToLines: false });
|
|
41285
|
+
expect(layout.position).toBe("absolute");
|
|
41286
|
+
expect(layout.left).toBe("50%");
|
|
41287
|
+
expect(layout.top).toBe("40%");
|
|
41288
|
+
expect(layout.transform).toBe("translateX(-50%)");
|
|
41289
|
+
});
|
|
41290
|
+
it("percentage line honors lineAlign end and center", () => {
|
|
41291
|
+
expect(
|
|
41292
|
+
computeCueLayout({ line: 20, snapToLines: false, lineAlign: "end" }).bottom
|
|
41293
|
+
).toBe("80%");
|
|
41294
|
+
const center = computeCueLayout({
|
|
41295
|
+
line: 30,
|
|
41296
|
+
snapToLines: false,
|
|
41297
|
+
lineAlign: "center"
|
|
41298
|
+
});
|
|
41299
|
+
expect(center.top).toBe("30%");
|
|
41300
|
+
expect(center.transform).toContain("translateY(-50%)");
|
|
41301
|
+
});
|
|
41302
|
+
it("snap-to-lines integer line: negative counts up from the bottom", () => {
|
|
41303
|
+
expect(computeCueLayout({ line: -3, snapToLines: true }).bottom).toBe(
|
|
41304
|
+
"3em"
|
|
41305
|
+
);
|
|
41306
|
+
expect(computeCueLayout({ line: 2, snapToLines: true }).top).toBe("3em");
|
|
41307
|
+
});
|
|
41308
|
+
it("positions horizontally by positionAlign", () => {
|
|
41309
|
+
expect(
|
|
41310
|
+
computeCueLayout({ position: 20, positionAlign: "line-left" }).left
|
|
41311
|
+
).toBe("20%");
|
|
41312
|
+
expect(
|
|
41313
|
+
computeCueLayout({ position: 20, positionAlign: "line-right" }).right
|
|
41314
|
+
).toBe("80%");
|
|
41315
|
+
const center = computeCueLayout({
|
|
41316
|
+
position: 30,
|
|
41317
|
+
positionAlign: "center"
|
|
41318
|
+
});
|
|
41319
|
+
expect(center.left).toBe("30%");
|
|
41320
|
+
expect(center.transform).toBe("translateX(-50%)");
|
|
41321
|
+
});
|
|
41322
|
+
it("a position without a line bottom-anchors", () => {
|
|
41323
|
+
expect(computeCueLayout({ position: 10 }).bottom).toBe("0");
|
|
41324
|
+
});
|
|
41325
|
+
});
|
|
41326
|
+
describe("renderCueText", () => {
|
|
41327
|
+
it("escapes plain text and turns newlines into <br>", () => {
|
|
41328
|
+
expect(renderCueText("a & b\nc")).toBe("a & b<br>c");
|
|
41329
|
+
});
|
|
41330
|
+
it("escapes a stray < that is not a tag", () => {
|
|
41331
|
+
expect(renderCueText("a < b")).toBe("a < b");
|
|
41332
|
+
});
|
|
41333
|
+
it("converts <c.class> to a styled span", () => {
|
|
41334
|
+
expect(renderCueText("<c.styledotitalic>hi</c>")).toBe(
|
|
41335
|
+
'<span class="styledotitalic">hi</span>'
|
|
41336
|
+
);
|
|
41337
|
+
});
|
|
41338
|
+
it("joins multiple cue classes", () => {
|
|
41339
|
+
expect(renderCueText("<c.a.b>x</c>")).toBe('<span class="a b">x</span>');
|
|
41340
|
+
});
|
|
41341
|
+
it("passes through i/b/u tags", () => {
|
|
41342
|
+
expect(renderCueText("<i>x</i><b>y</b><u>z</u>")).toBe(
|
|
41343
|
+
"<i>x</i><b>y</b><u>z</u>"
|
|
41344
|
+
);
|
|
41345
|
+
});
|
|
41346
|
+
it("maps voice and lang tags to spans with attributes", () => {
|
|
41347
|
+
expect(renderCueText("<v Bob>hi</v>")).toBe(
|
|
41348
|
+
'<span title="Bob">hi</span>'
|
|
41349
|
+
);
|
|
41350
|
+
expect(renderCueText("<lang en>x</lang>")).toBe(
|
|
41351
|
+
'<span lang="en">x</span>'
|
|
41352
|
+
);
|
|
41353
|
+
});
|
|
41354
|
+
it("renders voice/lang without an annotation as a bare span", () => {
|
|
41355
|
+
expect(renderCueText("<v>hi</v>")).toBe("<span>hi</span>");
|
|
41356
|
+
expect(renderCueText("<lang>x</lang>")).toBe("<span>x</span>");
|
|
41357
|
+
});
|
|
41358
|
+
it("escapes annotation values", () => {
|
|
41359
|
+
expect(renderCueText('<v a"b>x</v>')).toBe(
|
|
41360
|
+
'<span title="a"b">x</span>'
|
|
41361
|
+
);
|
|
41362
|
+
});
|
|
41363
|
+
it("drops timestamp tags and unknown tags", () => {
|
|
41364
|
+
expect(renderCueText("<00:00:01.000>a")).toBe("a");
|
|
41365
|
+
expect(renderCueText("<xyz>a</xyz>")).toBe("a");
|
|
41366
|
+
});
|
|
41367
|
+
});
|
|
41368
|
+
describe("HtmlTextTrackRenderer", () => {
|
|
41369
|
+
function fakeElement() {
|
|
41370
|
+
return {
|
|
41371
|
+
style: {},
|
|
41372
|
+
className: "",
|
|
41373
|
+
innerHTML: "",
|
|
41374
|
+
textContent: "",
|
|
41375
|
+
children: [],
|
|
41376
|
+
appendChild(node) {
|
|
41377
|
+
this.children.push(node);
|
|
41378
|
+
},
|
|
41379
|
+
replaceChildren(...nodes) {
|
|
41380
|
+
this.children = nodes;
|
|
41381
|
+
}
|
|
41382
|
+
};
|
|
41383
|
+
}
|
|
41384
|
+
function domChildren() {
|
|
41385
|
+
return Array.from(
|
|
41386
|
+
container.children
|
|
41387
|
+
);
|
|
41388
|
+
}
|
|
41389
|
+
function cueBoxes() {
|
|
41390
|
+
return domChildren().filter((c) => c.className === "vinyl-text-cue");
|
|
41391
|
+
}
|
|
41392
|
+
function styleElement() {
|
|
41393
|
+
return domChildren()[0];
|
|
41394
|
+
}
|
|
41395
|
+
class FakeTextTrack {
|
|
41396
|
+
constructor() {
|
|
41397
|
+
__publicField(this, "activeCues", []);
|
|
41398
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Map());
|
|
41399
|
+
__publicField(this, "addEventListener", jasmine.createSpy("addEventListener").and.callFake(
|
|
41400
|
+
(type, fn) => this.listeners.set(type, fn)
|
|
41401
|
+
));
|
|
41402
|
+
__publicField(this, "removeEventListener", jasmine.createSpy("removeEventListener").and.callFake((type) => this.listeners.delete(type)));
|
|
41403
|
+
}
|
|
41404
|
+
fireCueChange() {
|
|
41405
|
+
var _a3;
|
|
41406
|
+
(_a3 = this.listeners.get("cuechange")) == null ? void 0 : _a3();
|
|
41407
|
+
}
|
|
41408
|
+
}
|
|
41409
|
+
function cue(text, geometry = {}) {
|
|
41410
|
+
return __spreadValues({ text }, geometry);
|
|
41411
|
+
}
|
|
41412
|
+
let view;
|
|
41413
|
+
let container;
|
|
41414
|
+
let stubbedDocument = false;
|
|
41415
|
+
beforeEach(() => {
|
|
41416
|
+
if (typeof document === "undefined") {
|
|
41417
|
+
;
|
|
41418
|
+
globalThis.document = {
|
|
41419
|
+
createElement: () => fakeElement()
|
|
41420
|
+
};
|
|
41421
|
+
stubbedDocument = true;
|
|
41422
|
+
}
|
|
41423
|
+
view = new HtmlTextTrackRenderer();
|
|
41424
|
+
container = view.element;
|
|
41425
|
+
});
|
|
41426
|
+
afterEach(() => {
|
|
41427
|
+
view.dispose();
|
|
41428
|
+
if (stubbedDocument) {
|
|
41429
|
+
delete globalThis.document;
|
|
41430
|
+
stubbedDocument = false;
|
|
41431
|
+
}
|
|
41432
|
+
});
|
|
41433
|
+
it("creates a positioned overlay container", () => {
|
|
41434
|
+
expect(container.className).toBe("vinyl-text-container");
|
|
41435
|
+
expect(container.style.justifyContent).toBe("flex-end");
|
|
41436
|
+
expect(container.style.pointerEvents).toBe("none");
|
|
41437
|
+
});
|
|
41438
|
+
it("renders the active cues, escaping text and applying layout", () => {
|
|
41439
|
+
const track2 = new FakeTextTrack();
|
|
41440
|
+
track2.activeCues = [cue("hi\nthere", { align: "start" })];
|
|
41441
|
+
view.setTextTrack(track2);
|
|
41442
|
+
expect(track2.addEventListener).toHaveBeenCalledWith(
|
|
41443
|
+
"cuechange",
|
|
41444
|
+
jasmine.any(Function)
|
|
41445
|
+
);
|
|
41446
|
+
const boxes = cueBoxes();
|
|
41447
|
+
expect(boxes.length).toBe(1);
|
|
41448
|
+
expect(boxes[0].innerHTML).toBe("hi<br>there");
|
|
41449
|
+
expect(boxes[0].style.textAlign).toBe("start");
|
|
41450
|
+
});
|
|
41451
|
+
it("re-renders on cuechange", () => {
|
|
41452
|
+
const track2 = new FakeTextTrack();
|
|
41453
|
+
track2.activeCues = [];
|
|
41454
|
+
view.setTextTrack(track2);
|
|
41455
|
+
expect(cueBoxes().length).toBe(0);
|
|
41456
|
+
track2.activeCues = [cue("now showing")];
|
|
41457
|
+
track2.fireCueChange();
|
|
41458
|
+
expect(cueBoxes().length).toBe(1);
|
|
41459
|
+
});
|
|
41460
|
+
it("renders an empty box for a cue with no text", () => {
|
|
41461
|
+
const track2 = new FakeTextTrack();
|
|
41462
|
+
track2.activeCues = [{ align: "center" }];
|
|
41463
|
+
view.setTextTrack(track2);
|
|
41464
|
+
expect(cueBoxes()[0].innerHTML).toBe("");
|
|
41465
|
+
});
|
|
41466
|
+
it("handles a track with null activeCues", () => {
|
|
41467
|
+
const track2 = new FakeTextTrack();
|
|
41468
|
+
track2.activeCues = null;
|
|
41469
|
+
view.setTextTrack(track2);
|
|
41470
|
+
expect(cueBoxes().length).toBe(0);
|
|
41471
|
+
});
|
|
41472
|
+
it("is a no-op when set to the same track", () => {
|
|
41473
|
+
const track2 = new FakeTextTrack();
|
|
41474
|
+
view.setTextTrack(track2);
|
|
41475
|
+
view.setTextTrack(track2);
|
|
41476
|
+
expect(track2.addEventListener).toHaveBeenCalledTimes(1);
|
|
41477
|
+
});
|
|
41478
|
+
it("detaches and clears when set to null", () => {
|
|
41479
|
+
const track2 = new FakeTextTrack();
|
|
41480
|
+
track2.activeCues = [cue("bye")];
|
|
41481
|
+
view.setTextTrack(track2);
|
|
41482
|
+
view.setTextTrack(null);
|
|
41483
|
+
expect(track2.removeEventListener).toHaveBeenCalledWith(
|
|
41484
|
+
"cuechange",
|
|
41485
|
+
jasmine.any(Function)
|
|
41486
|
+
);
|
|
41487
|
+
expect(cueBoxes().length).toBe(0);
|
|
41488
|
+
});
|
|
41489
|
+
it("injects authored STYLE rules with ::cue selectors rewritten", () => {
|
|
41490
|
+
view.setStyles([
|
|
41491
|
+
"::cue(.loud) { font-weight: bold }\n::cue { color: red }"
|
|
41492
|
+
]);
|
|
41493
|
+
const style = styleElement();
|
|
41494
|
+
expect(style.textContent).toBe(
|
|
41495
|
+
".vinyl-text-cue .loud { font-weight: bold }\n.vinyl-text-cue { color: red }"
|
|
41496
|
+
);
|
|
41497
|
+
});
|
|
41498
|
+
it("clears authored styles when the track changes", () => {
|
|
41499
|
+
view.setStyles(["::cue { color: red }"]);
|
|
41500
|
+
const style = styleElement();
|
|
41501
|
+
expect(style.textContent).not.toBe("");
|
|
41502
|
+
view.setTextTrack(new FakeTextTrack());
|
|
41503
|
+
expect(style.textContent).toBe("");
|
|
41504
|
+
});
|
|
41505
|
+
it("dispose detaches the current track", () => {
|
|
41506
|
+
const track2 = new FakeTextTrack();
|
|
41507
|
+
view.setTextTrack(track2);
|
|
41508
|
+
view.dispose();
|
|
41509
|
+
expect(track2.removeEventListener).toHaveBeenCalled();
|
|
41510
|
+
});
|
|
41511
|
+
});
|
|
41512
|
+
|
|
41513
|
+
// test/unit/text/mediaTextTrackProvider.test.ts
|
|
41514
|
+
var FakeMediaElement = class {
|
|
41515
|
+
constructor() {
|
|
41516
|
+
__publicField(this, "tracks", []);
|
|
41517
|
+
__publicField(this, "addTextTrack", jasmine.createSpy("addTextTrack").and.callFake((kind, label, language) => {
|
|
41518
|
+
const track2 = {
|
|
41519
|
+
kind,
|
|
41520
|
+
label,
|
|
41521
|
+
language,
|
|
41522
|
+
mode: "disabled",
|
|
41523
|
+
cues: [],
|
|
41524
|
+
addCue(cue) {
|
|
41525
|
+
this.cues.push(cue);
|
|
41526
|
+
},
|
|
41527
|
+
removeCue(cue) {
|
|
41528
|
+
const i = this.cues.indexOf(cue);
|
|
41529
|
+
if (i >= 0) this.cues.splice(i, 1);
|
|
41530
|
+
}
|
|
41531
|
+
};
|
|
41532
|
+
this.tracks.push(track2);
|
|
41533
|
+
return track2;
|
|
41534
|
+
}));
|
|
41535
|
+
}
|
|
41536
|
+
// ArrayLike view over the live tracks (what `isAttached` scans).
|
|
41537
|
+
get textTracks() {
|
|
41538
|
+
return this.tracks;
|
|
41539
|
+
}
|
|
41540
|
+
asMedia() {
|
|
41541
|
+
return this;
|
|
41542
|
+
}
|
|
41543
|
+
cue() {
|
|
41544
|
+
return { id: "" };
|
|
41545
|
+
}
|
|
41546
|
+
};
|
|
41547
|
+
describe("createMediaTextTrackProvider", () => {
|
|
41548
|
+
let media;
|
|
41549
|
+
let provider;
|
|
41550
|
+
beforeEach(() => {
|
|
41551
|
+
media = new FakeMediaElement();
|
|
41552
|
+
provider = createMediaTextTrackProvider({ media: media.asMedia() });
|
|
41553
|
+
});
|
|
41554
|
+
it("creates a track from the given kind/label/language", () => {
|
|
41555
|
+
const ref = provider.getOrCreate("subtitles", "English", "en");
|
|
41556
|
+
expect(media.addTextTrack).toHaveBeenCalledOnceWith(
|
|
41557
|
+
"subtitles",
|
|
41558
|
+
"English",
|
|
41559
|
+
"en"
|
|
41560
|
+
);
|
|
41561
|
+
expect(media.tracks).toContain(ref.track);
|
|
41562
|
+
});
|
|
41563
|
+
it("memoizes by (kind,label,language) \u2014 same key returns the same track", () => {
|
|
41564
|
+
const a = provider.getOrCreate("subtitles", "English", "en");
|
|
41565
|
+
const b = provider.getOrCreate("subtitles", "English", "en");
|
|
41566
|
+
expect(b).toBe(a);
|
|
41567
|
+
expect(media.addTextTrack).toHaveBeenCalledTimes(1);
|
|
41568
|
+
});
|
|
41569
|
+
it("creates a distinct track per key", () => {
|
|
41570
|
+
provider.getOrCreate("subtitles", "English", "en");
|
|
41571
|
+
provider.getOrCreate("subtitles", "Espanol", "es");
|
|
41572
|
+
expect(media.addTextTrack).toHaveBeenCalledTimes(2);
|
|
41573
|
+
});
|
|
41574
|
+
it("recreates a track that was detached from the media element", () => {
|
|
41575
|
+
const first2 = provider.getOrCreate("subtitles", "English", "en");
|
|
41576
|
+
media.tracks.length = 0;
|
|
41577
|
+
const second = provider.getOrCreate("subtitles", "English", "en");
|
|
41578
|
+
expect(second).not.toBe(first2);
|
|
41579
|
+
expect(media.addTextTrack).toHaveBeenCalledTimes(2);
|
|
41580
|
+
expect(media.tracks).toContain(second.track);
|
|
41581
|
+
});
|
|
41582
|
+
it("clear() removes all cues from the track", () => {
|
|
41583
|
+
var _a3, _b;
|
|
41584
|
+
const ref = provider.getOrCreate("subtitles", "English", "en");
|
|
41585
|
+
ref.track.addCue(media.cue());
|
|
41586
|
+
ref.track.addCue(media.cue());
|
|
41587
|
+
expect((_a3 = ref.track.cues) == null ? void 0 : _a3.length).toBe(2);
|
|
41588
|
+
ref.clear();
|
|
41589
|
+
expect((_b = ref.track.cues) == null ? void 0 : _b.length).toBe(0);
|
|
41590
|
+
});
|
|
41591
|
+
it("clear() is a no-op when the track has no cues (cues null)", () => {
|
|
41592
|
+
const ref = provider.getOrCreate("subtitles", "English", "en");
|
|
41593
|
+
ref.track.cues = null;
|
|
41594
|
+
expect(() => ref.clear()).not.toThrow();
|
|
41595
|
+
});
|
|
41596
|
+
});
|
|
41597
|
+
|
|
40971
41598
|
// test/unit/text/parseWebVtt.test.ts
|
|
40972
41599
|
describe("parseWebVtt", () => {
|
|
40973
41600
|
it("parses a minimal cue", () => {
|
|
@@ -41076,15 +41703,30 @@ ok`;
|
|
|
41076
41703
|
const doc = parseWebVtt(text);
|
|
41077
41704
|
expect(doc.cues.length).toBe(1);
|
|
41078
41705
|
});
|
|
41079
|
-
it("
|
|
41706
|
+
it("captures STYLE block CSS while still parsing cues", () => {
|
|
41080
41707
|
const text = `WEBVTT
|
|
41081
41708
|
|
|
41082
41709
|
STYLE
|
|
41083
|
-
::cue {
|
|
41710
|
+
::cue(.loud) { font-weight: bold }
|
|
41711
|
+
|
|
41712
|
+
STYLE
|
|
41713
|
+
::cue { color: red }
|
|
41084
41714
|
|
|
41085
41715
|
00:00:01.000 --> 00:00:02.000
|
|
41086
41716
|
styled`;
|
|
41087
|
-
|
|
41717
|
+
const doc = parseWebVtt(text);
|
|
41718
|
+
expect(doc.cues.length).toBe(1);
|
|
41719
|
+
expect(doc.styles).toEqual([
|
|
41720
|
+
"::cue(.loud) { font-weight: bold }",
|
|
41721
|
+
"::cue { color: red }"
|
|
41722
|
+
]);
|
|
41723
|
+
});
|
|
41724
|
+
it("reports no styles when there are no STYLE blocks", () => {
|
|
41725
|
+
const text = `WEBVTT
|
|
41726
|
+
|
|
41727
|
+
00:00:01.000 --> 00:00:02.000
|
|
41728
|
+
x`;
|
|
41729
|
+
expect(parseWebVtt(text).styles).toEqual([]);
|
|
41088
41730
|
});
|
|
41089
41731
|
it("skips REGION blocks", () => {
|
|
41090
41732
|
const text = `WEBVTT
|
|
@@ -41095,14 +41737,96 @@ width:40%
|
|
|
41095
41737
|
|
|
41096
41738
|
00:00:01.000 --> 00:00:02.000
|
|
41097
41739
|
regional`;
|
|
41098
|
-
|
|
41740
|
+
const doc = parseWebVtt(text);
|
|
41741
|
+
expect(doc.cues.length).toBe(1);
|
|
41742
|
+
expect(doc.styles).toEqual([]);
|
|
41099
41743
|
});
|
|
41100
|
-
it("
|
|
41744
|
+
it("parses cue settings on the timing line", () => {
|
|
41101
41745
|
const text = `WEBVTT
|
|
41102
41746
|
|
|
41103
41747
|
00:00:01.000 --> 00:00:02.000 line:50% align:start
|
|
41104
41748
|
positioned`;
|
|
41105
|
-
|
|
41749
|
+
const cue = parseWebVtt(text).cues[0];
|
|
41750
|
+
expect(cue.text).toBe("positioned");
|
|
41751
|
+
expect(cue.settings).toEqual({
|
|
41752
|
+
line: 50,
|
|
41753
|
+
snapToLines: false,
|
|
41754
|
+
align: "start"
|
|
41755
|
+
});
|
|
41756
|
+
});
|
|
41757
|
+
it("parses integer/negative lines as snap-to-lines, with lineAlign", () => {
|
|
41758
|
+
const text = `WEBVTT
|
|
41759
|
+
|
|
41760
|
+
00:00:01.000 --> 00:00:02.000 line:-3,end
|
|
41761
|
+
a
|
|
41762
|
+
|
|
41763
|
+
00:00:03.000 --> 00:00:04.000 line:5
|
|
41764
|
+
b`;
|
|
41765
|
+
const [a, b] = parseWebVtt(text).cues;
|
|
41766
|
+
expect(a.settings).toEqual({
|
|
41767
|
+
line: -3,
|
|
41768
|
+
snapToLines: true,
|
|
41769
|
+
lineAlign: "end"
|
|
41770
|
+
});
|
|
41771
|
+
expect(b.settings).toEqual({ line: 5, snapToLines: true });
|
|
41772
|
+
});
|
|
41773
|
+
it("maps the legacy 'middle' keyword to 'center'", () => {
|
|
41774
|
+
const text = `WEBVTT
|
|
41775
|
+
|
|
41776
|
+
00:00:01.000 --> 00:00:02.000 align:middle line:85%,middle position:50%,middle
|
|
41777
|
+
a`;
|
|
41778
|
+
expect(parseWebVtt(text).cues[0].settings).toEqual({
|
|
41779
|
+
align: "center",
|
|
41780
|
+
line: 85,
|
|
41781
|
+
snapToLines: false,
|
|
41782
|
+
lineAlign: "center",
|
|
41783
|
+
position: 50,
|
|
41784
|
+
positionAlign: "center"
|
|
41785
|
+
});
|
|
41786
|
+
});
|
|
41787
|
+
it("parses position (with align), size and vertical", () => {
|
|
41788
|
+
const text = `WEBVTT
|
|
41789
|
+
|
|
41790
|
+
00:00:01.000 --> 00:00:02.000 position:20%,line-left size:80% vertical:rl
|
|
41791
|
+
a`;
|
|
41792
|
+
expect(parseWebVtt(text).cues[0].settings).toEqual({
|
|
41793
|
+
position: 20,
|
|
41794
|
+
positionAlign: "line-left",
|
|
41795
|
+
size: 80,
|
|
41796
|
+
vertical: "rl"
|
|
41797
|
+
});
|
|
41798
|
+
});
|
|
41799
|
+
it("omits settings and skips malformed/unknown tokens", () => {
|
|
41800
|
+
const text = `WEBVTT
|
|
41801
|
+
|
|
41802
|
+
00:00:01.000 --> 00:00:02.000
|
|
41803
|
+
plain
|
|
41804
|
+
|
|
41805
|
+
00:00:03.000 --> 00:00:04.000 align:bogus size:oops region:foo bareword
|
|
41806
|
+
b`;
|
|
41807
|
+
const [plain, b] = parseWebVtt(text).cues;
|
|
41808
|
+
expect(plain.settings).toBeUndefined();
|
|
41809
|
+
expect(b.settings).toBeUndefined();
|
|
41810
|
+
});
|
|
41811
|
+
it("skips malformed line/position/percent values and invalid aligns", () => {
|
|
41812
|
+
const text = `WEBVTT
|
|
41813
|
+
|
|
41814
|
+
00:00:01.000 --> 00:00:02.000 line:x% size:y%
|
|
41815
|
+
a
|
|
41816
|
+
|
|
41817
|
+
00:00:03.000 --> 00:00:04.000 line:1.5 position:bad
|
|
41818
|
+
b
|
|
41819
|
+
|
|
41820
|
+
00:00:05.000 --> 00:00:06.000 line:5,bogus position:20%,nope
|
|
41821
|
+
c`;
|
|
41822
|
+
const [a, b, c] = parseWebVtt(text).cues;
|
|
41823
|
+
expect(a.settings).toBeUndefined();
|
|
41824
|
+
expect(b.settings).toBeUndefined();
|
|
41825
|
+
expect(c.settings).toEqual({
|
|
41826
|
+
line: 5,
|
|
41827
|
+
snapToLines: true,
|
|
41828
|
+
position: 20
|
|
41829
|
+
});
|
|
41106
41830
|
});
|
|
41107
41831
|
it("skips a malformed cue and continues", () => {
|
|
41108
41832
|
const text = `WEBVTT
|
|
@@ -41117,6 +41841,114 @@ good`;
|
|
|
41117
41841
|
expect(cues.length).toBe(1);
|
|
41118
41842
|
expect(cues[0].text).toBe("good");
|
|
41119
41843
|
});
|
|
41844
|
+
it("skips a line with --> but an invalid start timestamp", () => {
|
|
41845
|
+
const text = `WEBVTT
|
|
41846
|
+
|
|
41847
|
+
BForced --> 00:00:02.000
|
|
41848
|
+
skipped
|
|
41849
|
+
|
|
41850
|
+
00:00:02.000 --> 00:00:03.000
|
|
41851
|
+
good`;
|
|
41852
|
+
const cues = parseWebVtt(text).cues;
|
|
41853
|
+
expect(cues.length).toBe(1);
|
|
41854
|
+
expect(cues[0].text).toBe("good");
|
|
41855
|
+
});
|
|
41856
|
+
it("ignores unparseable text glued after the end timestamp", () => {
|
|
41857
|
+
const cue = parseWebVtt(
|
|
41858
|
+
"WEBVTT\n\n00:00:01.000 --> 00:00:02.000x\ntext"
|
|
41859
|
+
).cues[0];
|
|
41860
|
+
expect(cue.endTime).toBe(2);
|
|
41861
|
+
expect(cue.settings).toBeUndefined();
|
|
41862
|
+
expect(cue.text).toBe("text");
|
|
41863
|
+
});
|
|
41864
|
+
it("parses multi-digit hours (spec allows two or more)", () => {
|
|
41865
|
+
const cues = parseWebVtt(
|
|
41866
|
+
"WEBVTT\n\n100:00:01.000 --> 100:00:02.500\nhi"
|
|
41867
|
+
).cues;
|
|
41868
|
+
expect(cues.length).toBe(1);
|
|
41869
|
+
expect(cues[0].startTime).toBe(360001);
|
|
41870
|
+
expect(cues[0].endTime).toBe(360002.5);
|
|
41871
|
+
});
|
|
41872
|
+
it("treats consecutive blank lines between cues as separators", () => {
|
|
41873
|
+
const text = "WEBVTT\n\n00:00:01.000 --> 00:00:02.000\none\n\n\n00:00:03.000 --> 00:00:04.000\ntwo";
|
|
41874
|
+
expect(parseWebVtt(text).cues.map((c) => c.text)).toEqual([
|
|
41875
|
+
"one",
|
|
41876
|
+
"two"
|
|
41877
|
+
]);
|
|
41878
|
+
});
|
|
41879
|
+
for (const timing of [
|
|
41880
|
+
"12 --> 00:00:02.000",
|
|
41881
|
+
// two digits then no ':'
|
|
41882
|
+
"7 --> 00:00:02.000",
|
|
41883
|
+
// one digit then no second digit
|
|
41884
|
+
"00:00:01.000 00:00:02.000",
|
|
41885
|
+
// no --> ⇒ treated as an identifier
|
|
41886
|
+
"5:30.000 --> 00:00:02.000",
|
|
41887
|
+
// MM:SS minutes not two digits
|
|
41888
|
+
"75:30.000 --> 00:00:02.000",
|
|
41889
|
+
// MM:SS minutes > 59
|
|
41890
|
+
"12:34 --> 00:00:02.000",
|
|
41891
|
+
// seconds not terminated by '.'
|
|
41892
|
+
"00:99:00.000 --> 00:00:02.000",
|
|
41893
|
+
// HH:MM:SS minutes > 59
|
|
41894
|
+
"00:00:99.000 --> 00:00:02.000",
|
|
41895
|
+
// HH:MM:SS seconds > 59
|
|
41896
|
+
"00:00:0.400 --> 00:00:02.000",
|
|
41897
|
+
// HH:MM:SS seconds not two digits
|
|
41898
|
+
"12:3:45.000 --> 00:00:02.000",
|
|
41899
|
+
// minutes not two digits before ':'
|
|
41900
|
+
"00:00:00 --> 00:00:02.000",
|
|
41901
|
+
// HH:MM:SS missing .mmm
|
|
41902
|
+
"00:00:00.00 --> 00:00:02.000",
|
|
41903
|
+
// milliseconds only two digits
|
|
41904
|
+
"00:00.0 --> 00:00:02.000",
|
|
41905
|
+
// milliseconds only one digit
|
|
41906
|
+
"00:00. --> 00:00:02.000",
|
|
41907
|
+
// milliseconds missing
|
|
41908
|
+
"12:ab --> 00:00:02.000",
|
|
41909
|
+
// non-digit minute/second
|
|
41910
|
+
"00:00:01.0"
|
|
41911
|
+
// truncated timestamp at end of input
|
|
41912
|
+
]) {
|
|
41913
|
+
it(`skips a line with an invalid timestamp: "${timing}"`, () => {
|
|
41914
|
+
const text = `WEBVTT
|
|
41915
|
+
|
|
41916
|
+
${timing}
|
|
41917
|
+
filler
|
|
41918
|
+
|
|
41919
|
+
00:00:05.000 --> 00:00:06.000
|
|
41920
|
+
ok`;
|
|
41921
|
+
expect(parseWebVtt(text).cues.map((c) => c.text)).toEqual(["ok"]);
|
|
41922
|
+
});
|
|
41923
|
+
}
|
|
41924
|
+
for (const identifier of ["NOTES", "REGIONAL", "STYLES"]) {
|
|
41925
|
+
it(`treats keyword prefix "${identifier}" as an identifier`, () => {
|
|
41926
|
+
const cues = parseWebVtt(
|
|
41927
|
+
`WEBVTT
|
|
41928
|
+
|
|
41929
|
+
${identifier}
|
|
41930
|
+
00:00:01.000 --> 00:00:02.000
|
|
41931
|
+
hi`
|
|
41932
|
+
).cues;
|
|
41933
|
+
expect(cues.length).toBe(1);
|
|
41934
|
+
expect(cues[0].id).toBe(identifier);
|
|
41935
|
+
});
|
|
41936
|
+
}
|
|
41937
|
+
it("normalizes CRLF line endings and splits CRLF-separated cues", () => {
|
|
41938
|
+
const text = "WEBVTT\r\n\r\n00:00:01.000 --> 00:00:02.000\r\nline one\r\nline two\r\n\r\n00:00:03.000 --> 00:00:04.000\r\nsecond";
|
|
41939
|
+
expect(parseWebVtt(text).cues.map((c) => c.text)).toEqual([
|
|
41940
|
+
"line one\nline two",
|
|
41941
|
+
"second"
|
|
41942
|
+
]);
|
|
41943
|
+
});
|
|
41944
|
+
it("accepts a NOTE block at end of input", () => {
|
|
41945
|
+
expect(parseWebVtt("WEBVTT\n\nNOTE").cues).toEqual([]);
|
|
41946
|
+
});
|
|
41947
|
+
it("handles input that ends mid-timestamp", () => {
|
|
41948
|
+
expect(parseWebVtt("WEBVTT\n\n00:00:00.000 --> 00:00:0").cues).toEqual(
|
|
41949
|
+
[]
|
|
41950
|
+
);
|
|
41951
|
+
});
|
|
41120
41952
|
it("returns empty cues on header-only input", () => {
|
|
41121
41953
|
expect(parseWebVtt("WEBVTT\n").cues).toEqual([]);
|
|
41122
41954
|
});
|
|
@@ -41193,7 +42025,21 @@ trailing-id`;
|
|
|
41193
42025
|
}, overrides);
|
|
41194
42026
|
}
|
|
41195
42027
|
describe("pickPreferredTextTrack", () => {
|
|
41196
|
-
it("
|
|
42028
|
+
it("falls back to navigator.languages when the preference is null", () => {
|
|
42029
|
+
setMockNavigator().languages = ["en"];
|
|
42030
|
+
const en = track({ id: "en", language: "en" });
|
|
42031
|
+
expect(pickPreferredTextTrack([en], null)).toBe(en);
|
|
42032
|
+
});
|
|
42033
|
+
it("returns null when no track relates to navigator.languages", () => {
|
|
42034
|
+
setMockNavigator().languages = ["en"];
|
|
42035
|
+
expect(
|
|
42036
|
+
pickPreferredTextTrack([track({ language: "ja" })], null)
|
|
42037
|
+
).toBeNull();
|
|
42038
|
+
});
|
|
42039
|
+
it("returns null for a null preference when navigator is unavailable", () => {
|
|
42040
|
+
spyOnPropertySafe(globalThis, "navigator").and.returnValue(
|
|
42041
|
+
void 0
|
|
42042
|
+
);
|
|
41197
42043
|
expect(pickPreferredTextTrack([track()], null)).toBeNull();
|
|
41198
42044
|
});
|
|
41199
42045
|
it("returns null when there are no tracks", () => {
|
|
@@ -41246,28 +42092,60 @@ trailing-id`;
|
|
|
41246
42092
|
async function flushUntil(predicate) {
|
|
41247
42093
|
for (let i = 0; i < 100 && !predicate(); i++) await flushPromises();
|
|
41248
42094
|
}
|
|
41249
|
-
var
|
|
42095
|
+
var FakeTextTrackProvider = class {
|
|
41250
42096
|
constructor() {
|
|
41251
|
-
|
|
42097
|
+
// Every distinct track created (one per rendition key).
|
|
42098
|
+
__publicField(this, "created", []);
|
|
42099
|
+
// The track handed out by the most recent getOrCreate (the active one).
|
|
42100
|
+
__publicField(this, "lastTrack", null);
|
|
42101
|
+
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
42102
|
+
__publicField(this, "getOrCreate", jasmine.createSpy("getOrCreate").and.callFake((kind, label, language) => {
|
|
42103
|
+
const key = `${kind}-${label}-${language}`;
|
|
42104
|
+
const existing = this.cache.get(key);
|
|
42105
|
+
if (existing) {
|
|
42106
|
+
this.lastTrack = existing.track;
|
|
42107
|
+
return existing;
|
|
42108
|
+
}
|
|
42109
|
+
const storedCues = [];
|
|
41252
42110
|
const track2 = {
|
|
41253
42111
|
kind,
|
|
41254
42112
|
label: label != null ? label : "",
|
|
41255
42113
|
language: language != null ? language : "",
|
|
41256
42114
|
mode: "disabled",
|
|
41257
|
-
|
|
42115
|
+
storedCues,
|
|
42116
|
+
// Real TextTrack.cues returns null when mode is 'disabled'.
|
|
42117
|
+
get cues() {
|
|
42118
|
+
return this.mode === "disabled" ? null : storedCues;
|
|
42119
|
+
},
|
|
41258
42120
|
addCue(cue) {
|
|
41259
|
-
|
|
42121
|
+
storedCues.push(cue);
|
|
41260
42122
|
},
|
|
41261
42123
|
removeCue(cue) {
|
|
41262
|
-
const i =
|
|
41263
|
-
if (i >= 0)
|
|
42124
|
+
const i = storedCues.indexOf(cue);
|
|
42125
|
+
if (i >= 0) storedCues.splice(i, 1);
|
|
42126
|
+
}
|
|
42127
|
+
};
|
|
42128
|
+
const ref = {
|
|
42129
|
+
track: track2,
|
|
42130
|
+
clear() {
|
|
42131
|
+
if (track2.mode === "disabled") return;
|
|
42132
|
+
storedCues.length = 0;
|
|
41264
42133
|
}
|
|
41265
42134
|
};
|
|
42135
|
+
this.created.push(track2);
|
|
42136
|
+
this.cache.set(key, ref);
|
|
41266
42137
|
this.lastTrack = track2;
|
|
41267
|
-
return
|
|
42138
|
+
return ref;
|
|
41268
42139
|
}));
|
|
41269
|
-
|
|
41270
|
-
|
|
42140
|
+
}
|
|
42141
|
+
/** Tracks currently rendering captions (showing with at least one cue). */
|
|
42142
|
+
showingWithCues() {
|
|
42143
|
+
return this.created.filter(
|
|
42144
|
+
(t) => {
|
|
42145
|
+
var _a3, _b;
|
|
42146
|
+
return t.mode === "showing" && ((_b = (_a3 = t.cues) == null ? void 0 : _a3.length) != null ? _b : 0) > 0;
|
|
42147
|
+
}
|
|
42148
|
+
);
|
|
41271
42149
|
}
|
|
41272
42150
|
};
|
|
41273
42151
|
function makeTrack(overrides = {}) {
|
|
@@ -41284,18 +42162,47 @@ trailing-id`;
|
|
|
41284
42162
|
}, overrides);
|
|
41285
42163
|
}
|
|
41286
42164
|
describe("SidecarTextTrackController", () => {
|
|
41287
|
-
let media;
|
|
41288
|
-
let controller;
|
|
41289
42165
|
let requester;
|
|
41290
42166
|
let originalCue;
|
|
42167
|
+
const disposers = [];
|
|
42168
|
+
function build(opts) {
|
|
42169
|
+
var _a3;
|
|
42170
|
+
const provider = new FakeTextTrackProvider();
|
|
42171
|
+
const textTracks$ = data(
|
|
42172
|
+
Promise.resolve([])
|
|
42173
|
+
);
|
|
42174
|
+
const options$ = data(
|
|
42175
|
+
(_a3 = opts == null ? void 0 : opts.options) != null ? _a3 : { selection: {} }
|
|
42176
|
+
);
|
|
42177
|
+
const playback = { currentTime: 0 };
|
|
42178
|
+
const controller = new SidecarTextTrackController({
|
|
42179
|
+
textTrackProvider: provider,
|
|
42180
|
+
textTracks: textTracks$,
|
|
42181
|
+
options: options$,
|
|
42182
|
+
playbackController: playback,
|
|
42183
|
+
requestInit: opts == null ? void 0 : opts.requestInit,
|
|
42184
|
+
textTrackRenderer: opts == null ? void 0 : opts.textTrackRenderer
|
|
42185
|
+
});
|
|
42186
|
+
if ((opts == null ? void 0 : opts.activate) !== false) controller.activate();
|
|
42187
|
+
disposers.push(() => controller.dispose());
|
|
42188
|
+
return {
|
|
42189
|
+
controller,
|
|
42190
|
+
provider,
|
|
42191
|
+
options$,
|
|
42192
|
+
textTracks$,
|
|
42193
|
+
playback,
|
|
42194
|
+
async setTracks(tracks) {
|
|
42195
|
+
textTracks$.value = Promise.resolve(tracks);
|
|
42196
|
+
await flushPromises();
|
|
42197
|
+
},
|
|
42198
|
+
select(selection) {
|
|
42199
|
+
options$.value = __spreadProps(__spreadValues({}, options$.value), { selection });
|
|
42200
|
+
}
|
|
42201
|
+
};
|
|
42202
|
+
}
|
|
41291
42203
|
beforeEach(() => {
|
|
41292
42204
|
requester = new MockRequester();
|
|
41293
42205
|
requesterWithRetryRef.set(() => requester);
|
|
41294
|
-
media = new FakeMediaElement();
|
|
41295
|
-
controller = new SidecarTextTrackController({
|
|
41296
|
-
media,
|
|
41297
|
-
cueStyle: data({ size: 90 })
|
|
41298
|
-
});
|
|
41299
42206
|
originalCue = globalThis.VTTCue;
|
|
41300
42207
|
globalThis.VTTCue = function VTTCue2(startTime, endTime, text) {
|
|
41301
42208
|
this.startTime = startTime;
|
|
@@ -41306,137 +42213,182 @@ trailing-id`;
|
|
|
41306
42213
|
};
|
|
41307
42214
|
});
|
|
41308
42215
|
afterEach(() => {
|
|
41309
|
-
|
|
42216
|
+
for (const dispose of disposers.splice(0)) dispose();
|
|
41310
42217
|
globalThis.VTTCue = originalCue;
|
|
41311
42218
|
});
|
|
41312
42219
|
function respondVtt(body) {
|
|
41313
42220
|
requester.request.and.resolveTo(new Response(body));
|
|
41314
42221
|
}
|
|
41315
42222
|
it("starts with no tracks and no active selection", () => {
|
|
42223
|
+
const { controller } = build();
|
|
41316
42224
|
expect(controller.textTracks).toEqual([]);
|
|
41317
42225
|
expect(controller.activeTextTrack).toBeNull();
|
|
41318
42226
|
});
|
|
41319
|
-
describe("
|
|
41320
|
-
|
|
42227
|
+
describe("resolveSelectedTrack", () => {
|
|
42228
|
+
const full = makeTrack({ id: "full", language: "en", forced: false });
|
|
42229
|
+
const forced = makeTrack({ id: "forced", language: "en", forced: true });
|
|
42230
|
+
const captions = makeTrack({
|
|
42231
|
+
id: "cc",
|
|
42232
|
+
language: "en",
|
|
42233
|
+
forced: false,
|
|
42234
|
+
kind: "captions"
|
|
42235
|
+
});
|
|
42236
|
+
it("renders nothing when enabled is 'off' (even with an id)", () => {
|
|
42237
|
+
expect(
|
|
42238
|
+
resolveSelectedTrack([full, forced], {
|
|
42239
|
+
enabled: "off",
|
|
42240
|
+
selection: { id: "full" }
|
|
42241
|
+
})
|
|
42242
|
+
).toBeNull();
|
|
42243
|
+
});
|
|
42244
|
+
it("enabled 'on' selects the full track", () => {
|
|
41321
42245
|
var _a3;
|
|
41322
|
-
|
|
41323
|
-
|
|
41324
|
-
|
|
41325
|
-
|
|
41326
|
-
|
|
42246
|
+
expect(
|
|
42247
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42248
|
+
enabled: "on",
|
|
42249
|
+
selection: { language: "en" }
|
|
42250
|
+
})) == null ? void 0 : _a3.id
|
|
42251
|
+
).toBe("full");
|
|
41327
42252
|
});
|
|
41328
|
-
it("
|
|
42253
|
+
it("enabled 'forced' selects the forced track", () => {
|
|
41329
42254
|
var _a3;
|
|
41330
|
-
|
|
41331
|
-
|
|
41332
|
-
|
|
41333
|
-
|
|
41334
|
-
|
|
41335
|
-
|
|
41336
|
-
default: true
|
|
41337
|
-
})
|
|
41338
|
-
]);
|
|
41339
|
-
expect((_a3 = controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("es-f");
|
|
42255
|
+
expect(
|
|
42256
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42257
|
+
enabled: "forced",
|
|
42258
|
+
selection: { language: "en" }
|
|
42259
|
+
})) == null ? void 0 : _a3.id
|
|
42260
|
+
).toBe("forced");
|
|
41340
42261
|
});
|
|
41341
|
-
it("
|
|
41342
|
-
|
|
41343
|
-
expect(
|
|
42262
|
+
it("selection.forced overrides the enabled mode", () => {
|
|
42263
|
+
var _a3;
|
|
42264
|
+
expect(
|
|
42265
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42266
|
+
enabled: "on",
|
|
42267
|
+
selection: { language: "en", forced: true }
|
|
42268
|
+
})) == null ? void 0 : _a3.id
|
|
42269
|
+
).toBe("forced");
|
|
41344
42270
|
});
|
|
41345
|
-
it("
|
|
41346
|
-
|
|
41347
|
-
|
|
41348
|
-
|
|
41349
|
-
|
|
41350
|
-
|
|
42271
|
+
it("an explicit id wins over other criteria", () => {
|
|
42272
|
+
var _a3;
|
|
42273
|
+
expect(
|
|
42274
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42275
|
+
selection: { id: "forced", language: "en", forced: false }
|
|
42276
|
+
})) == null ? void 0 : _a3.id
|
|
42277
|
+
).toBe("forced");
|
|
41351
42278
|
});
|
|
41352
|
-
it("
|
|
42279
|
+
it("unknown id selects nothing", () => {
|
|
42280
|
+
expect(
|
|
42281
|
+
resolveSelectedTrack([full], { selection: { id: "nope" } })
|
|
42282
|
+
).toBeNull();
|
|
42283
|
+
});
|
|
42284
|
+
it("forced defaults to true (only forced tracks)", () => {
|
|
41353
42285
|
var _a3;
|
|
41354
|
-
|
|
41355
|
-
|
|
41356
|
-
|
|
41357
|
-
|
|
41358
|
-
|
|
41359
|
-
|
|
41360
|
-
|
|
42286
|
+
expect(
|
|
42287
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42288
|
+
selection: { language: "en" }
|
|
42289
|
+
})) == null ? void 0 : _a3.id
|
|
42290
|
+
).toBe("forced");
|
|
42291
|
+
});
|
|
42292
|
+
it("forced:false selects the full track", () => {
|
|
42293
|
+
var _a3;
|
|
42294
|
+
expect(
|
|
42295
|
+
(_a3 = resolveSelectedTrack([full, forced], {
|
|
42296
|
+
selection: { language: "en", forced: false }
|
|
42297
|
+
})) == null ? void 0 : _a3.id
|
|
42298
|
+
).toBe("full");
|
|
42299
|
+
});
|
|
42300
|
+
it("kind restricts the candidate pool", () => {
|
|
42301
|
+
var _a3;
|
|
42302
|
+
expect(
|
|
42303
|
+
(_a3 = resolveSelectedTrack([full, captions], {
|
|
42304
|
+
selection: {
|
|
42305
|
+
language: "en",
|
|
42306
|
+
forced: false,
|
|
42307
|
+
kind: "captions"
|
|
42308
|
+
}
|
|
42309
|
+
})) == null ? void 0 : _a3.id
|
|
42310
|
+
).toBe("cc");
|
|
42311
|
+
});
|
|
42312
|
+
it("falls back to navigator.languages when no language is given", () => {
|
|
42313
|
+
var _a3;
|
|
42314
|
+
expect(
|
|
42315
|
+
(_a3 = resolveSelectedTrack([full], { selection: { forced: false } })) == null ? void 0 : _a3.id
|
|
42316
|
+
).toBe("full");
|
|
42317
|
+
});
|
|
42318
|
+
it("an ordered language list prefers earlier entries", () => {
|
|
42319
|
+
var _a3;
|
|
42320
|
+
const es = makeTrack({ id: "es", language: "es", forced: false });
|
|
42321
|
+
expect(
|
|
42322
|
+
(_a3 = resolveSelectedTrack([full, es], {
|
|
42323
|
+
selection: { language: ["es", "en"], forced: false }
|
|
42324
|
+
})) == null ? void 0 : _a3.id
|
|
42325
|
+
).toBe("es");
|
|
41361
42326
|
});
|
|
41362
|
-
});
|
|
41363
|
-
it("emits textTracksChange when discovered list changes", () => {
|
|
41364
|
-
const handler = jasmine.createSpy("textTracksChange");
|
|
41365
|
-
controller.on("textTracksChange", handler);
|
|
41366
|
-
const t = makeTrack();
|
|
41367
|
-
controller.setTextTracks([t]);
|
|
41368
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
41369
|
-
controller.setTextTracks([t]);
|
|
41370
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
41371
|
-
});
|
|
41372
|
-
it("detects changed track properties", () => {
|
|
41373
|
-
const handler = jasmine.createSpy();
|
|
41374
|
-
controller.on("textTracksChange", handler);
|
|
41375
|
-
controller.setTextTracks([makeTrack({ label: "A" })]);
|
|
41376
|
-
controller.setTextTracks([makeTrack({ label: "B" })]);
|
|
41377
|
-
expect(handler).toHaveBeenCalledTimes(2);
|
|
41378
42327
|
});
|
|
41379
42328
|
it("selecting a known id loads cues and creates a TextTrack", async () => {
|
|
41380
|
-
var _a3, _b, _c;
|
|
42329
|
+
var _a3, _b, _c, _d;
|
|
41381
42330
|
respondVtt(`WEBVTT
|
|
41382
42331
|
|
|
41383
42332
|
00:00:01.000 --> 00:00:02.000
|
|
41384
42333
|
first`);
|
|
41385
|
-
const
|
|
41386
|
-
controller.setTextTracks([t]);
|
|
42334
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41387
42335
|
const activeChange = jasmine.createSpy();
|
|
41388
|
-
controller.on("activeTextTrackChange", activeChange);
|
|
41389
|
-
|
|
41390
|
-
expect(controller.activeTextTrack).toBe(
|
|
42336
|
+
h.controller.on("activeTextTrackChange", activeChange);
|
|
42337
|
+
await h.setTracks([makeTrack()]);
|
|
42338
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
41391
42339
|
expect(activeChange).toHaveBeenCalledTimes(1);
|
|
41392
42340
|
await flushUntil(() => {
|
|
41393
|
-
var _a4, _b2;
|
|
41394
|
-
return ((_b2 = (_a4 =
|
|
42341
|
+
var _a4, _b2, _c2;
|
|
42342
|
+
return ((_c2 = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c2 : 0) > 0;
|
|
41395
42343
|
});
|
|
41396
|
-
expect(
|
|
42344
|
+
expect(h.provider.getOrCreate).toHaveBeenCalledWith(
|
|
41397
42345
|
"subtitles",
|
|
41398
42346
|
"English",
|
|
41399
42347
|
"en"
|
|
41400
42348
|
);
|
|
41401
|
-
expect((
|
|
41402
|
-
expect((
|
|
41403
|
-
const cue = (_c = media.lastTrack) == null ? void 0 : _c.cues[0];
|
|
41404
|
-
expect(cue.size).toBe(90);
|
|
42349
|
+
expect((_b = h.provider.lastTrack) == null ? void 0 : _b.mode).toBe("showing");
|
|
42350
|
+
expect((_d = (_c = h.provider.lastTrack) == null ? void 0 : _c.cues) == null ? void 0 : _d.length).toBe(1);
|
|
41405
42351
|
});
|
|
41406
|
-
describe("
|
|
41407
|
-
async
|
|
41408
|
-
var _a3;
|
|
42352
|
+
describe("authored cue settings", () => {
|
|
42353
|
+
it("applies the cue's own parsed settings to the VTTCue", async () => {
|
|
42354
|
+
var _a3, _b;
|
|
42355
|
+
respondVtt(
|
|
42356
|
+
`WEBVTT
|
|
42357
|
+
|
|
42358
|
+
00:00:01.000 --> 00:00:02.000 align:left size:80%
|
|
42359
|
+
x`
|
|
42360
|
+
);
|
|
42361
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42362
|
+
await h.setTracks([makeTrack()]);
|
|
42363
|
+
await flushUntil(
|
|
42364
|
+
() => {
|
|
42365
|
+
var _a4, _b2, _c;
|
|
42366
|
+
return ((_c = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c : 0) > 0;
|
|
42367
|
+
}
|
|
42368
|
+
);
|
|
42369
|
+
const cue = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b[0];
|
|
42370
|
+
expect(cue.align).toBe("left");
|
|
42371
|
+
expect(cue.size).toBe(80);
|
|
42372
|
+
});
|
|
42373
|
+
it("leaves the cue at defaults when it declares no settings", async () => {
|
|
42374
|
+
var _a3, _b;
|
|
41409
42375
|
respondVtt(`WEBVTT
|
|
41410
42376
|
|
|
41411
42377
|
00:00:01.000 --> 00:00:02.000
|
|
41412
|
-
|
|
41413
|
-
const
|
|
41414
|
-
|
|
41415
|
-
|
|
41416
|
-
|
|
41417
|
-
|
|
41418
|
-
|
|
41419
|
-
|
|
41420
|
-
|
|
41421
|
-
|
|
41422
|
-
it("does not style cues when cueStyle is null", async () => {
|
|
41423
|
-
const ctrl = new SidecarTextTrackController({
|
|
41424
|
-
media,
|
|
41425
|
-
cueStyle: data(null)
|
|
41426
|
-
});
|
|
41427
|
-
const cue = await loadFirstCue(ctrl);
|
|
41428
|
-
expect(cue.size).toBe(100);
|
|
41429
|
-
ctrl.dispose();
|
|
41430
|
-
});
|
|
41431
|
-
it("does not style cues when no cueStyle dep is provided", async () => {
|
|
41432
|
-
const ctrl = new SidecarTextTrackController({
|
|
41433
|
-
media
|
|
41434
|
-
});
|
|
41435
|
-
const cue = await loadFirstCue(ctrl);
|
|
42378
|
+
x`);
|
|
42379
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42380
|
+
await h.setTracks([makeTrack()]);
|
|
42381
|
+
await flushUntil(
|
|
42382
|
+
() => {
|
|
42383
|
+
var _a4, _b2, _c;
|
|
42384
|
+
return ((_c = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c : 0) > 0;
|
|
42385
|
+
}
|
|
42386
|
+
);
|
|
42387
|
+
const cue = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b[0];
|
|
41436
42388
|
expect(cue.size).toBe(100);
|
|
41437
|
-
ctrl.dispose();
|
|
41438
42389
|
});
|
|
41439
|
-
it("does not
|
|
42390
|
+
it("does not apply settings to non-VTTCue cues", async () => {
|
|
42391
|
+
var _a3, _b;
|
|
41440
42392
|
;
|
|
41441
42393
|
globalThis.VTTCue = void 0;
|
|
41442
42394
|
globalThis.TextTrackCue = function TextTrackCue(start, end, text) {
|
|
@@ -41446,31 +42398,190 @@ first`);
|
|
|
41446
42398
|
this.id = "";
|
|
41447
42399
|
this.size = 100;
|
|
41448
42400
|
};
|
|
41449
|
-
|
|
41450
|
-
|
|
41451
|
-
|
|
41452
|
-
|
|
41453
|
-
|
|
42401
|
+
respondVtt(
|
|
42402
|
+
`WEBVTT
|
|
42403
|
+
|
|
42404
|
+
00:00:01.000 --> 00:00:02.000 size:80%
|
|
42405
|
+
ignored`
|
|
42406
|
+
);
|
|
42407
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42408
|
+
await h.setTracks([makeTrack()]);
|
|
42409
|
+
await flushUntil(
|
|
42410
|
+
() => {
|
|
42411
|
+
var _a4, _b2, _c;
|
|
42412
|
+
return ((_c = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c : 0) > 0;
|
|
42413
|
+
}
|
|
42414
|
+
);
|
|
42415
|
+
const cue = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b[0];
|
|
41454
42416
|
expect(cue.size).toBe(100);
|
|
41455
|
-
ctrl.dispose();
|
|
41456
42417
|
globalThis.TextTrackCue = void 0;
|
|
41457
42418
|
});
|
|
41458
42419
|
});
|
|
42420
|
+
describe("text track renderer", () => {
|
|
42421
|
+
function fakeRenderer() {
|
|
42422
|
+
return {
|
|
42423
|
+
setTextTrack: jasmine.createSpy(
|
|
42424
|
+
"setTextTrack"
|
|
42425
|
+
),
|
|
42426
|
+
setStyles: jasmine.createSpy(
|
|
42427
|
+
"setStyles"
|
|
42428
|
+
)
|
|
42429
|
+
};
|
|
42430
|
+
}
|
|
42431
|
+
it("keeps the DOM track 'hidden' and hands it to the renderer", async () => {
|
|
42432
|
+
var _a3;
|
|
42433
|
+
respondVtt(`WEBVTT
|
|
42434
|
+
|
|
42435
|
+
00:00:01.000 --> 00:00:02.000
|
|
42436
|
+
x`);
|
|
42437
|
+
const renderer = fakeRenderer();
|
|
42438
|
+
const h = build({
|
|
42439
|
+
options: { selection: { id: "a" } },
|
|
42440
|
+
textTrackRenderer: renderer
|
|
42441
|
+
});
|
|
42442
|
+
await h.setTracks([makeTrack()]);
|
|
42443
|
+
await flushUntil(
|
|
42444
|
+
() => {
|
|
42445
|
+
var _a4, _b, _c;
|
|
42446
|
+
return ((_c = (_b = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
42447
|
+
}
|
|
42448
|
+
);
|
|
42449
|
+
expect((_a3 = h.provider.lastTrack) == null ? void 0 : _a3.mode).toBe("hidden");
|
|
42450
|
+
expect(renderer.setTextTrack).toHaveBeenCalledWith(
|
|
42451
|
+
h.provider.lastTrack
|
|
42452
|
+
);
|
|
42453
|
+
});
|
|
42454
|
+
it("detaches the renderer when captions are turned off", async () => {
|
|
42455
|
+
respondVtt(`WEBVTT
|
|
42456
|
+
|
|
42457
|
+
00:00:01.000 --> 00:00:02.000
|
|
42458
|
+
x`);
|
|
42459
|
+
const renderer = fakeRenderer();
|
|
42460
|
+
const h = build({
|
|
42461
|
+
options: { selection: { id: "a" } },
|
|
42462
|
+
textTrackRenderer: renderer
|
|
42463
|
+
});
|
|
42464
|
+
await h.setTracks([makeTrack()]);
|
|
42465
|
+
await flushUntil(
|
|
42466
|
+
() => {
|
|
42467
|
+
var _a3, _b, _c;
|
|
42468
|
+
return ((_c = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
42469
|
+
}
|
|
42470
|
+
);
|
|
42471
|
+
renderer.setTextTrack.calls.reset();
|
|
42472
|
+
h.options$.value = { enabled: "off" };
|
|
42473
|
+
expect(renderer.setTextTrack).toHaveBeenCalledWith(null);
|
|
42474
|
+
});
|
|
42475
|
+
it("forwards authored STYLE blocks to the renderer", async () => {
|
|
42476
|
+
respondVtt(
|
|
42477
|
+
`WEBVTT
|
|
42478
|
+
|
|
42479
|
+
STYLE
|
|
42480
|
+
::cue { color: red }
|
|
42481
|
+
|
|
42482
|
+
00:00:01.000 --> 00:00:02.000
|
|
42483
|
+
x`
|
|
42484
|
+
);
|
|
42485
|
+
const renderer = fakeRenderer();
|
|
42486
|
+
const h = build({
|
|
42487
|
+
options: { selection: { id: "a" } },
|
|
42488
|
+
textTrackRenderer: renderer
|
|
42489
|
+
});
|
|
42490
|
+
await h.setTracks([makeTrack()]);
|
|
42491
|
+
await flushUntil(() => renderer.setStyles.calls.count() > 0);
|
|
42492
|
+
expect(renderer.setStyles).toHaveBeenCalledWith([
|
|
42493
|
+
"::cue { color: red }"
|
|
42494
|
+
]);
|
|
42495
|
+
});
|
|
42496
|
+
it("ignores styles that arrive after the load is cancelled", async () => {
|
|
42497
|
+
let resolveFetch = () => {
|
|
42498
|
+
};
|
|
42499
|
+
requester.request.and.returnValue(
|
|
42500
|
+
new Promise((res) => {
|
|
42501
|
+
resolveFetch = res;
|
|
42502
|
+
})
|
|
42503
|
+
);
|
|
42504
|
+
const renderer = fakeRenderer();
|
|
42505
|
+
const h = build({
|
|
42506
|
+
options: { selection: { id: "a" } },
|
|
42507
|
+
textTrackRenderer: renderer
|
|
42508
|
+
});
|
|
42509
|
+
await h.setTracks([makeTrack()]);
|
|
42510
|
+
h.options$.value = { enabled: "off" };
|
|
42511
|
+
renderer.setStyles.calls.reset();
|
|
42512
|
+
resolveFetch(
|
|
42513
|
+
new Response(
|
|
42514
|
+
`WEBVTT
|
|
42515
|
+
|
|
42516
|
+
STYLE
|
|
42517
|
+
::cue { color: red }
|
|
42518
|
+
|
|
42519
|
+
00:00:01.000 --> 00:00:02.000
|
|
42520
|
+
x`
|
|
42521
|
+
)
|
|
42522
|
+
);
|
|
42523
|
+
await flushPromises();
|
|
42524
|
+
expect(renderer.setStyles).not.toHaveBeenCalled();
|
|
42525
|
+
});
|
|
42526
|
+
it("works with a renderer that cannot consume styles", async () => {
|
|
42527
|
+
respondVtt(
|
|
42528
|
+
`WEBVTT
|
|
42529
|
+
|
|
42530
|
+
STYLE
|
|
42531
|
+
::cue { color: red }
|
|
42532
|
+
|
|
42533
|
+
00:00:01.000 --> 00:00:02.000
|
|
42534
|
+
x`
|
|
42535
|
+
);
|
|
42536
|
+
const renderer = {
|
|
42537
|
+
setTextTrack: jasmine.createSpy(
|
|
42538
|
+
"setTextTrack"
|
|
42539
|
+
)
|
|
42540
|
+
};
|
|
42541
|
+
const h = build({
|
|
42542
|
+
options: { selection: { id: "a" } },
|
|
42543
|
+
textTrackRenderer: renderer
|
|
42544
|
+
});
|
|
42545
|
+
await h.setTracks([makeTrack()]);
|
|
42546
|
+
await flushUntil(
|
|
42547
|
+
() => {
|
|
42548
|
+
var _a3, _b, _c;
|
|
42549
|
+
return ((_c = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
42550
|
+
}
|
|
42551
|
+
);
|
|
42552
|
+
expect(renderer.setTextTrack).toHaveBeenCalled();
|
|
42553
|
+
});
|
|
42554
|
+
it("renders natively ('showing') when no renderer is injected", async () => {
|
|
42555
|
+
var _a3;
|
|
42556
|
+
respondVtt(`WEBVTT
|
|
42557
|
+
|
|
42558
|
+
00:00:01.000 --> 00:00:02.000
|
|
42559
|
+
x`);
|
|
42560
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42561
|
+
await h.setTracks([makeTrack()]);
|
|
42562
|
+
await flushUntil(
|
|
42563
|
+
() => {
|
|
42564
|
+
var _a4, _b, _c;
|
|
42565
|
+
return ((_c = (_b = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
42566
|
+
}
|
|
42567
|
+
);
|
|
42568
|
+
expect((_a3 = h.provider.lastTrack) == null ? void 0 : _a3.mode).toBe("showing");
|
|
42569
|
+
});
|
|
42570
|
+
});
|
|
41459
42571
|
it("preserves cue id when present", async () => {
|
|
41460
|
-
var _a3;
|
|
42572
|
+
var _a3, _b;
|
|
41461
42573
|
respondVtt(`WEBVTT
|
|
41462
42574
|
|
|
41463
42575
|
idA
|
|
41464
42576
|
00:00:01.000 --> 00:00:02.000
|
|
41465
42577
|
first`);
|
|
41466
|
-
const
|
|
41467
|
-
|
|
41468
|
-
controller.setActiveTextTrack(t.id);
|
|
42578
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42579
|
+
await h.setTracks([makeTrack()]);
|
|
41469
42580
|
await flushUntil(() => {
|
|
41470
|
-
var _a4,
|
|
41471
|
-
return ((
|
|
42581
|
+
var _a4, _b2, _c;
|
|
42582
|
+
return ((_c = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c : 0) > 0;
|
|
41472
42583
|
});
|
|
41473
|
-
const cue = (_a3 =
|
|
42584
|
+
const cue = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b[0];
|
|
41474
42585
|
expect(cue.id).toBe("idA");
|
|
41475
42586
|
});
|
|
41476
42587
|
it("dedupes cues that appear in more than one segment", async () => {
|
|
@@ -41504,63 +42615,67 @@ only-b`;
|
|
|
41504
42615
|
if (call === 2) return Promise.resolve(new Response(segA));
|
|
41505
42616
|
return Promise.resolve(new Response(segB));
|
|
41506
42617
|
});
|
|
41507
|
-
const
|
|
41508
|
-
|
|
41509
|
-
controller.setActiveTextTrack(t.id);
|
|
42618
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42619
|
+
await h.setTracks([makeTrack({ uri: "https://x.test/sub.m3u8" })]);
|
|
41510
42620
|
await flushUntil(() => {
|
|
41511
|
-
var _a4, _b2;
|
|
41512
|
-
return ((_b2 = (_a4 =
|
|
42621
|
+
var _a4, _b2, _c;
|
|
42622
|
+
return ((_c = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c : 0) >= 3;
|
|
41513
42623
|
});
|
|
41514
|
-
const texts = ((_b = (_a3 =
|
|
42624
|
+
const texts = ((_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) != null ? _b : []).map(
|
|
41515
42625
|
(c) => c.text
|
|
41516
42626
|
);
|
|
41517
42627
|
expect(texts).toEqual(["only-a", "shared", "only-b"]);
|
|
41518
42628
|
});
|
|
41519
|
-
it("selecting an unknown id
|
|
42629
|
+
it("selecting an unknown id renders nothing and stays inactive", async () => {
|
|
41520
42630
|
const handler = jasmine.createSpy();
|
|
41521
|
-
|
|
41522
|
-
controller.
|
|
41523
|
-
|
|
42631
|
+
const h = build({ options: { selection: { id: "unknown" } } });
|
|
42632
|
+
h.controller.on("activeTextTrackChange", handler);
|
|
42633
|
+
await h.setTracks([makeTrack()]);
|
|
41524
42634
|
expect(handler).not.toHaveBeenCalled();
|
|
41525
|
-
expect(controller.activeTextTrack).toBeNull();
|
|
42635
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
42636
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
41526
42637
|
});
|
|
41527
|
-
it("
|
|
42638
|
+
it("disabling clears the current active track", async () => {
|
|
42639
|
+
var _a3;
|
|
41528
42640
|
respondVtt(`WEBVTT
|
|
41529
42641
|
`);
|
|
41530
|
-
const
|
|
41531
|
-
|
|
41532
|
-
controller.
|
|
41533
|
-
|
|
41534
|
-
expect(controller.activeTextTrack).toBeNull();
|
|
42642
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42643
|
+
await h.setTracks([makeTrack()]);
|
|
42644
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
42645
|
+
h.options$.value = { enabled: "off" };
|
|
42646
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
41535
42647
|
});
|
|
41536
|
-
it("clears active selection when its track is removed", () => {
|
|
42648
|
+
it("clears active selection when its track is removed", async () => {
|
|
42649
|
+
var _a3;
|
|
41537
42650
|
respondVtt(`WEBVTT
|
|
41538
42651
|
`);
|
|
42652
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41539
42653
|
const t1 = makeTrack({ id: "a" });
|
|
41540
42654
|
const t2 = makeTrack({ id: "b", uri: "https://x.test/b.vtt" });
|
|
41541
|
-
|
|
41542
|
-
controller.
|
|
41543
|
-
|
|
41544
|
-
expect(controller.activeTextTrack).toBeNull();
|
|
42655
|
+
await h.setTracks([t1, t2]);
|
|
42656
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
42657
|
+
await h.setTracks([t2]);
|
|
42658
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
41545
42659
|
});
|
|
41546
|
-
it("keeps active selection when
|
|
41547
|
-
var _a3;
|
|
42660
|
+
it("keeps the active selection when the list updates with it still present", async () => {
|
|
42661
|
+
var _a3, _b;
|
|
41548
42662
|
respondVtt(`WEBVTT
|
|
41549
42663
|
`);
|
|
42664
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41550
42665
|
const t1 = makeTrack({ id: "a" });
|
|
41551
42666
|
const t2 = makeTrack({ id: "b" });
|
|
41552
|
-
|
|
41553
|
-
controller.
|
|
41554
|
-
|
|
41555
|
-
expect((
|
|
42667
|
+
await h.setTracks([t1]);
|
|
42668
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
42669
|
+
await h.setTracks([t1, t2]);
|
|
42670
|
+
expect((_b = h.controller.activeTextTrack) == null ? void 0 : _b.id).toBe("a");
|
|
41556
42671
|
});
|
|
41557
42672
|
it("emits textTrackError on fetch failure", async () => {
|
|
41558
42673
|
requester.request.and.rejectWith(new Error("boom"));
|
|
42674
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41559
42675
|
const onError = jasmine.createSpy("textTrackError");
|
|
41560
|
-
controller.on("textTrackError", onError);
|
|
42676
|
+
h.controller.on("textTrackError", onError);
|
|
41561
42677
|
const t = makeTrack();
|
|
41562
|
-
|
|
41563
|
-
controller.setActiveTextTrack(t.id);
|
|
42678
|
+
await h.setTracks([t]);
|
|
41564
42679
|
await flushUntil(() => onError.calls.count() > 0);
|
|
41565
42680
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
41566
42681
|
const arg = onError.calls.mostRecent().args[0];
|
|
@@ -41569,17 +42684,15 @@ only-b`;
|
|
|
41569
42684
|
});
|
|
41570
42685
|
it("wraps non-Error rejections", async () => {
|
|
41571
42686
|
requester.request.and.rejectWith("plain");
|
|
42687
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41572
42688
|
const onError = jasmine.createSpy();
|
|
41573
|
-
controller.on("textTrackError", onError);
|
|
41574
|
-
|
|
41575
|
-
controller.setTextTracks([t]);
|
|
41576
|
-
controller.setActiveTextTrack(t.id);
|
|
42689
|
+
h.controller.on("textTrackError", onError);
|
|
42690
|
+
await h.setTracks([makeTrack()]);
|
|
41577
42691
|
await flushUntil(() => onError.calls.count() > 0);
|
|
41578
|
-
|
|
41579
|
-
expect(arg.error.message).toBe("plain");
|
|
42692
|
+
expect(onError.calls.mostRecent().args[0].error.message).toBe("plain");
|
|
41580
42693
|
});
|
|
41581
42694
|
it("drops cues that arrive after the load has been cancelled", async () => {
|
|
41582
|
-
var _a3
|
|
42695
|
+
var _a3;
|
|
41583
42696
|
let resolveFetch = () => {
|
|
41584
42697
|
};
|
|
41585
42698
|
requester.request.and.returnValue(
|
|
@@ -41587,10 +42700,10 @@ only-b`;
|
|
|
41587
42700
|
resolveFetch = res;
|
|
41588
42701
|
})
|
|
41589
42702
|
);
|
|
41590
|
-
const
|
|
41591
|
-
|
|
41592
|
-
|
|
41593
|
-
|
|
42703
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42704
|
+
await h.setTracks([makeTrack()]);
|
|
42705
|
+
const track2 = h.provider.lastTrack;
|
|
42706
|
+
h.options$.value = { enabled: "off" };
|
|
41594
42707
|
resolveFetch(
|
|
41595
42708
|
new Response(`WEBVTT
|
|
41596
42709
|
|
|
@@ -41598,9 +42711,9 @@ only-b`;
|
|
|
41598
42711
|
post`)
|
|
41599
42712
|
);
|
|
41600
42713
|
await flushPromises();
|
|
41601
|
-
expect((
|
|
42714
|
+
expect((_a3 = track2 == null ? void 0 : track2.storedCues.length) != null ? _a3 : 0).toBe(0);
|
|
41602
42715
|
});
|
|
41603
|
-
it("does not emit textTrackError when load is cancelled by switching
|
|
42716
|
+
it("does not emit textTrackError when a load is cancelled by switching", async () => {
|
|
41604
42717
|
let rejectFirst = () => {
|
|
41605
42718
|
};
|
|
41606
42719
|
let callCount = 0;
|
|
@@ -41611,17 +42724,16 @@ post`)
|
|
|
41611
42724
|
else rej(new Error("unused"));
|
|
41612
42725
|
})
|
|
41613
42726
|
);
|
|
42727
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
41614
42728
|
const onError = jasmine.createSpy("textTrackError");
|
|
41615
|
-
controller.on("textTrackError", onError);
|
|
41616
|
-
|
|
41617
|
-
|
|
41618
|
-
controller.setActiveTextTrack(t.id);
|
|
41619
|
-
controller.setActiveTextTrack(null);
|
|
42729
|
+
h.controller.on("textTrackError", onError);
|
|
42730
|
+
await h.setTracks([makeTrack()]);
|
|
42731
|
+
h.options$.value = { enabled: "off" };
|
|
41620
42732
|
rejectFirst(new Error("would-be error"));
|
|
41621
42733
|
await flushPromises();
|
|
41622
42734
|
expect(onError).not.toHaveBeenCalled();
|
|
41623
42735
|
});
|
|
41624
|
-
it("switching
|
|
42736
|
+
it("switching language reuses the per-rendition track and swaps its cues", async () => {
|
|
41625
42737
|
var _a3;
|
|
41626
42738
|
let call = 0;
|
|
41627
42739
|
requester.request.and.callFake(() => {
|
|
@@ -41638,33 +42750,65 @@ two`
|
|
|
41638
42750
|
)
|
|
41639
42751
|
);
|
|
41640
42752
|
});
|
|
41641
|
-
const
|
|
41642
|
-
const
|
|
41643
|
-
id: "
|
|
41644
|
-
|
|
41645
|
-
label: "
|
|
41646
|
-
|
|
41647
|
-
|
|
41648
|
-
|
|
42753
|
+
const en = makeTrack({ id: "en", language: "en", label: "English" });
|
|
42754
|
+
const es = makeTrack({
|
|
42755
|
+
id: "es",
|
|
42756
|
+
language: "es",
|
|
42757
|
+
label: "Espanol",
|
|
42758
|
+
uri: "https://x.test/es.vtt"
|
|
42759
|
+
});
|
|
42760
|
+
const h = build({ options: { selection: { id: "en" } } });
|
|
42761
|
+
await h.setTracks([en, es]);
|
|
41649
42762
|
await flushUntil(() => {
|
|
41650
|
-
var _a4, _b;
|
|
41651
|
-
return ((_b = (_a4 =
|
|
42763
|
+
var _a4, _b, _c;
|
|
42764
|
+
return ((_c = (_b = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
41652
42765
|
});
|
|
41653
|
-
const
|
|
41654
|
-
|
|
41655
|
-
|
|
41656
|
-
expect(
|
|
41657
|
-
expect(firstTrack == null ? void 0 : firstTrack.mode).toBe("disabled");
|
|
42766
|
+
const enTrack = h.provider.lastTrack;
|
|
42767
|
+
h.select({ id: "es" });
|
|
42768
|
+
expect(enTrack == null ? void 0 : enTrack.storedCues.length).toBe(0);
|
|
42769
|
+
expect(enTrack == null ? void 0 : enTrack.mode).toBe("disabled");
|
|
41658
42770
|
await flushUntil(
|
|
41659
42771
|
() => {
|
|
41660
|
-
var _a4, _b;
|
|
41661
|
-
return
|
|
42772
|
+
var _a4, _b, _c;
|
|
42773
|
+
return h.provider.lastTrack !== enTrack && ((_c = (_b = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
41662
42774
|
}
|
|
41663
42775
|
);
|
|
41664
|
-
expect(
|
|
42776
|
+
expect(h.provider.lastTrack).not.toBe(enTrack);
|
|
42777
|
+
expect((_a3 = h.provider.lastTrack) == null ? void 0 : _a3.language).toBe("es");
|
|
42778
|
+
expect(h.provider.showingWithCues().length).toBe(1);
|
|
41665
42779
|
});
|
|
41666
|
-
it("
|
|
41667
|
-
|
|
42780
|
+
it("cycling languages never accumulates or double-shows cues", async () => {
|
|
42781
|
+
requester.request.and.callFake(
|
|
42782
|
+
() => Promise.resolve(
|
|
42783
|
+
new Response(`WEBVTT
|
|
42784
|
+
|
|
42785
|
+
00:00:01.000 --> 00:00:02.000
|
|
42786
|
+
cue`)
|
|
42787
|
+
)
|
|
42788
|
+
);
|
|
42789
|
+
const langs = ["en", "es", "ja"].map(
|
|
42790
|
+
(language) => makeTrack({
|
|
42791
|
+
id: language,
|
|
42792
|
+
language,
|
|
42793
|
+
uri: `https://x.test/${language}.vtt`
|
|
42794
|
+
})
|
|
42795
|
+
);
|
|
42796
|
+
const h = build({ options: { selection: { id: "en" } } });
|
|
42797
|
+
await h.setTracks(langs);
|
|
42798
|
+
for (const id of ["en", "es", "ja", "en"]) {
|
|
42799
|
+
h.select({ id });
|
|
42800
|
+
await flushUntil(
|
|
42801
|
+
() => {
|
|
42802
|
+
var _a3, _b, _c;
|
|
42803
|
+
return ((_c = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
42804
|
+
}
|
|
42805
|
+
);
|
|
42806
|
+
expect(h.provider.showingWithCues().length).withContext(`after selecting ${id}`).toBe(1);
|
|
42807
|
+
}
|
|
42808
|
+
expect(h.provider.created.length).toBe(3);
|
|
42809
|
+
});
|
|
42810
|
+
it("deactivate hides the DOM track and activate rebuilds it, keeping the selection", async () => {
|
|
42811
|
+
var _a3, _b, _c, _d;
|
|
41668
42812
|
requester.request.and.callFake(
|
|
41669
42813
|
() => Promise.resolve(
|
|
41670
42814
|
new Response(`WEBVTT
|
|
@@ -41673,96 +42817,94 @@ two`
|
|
|
41673
42817
|
one`)
|
|
41674
42818
|
)
|
|
41675
42819
|
);
|
|
41676
|
-
const
|
|
41677
|
-
|
|
41678
|
-
controller.setActiveTextTrack(t.id);
|
|
42820
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42821
|
+
await h.setTracks([makeTrack()]);
|
|
41679
42822
|
await flushUntil(() => {
|
|
41680
|
-
var _a4, _b2;
|
|
41681
|
-
return ((_b2 = (_a4 =
|
|
41682
|
-
});
|
|
41683
|
-
const
|
|
41684
|
-
expect(
|
|
41685
|
-
controller.
|
|
41686
|
-
|
|
41687
|
-
expect(
|
|
41688
|
-
expect(
|
|
41689
|
-
|
|
41690
|
-
|
|
41691
|
-
|
|
41692
|
-
|
|
41693
|
-
|
|
41694
|
-
|
|
41695
|
-
|
|
41696
|
-
|
|
41697
|
-
expect((
|
|
42823
|
+
var _a4, _b2, _c2;
|
|
42824
|
+
return ((_c2 = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c2 : 0) > 0;
|
|
42825
|
+
});
|
|
42826
|
+
const track2 = h.provider.lastTrack;
|
|
42827
|
+
expect(track2 == null ? void 0 : track2.mode).toBe("showing");
|
|
42828
|
+
expect(h.controller.active).toBeTrue();
|
|
42829
|
+
h.controller.deactivate();
|
|
42830
|
+
expect(h.controller.active).toBeFalse();
|
|
42831
|
+
expect(track2 == null ? void 0 : track2.storedCues.length).toBe(0);
|
|
42832
|
+
expect(track2 == null ? void 0 : track2.mode).toBe("disabled");
|
|
42833
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
42834
|
+
h.controller.activate();
|
|
42835
|
+
expect(h.controller.active).toBeTrue();
|
|
42836
|
+
await flushUntil(() => {
|
|
42837
|
+
var _a4, _b2, _c2;
|
|
42838
|
+
return ((_c2 = (_b2 = (_a4 = h.provider.lastTrack) == null ? void 0 : _a4.cues) == null ? void 0 : _b2.length) != null ? _c2 : 0) > 0;
|
|
42839
|
+
});
|
|
42840
|
+
expect((_b = h.provider.lastTrack) == null ? void 0 : _b.mode).toBe("showing");
|
|
42841
|
+
expect((_d = (_c = h.provider.lastTrack) == null ? void 0 : _c.cues) == null ? void 0 : _d.length).toBe(1);
|
|
41698
42842
|
});
|
|
41699
|
-
it("
|
|
41700
|
-
|
|
41701
|
-
|
|
42843
|
+
it("activate is a no-op when nothing is selected", () => {
|
|
42844
|
+
const h = build();
|
|
42845
|
+
h.controller.activate();
|
|
42846
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
41702
42847
|
});
|
|
41703
|
-
it("
|
|
42848
|
+
it("activate is a no-op when already rendering", async () => {
|
|
41704
42849
|
respondVtt(`WEBVTT
|
|
41705
42850
|
|
|
41706
42851
|
00:00:01.000 --> 00:00:02.000
|
|
41707
42852
|
one`);
|
|
41708
|
-
const
|
|
41709
|
-
|
|
41710
|
-
controller.setActiveTextTrack(t.id);
|
|
42853
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42854
|
+
await h.setTracks([makeTrack()]);
|
|
41711
42855
|
await flushUntil(() => {
|
|
41712
|
-
var _a3, _b;
|
|
41713
|
-
return ((_b = (_a3 =
|
|
42856
|
+
var _a3, _b, _c;
|
|
42857
|
+
return ((_c = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b.length) != null ? _c : 0) > 0;
|
|
41714
42858
|
});
|
|
41715
|
-
|
|
41716
|
-
controller.
|
|
41717
|
-
expect(
|
|
42859
|
+
h.provider.getOrCreate.calls.reset();
|
|
42860
|
+
h.controller.activate();
|
|
42861
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
41718
42862
|
});
|
|
41719
|
-
it("
|
|
42863
|
+
it("does not render while deactivated even as the selection resolves", async () => {
|
|
41720
42864
|
var _a3;
|
|
41721
|
-
;
|
|
41722
|
-
globalThis.VTTCue = void 0;
|
|
41723
42865
|
respondVtt(`WEBVTT
|
|
41724
42866
|
|
|
41725
42867
|
00:00:01.000 --> 00:00:02.000
|
|
41726
|
-
|
|
41727
|
-
const
|
|
41728
|
-
|
|
41729
|
-
|
|
41730
|
-
|
|
41731
|
-
|
|
42868
|
+
one`);
|
|
42869
|
+
const h = build({
|
|
42870
|
+
options: { selection: { id: "a" } },
|
|
42871
|
+
activate: false
|
|
42872
|
+
});
|
|
42873
|
+
h.controller.deactivate();
|
|
42874
|
+
await h.setTracks([makeTrack()]);
|
|
42875
|
+
expect((_a3 = h.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("a");
|
|
42876
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
41732
42877
|
});
|
|
41733
|
-
it("
|
|
41734
|
-
|
|
41735
|
-
|
|
42878
|
+
it("skips DOM cue creation when no VTTCue ctor is available", async () => {
|
|
42879
|
+
var _a3, _b, _c;
|
|
42880
|
+
;
|
|
42881
|
+
globalThis.VTTCue = void 0;
|
|
41736
42882
|
respondVtt(`WEBVTT
|
|
41737
42883
|
|
|
41738
42884
|
00:00:01.000 --> 00:00:02.000
|
|
41739
|
-
|
|
41740
|
-
|
|
41741
|
-
|
|
42885
|
+
ignored`);
|
|
42886
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42887
|
+
await h.setTracks([makeTrack()]);
|
|
41742
42888
|
await flushPromises();
|
|
41743
|
-
expect(
|
|
41744
|
-
headless.dispose();
|
|
42889
|
+
expect((_c = (_b = (_a3 = h.provider.lastTrack) == null ? void 0 : _a3.cues) == null ? void 0 : _b.length) != null ? _c : 0).toBe(0);
|
|
41745
42890
|
});
|
|
41746
42891
|
it("forwards requestInit when configured", async () => {
|
|
41747
|
-
const customController = new SidecarTextTrackController({
|
|
41748
|
-
media,
|
|
41749
|
-
requestInit: { credentials: "include" }
|
|
41750
|
-
});
|
|
41751
42892
|
respondVtt(`WEBVTT
|
|
41752
42893
|
`);
|
|
42894
|
+
const h = build({
|
|
42895
|
+
options: { selection: { id: "a" } },
|
|
42896
|
+
requestInit: { credentials: "include" }
|
|
42897
|
+
});
|
|
41753
42898
|
const t = makeTrack();
|
|
41754
|
-
|
|
41755
|
-
customController.setActiveTextTrack(t.id);
|
|
42899
|
+
await h.setTracks([t]);
|
|
41756
42900
|
await flushPromises();
|
|
41757
42901
|
expect(requester.request).toHaveBeenCalledWith(
|
|
41758
42902
|
t.uri,
|
|
41759
42903
|
jasmine.objectContaining({ credentials: "include" }),
|
|
41760
42904
|
jasmine.any(Object)
|
|
41761
42905
|
);
|
|
41762
|
-
customController.dispose();
|
|
41763
42906
|
});
|
|
41764
42907
|
it("drives HLS caption segment order from the media playhead", async () => {
|
|
41765
|
-
media.currentTime = 12;
|
|
41766
42908
|
const playlistBody = `#EXTM3U
|
|
41767
42909
|
#EXT-X-TARGETDURATION:5
|
|
41768
42910
|
#EXTINF:5.0,
|
|
@@ -41789,14 +42931,13 @@ d.vtt
|
|
|
41789
42931
|
${path}`)
|
|
41790
42932
|
);
|
|
41791
42933
|
});
|
|
41792
|
-
const
|
|
41793
|
-
|
|
41794
|
-
|
|
42934
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42935
|
+
h.playback.currentTime = 12;
|
|
42936
|
+
await h.setTracks([makeTrack({ uri: "https://x.test/sub.m3u8" })]);
|
|
41795
42937
|
await flushUntil(() => fetched.length > 0);
|
|
41796
42938
|
expect(fetched[0]).toBe("c.vtt");
|
|
41797
42939
|
});
|
|
41798
42940
|
it("dispose aborts in-flight loads and clears state", async () => {
|
|
41799
|
-
var _a3, _b;
|
|
41800
42941
|
let resolveResponse = () => {
|
|
41801
42942
|
};
|
|
41802
42943
|
requester.request.and.callFake(
|
|
@@ -41804,11 +42945,11 @@ ${path}`)
|
|
|
41804
42945
|
resolveResponse = res;
|
|
41805
42946
|
})
|
|
41806
42947
|
);
|
|
41807
|
-
const
|
|
41808
|
-
|
|
41809
|
-
|
|
41810
|
-
expect(
|
|
41811
|
-
controller.dispose();
|
|
42948
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42949
|
+
await h.setTracks([makeTrack()]);
|
|
42950
|
+
const track2 = h.provider.lastTrack;
|
|
42951
|
+
expect(track2).not.toBeNull();
|
|
42952
|
+
h.controller.dispose();
|
|
41812
42953
|
resolveResponse(
|
|
41813
42954
|
new Response(`WEBVTT
|
|
41814
42955
|
|
|
@@ -41816,103 +42957,42 @@ ${path}`)
|
|
|
41816
42957
|
post`)
|
|
41817
42958
|
);
|
|
41818
42959
|
await flushPromises();
|
|
41819
|
-
expect(
|
|
41820
|
-
expect(
|
|
41821
|
-
expect(controller.activeTextTrack).toBeNull();
|
|
41822
|
-
expect(controller.textTracks).toEqual([]);
|
|
42960
|
+
expect(track2 == null ? void 0 : track2.mode).toBe("disabled");
|
|
42961
|
+
expect(track2 == null ? void 0 : track2.storedCues.length).toBe(0);
|
|
42962
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
42963
|
+
expect(h.controller.textTracks).toEqual([]);
|
|
41823
42964
|
});
|
|
41824
|
-
it("
|
|
42965
|
+
it("creates the DOM track with an undefined language for null-language tracks", async () => {
|
|
41825
42966
|
respondVtt(`WEBVTT
|
|
41826
42967
|
`);
|
|
41827
|
-
const
|
|
41828
|
-
|
|
41829
|
-
controller.setActiveTextTrack(t.id);
|
|
42968
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42969
|
+
await h.setTracks([makeTrack({ language: null })]);
|
|
41830
42970
|
await flushPromises();
|
|
41831
|
-
expect(
|
|
42971
|
+
expect(h.provider.getOrCreate).toHaveBeenCalledWith(
|
|
41832
42972
|
"subtitles",
|
|
41833
42973
|
"English",
|
|
41834
42974
|
void 0
|
|
41835
42975
|
);
|
|
41836
42976
|
});
|
|
41837
|
-
it("
|
|
42977
|
+
it("setting the same resolved track list twice emits textTracksChange once", async () => {
|
|
41838
42978
|
const handler = jasmine.createSpy();
|
|
41839
|
-
|
|
42979
|
+
const h = build();
|
|
42980
|
+
h.controller.on("textTracksChange", handler);
|
|
41840
42981
|
const list = [makeTrack()];
|
|
41841
|
-
|
|
41842
|
-
|
|
42982
|
+
await h.setTracks(list);
|
|
42983
|
+
await h.setTracks([makeTrack()]);
|
|
41843
42984
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
41844
42985
|
});
|
|
41845
|
-
it("
|
|
41846
|
-
const handler = jasmine.createSpy();
|
|
41847
|
-
controller.on("activeTextTrackChange", handler);
|
|
41848
|
-
controller.setActiveTextTrack(null);
|
|
41849
|
-
expect(handler).not.toHaveBeenCalled();
|
|
41850
|
-
});
|
|
41851
|
-
it("selecting same id is a no-op", () => {
|
|
42986
|
+
it("re-selecting the same id is a no-op", async () => {
|
|
41852
42987
|
respondVtt(`WEBVTT
|
|
41853
42988
|
`);
|
|
41854
|
-
const
|
|
42989
|
+
const h = build({ options: { selection: { id: "a" } } });
|
|
42990
|
+
await h.setTracks([makeTrack()]);
|
|
41855
42991
|
const handler = jasmine.createSpy();
|
|
41856
|
-
controller.
|
|
41857
|
-
|
|
41858
|
-
controller.on("activeTextTrackChange", handler);
|
|
41859
|
-
controller.setActiveTextTrack(t.id);
|
|
42992
|
+
h.controller.on("activeTextTrackChange", handler);
|
|
42993
|
+
h.select({ id: "a" });
|
|
41860
42994
|
expect(handler).not.toHaveBeenCalled();
|
|
41861
42995
|
});
|
|
41862
|
-
describe("preferredTextLanguage", () => {
|
|
41863
|
-
const en = makeTrack({ id: "en", language: "en", label: "English" });
|
|
41864
|
-
const es = makeTrack({ id: "es", language: "es", label: "Espanol" });
|
|
41865
|
-
let preferredTextLanguage;
|
|
41866
|
-
let prefController;
|
|
41867
|
-
beforeEach(() => {
|
|
41868
|
-
preferredTextLanguage = data(
|
|
41869
|
-
null
|
|
41870
|
-
);
|
|
41871
|
-
prefController = new SidecarTextTrackController({
|
|
41872
|
-
media: null,
|
|
41873
|
-
preferredTextLanguage
|
|
41874
|
-
});
|
|
41875
|
-
});
|
|
41876
|
-
afterEach(() => prefController.dispose());
|
|
41877
|
-
it("selects the matching track when the preference changes", () => {
|
|
41878
|
-
var _a3;
|
|
41879
|
-
prefController.setTextTracks([en, es]);
|
|
41880
|
-
expect(prefController.activeTextTrack).toBeNull();
|
|
41881
|
-
preferredTextLanguage.value = "es";
|
|
41882
|
-
expect((_a3 = prefController.activeTextTrack) == null ? void 0 : _a3.id).toBe("es");
|
|
41883
|
-
});
|
|
41884
|
-
it("clears the selection when the preference is set back to null", () => {
|
|
41885
|
-
var _a3;
|
|
41886
|
-
prefController.setTextTracks([en, es]);
|
|
41887
|
-
preferredTextLanguage.value = "es";
|
|
41888
|
-
expect((_a3 = prefController.activeTextTrack) == null ? void 0 : _a3.id).toBe("es");
|
|
41889
|
-
preferredTextLanguage.value = null;
|
|
41890
|
-
expect(prefController.activeTextTrack).toBeNull();
|
|
41891
|
-
});
|
|
41892
|
-
it("re-applies the preference when the track list changes", () => {
|
|
41893
|
-
var _a3;
|
|
41894
|
-
preferredTextLanguage.value = "es";
|
|
41895
|
-
expect(prefController.activeTextTrack).toBeNull();
|
|
41896
|
-
prefController.setTextTracks([en, es]);
|
|
41897
|
-
expect((_a3 = prefController.activeTextTrack) == null ? void 0 : _a3.id).toBe("es");
|
|
41898
|
-
});
|
|
41899
|
-
it("a preferred language wins over forced auto-selection", () => {
|
|
41900
|
-
var _a3;
|
|
41901
|
-
preferredTextLanguage.value = "es";
|
|
41902
|
-
prefController.setTextTracks([
|
|
41903
|
-
makeTrack({ id: "en-forced", language: "en", forced: true }),
|
|
41904
|
-
es
|
|
41905
|
-
]);
|
|
41906
|
-
expect((_a3 = prefController.activeTextTrack) == null ? void 0 : _a3.id).toBe("es");
|
|
41907
|
-
});
|
|
41908
|
-
it("leaves forced auto-selection intact while no preference is set", () => {
|
|
41909
|
-
var _a3;
|
|
41910
|
-
prefController.setTextTracks([
|
|
41911
|
-
makeTrack({ id: "forced", forced: true })
|
|
41912
|
-
]);
|
|
41913
|
-
expect((_a3 = prefController.activeTextTrack) == null ? void 0 : _a3.id).toBe("forced");
|
|
41914
|
-
});
|
|
41915
|
-
});
|
|
41916
42996
|
});
|
|
41917
42997
|
|
|
41918
42998
|
// test/unit/text/SidecarTextTrackLoader.test.ts
|
|
@@ -41996,6 +43076,39 @@ from-segment`;
|
|
|
41996
43076
|
await loadWebVttCues("https://x.test/sub.txt", options);
|
|
41997
43077
|
expect(batches.flat().map((c) => c.text)).toEqual(["from-segment"]);
|
|
41998
43078
|
});
|
|
43079
|
+
it("forwards STYLE blocks parsed from a segment via onStyles", async () => {
|
|
43080
|
+
const playlistText = `#EXTM3U
|
|
43081
|
+
#EXT-X-TARGETDURATION:10
|
|
43082
|
+
#EXTINF:10.0,
|
|
43083
|
+
seg.vtt
|
|
43084
|
+
#EXT-X-ENDLIST
|
|
43085
|
+
`;
|
|
43086
|
+
const segmentText = `WEBVTT
|
|
43087
|
+
|
|
43088
|
+
STYLE
|
|
43089
|
+
::cue { color: red }
|
|
43090
|
+
|
|
43091
|
+
00:00:01.000 --> 00:00:02.000
|
|
43092
|
+
from-segment`;
|
|
43093
|
+
let call = 0;
|
|
43094
|
+
requester.request.and.callFake(() => {
|
|
43095
|
+
call++;
|
|
43096
|
+
if (call === 1) {
|
|
43097
|
+
return respondText(
|
|
43098
|
+
playlistText,
|
|
43099
|
+
"application/vnd.apple.mpegurl",
|
|
43100
|
+
"https://x.test/sub.m3u8"
|
|
43101
|
+
);
|
|
43102
|
+
}
|
|
43103
|
+
return respondText(segmentText, "text/vtt");
|
|
43104
|
+
});
|
|
43105
|
+
const styleBatches = [];
|
|
43106
|
+
const { options } = streamingOpts({
|
|
43107
|
+
onStyles: (styles) => styleBatches.push([...styles])
|
|
43108
|
+
});
|
|
43109
|
+
await loadWebVttCues("https://x.test/sub.m3u8", options);
|
|
43110
|
+
expect(styleBatches).toEqual([["::cue { color: red }"]]);
|
|
43111
|
+
});
|
|
41999
43112
|
it("falls back to the request URI when response.url is empty (body sniffed)", async () => {
|
|
42000
43113
|
const playlistText = `#EXTM3U
|
|
42001
43114
|
#EXT-X-VERSION:3
|
|
@@ -43370,13 +44483,8 @@ x`);
|
|
|
43370
44483
|
});
|
|
43371
44484
|
const deps = createContainer(factories).dependencies;
|
|
43372
44485
|
const options = dashFactoryDeps.options.value;
|
|
43373
|
-
expect(deps.
|
|
43374
|
-
options.preferredAudioLanguage
|
|
43375
|
-
);
|
|
44486
|
+
expect(deps.audio.value).toBe(options.audio);
|
|
43376
44487
|
expect(deps.allowedContentTypes.value).toBe(options.allowedContentTypes);
|
|
43377
|
-
expect(deps.preferDescriptiveAudio.value).toBe(
|
|
43378
|
-
options.preferDescriptiveAudio
|
|
43379
|
-
);
|
|
43380
44488
|
expect(deps.abr.value).toBe(options.abr);
|
|
43381
44489
|
});
|
|
43382
44490
|
it("provides streaming options to createContentStreamFactories", () => {
|
|
@@ -46840,9 +47948,8 @@ x`);
|
|
|
46840
47948
|
drmController,
|
|
46841
47949
|
mediaTimeline: data(Promise.resolve(timeline)),
|
|
46842
47950
|
options: data({
|
|
46843
|
-
|
|
46844
|
-
codecOverrides: {}
|
|
46845
|
-
preferDescriptiveAudio: false
|
|
47951
|
+
audio: { selection: { language: null, descriptive: false } },
|
|
47952
|
+
codecOverrides: {}
|
|
46846
47953
|
})
|
|
46847
47954
|
}).value;
|
|
46848
47955
|
const audio = result.periods[0].qualities.filter(
|
|
@@ -47336,13 +48443,8 @@ x`);
|
|
|
47336
48443
|
});
|
|
47337
48444
|
const deps = createContainer(factories).dependencies;
|
|
47338
48445
|
const options = hlsFactoryDeps.options.value;
|
|
47339
|
-
expect(deps.
|
|
47340
|
-
options.preferredAudioLanguage
|
|
47341
|
-
);
|
|
48446
|
+
expect(deps.audio.value).toBe(options.audio);
|
|
47342
48447
|
expect(deps.allowedContentTypes.value).toBe(options.allowedContentTypes);
|
|
47343
|
-
expect(deps.preferDescriptiveAudio.value).toBe(
|
|
47344
|
-
options.preferDescriptiveAudio
|
|
47345
|
-
);
|
|
47346
48448
|
expect(deps.abr.value).toBe(options.abr);
|
|
47347
48449
|
});
|
|
47348
48450
|
it("exposes the shared adController without owning it", () => {
|
|
@@ -51686,6 +52788,8 @@ x`);
|
|
|
51686
52788
|
objectContaining25({
|
|
51687
52789
|
patchedMedia: any30(Object),
|
|
51688
52790
|
media: any30(Object),
|
|
52791
|
+
textTrackProvider: any30(Object),
|
|
52792
|
+
textTrackRenderer: null,
|
|
51689
52793
|
playbackController: any30(PlaybackControllerImpl),
|
|
51690
52794
|
playbackSource: any30(PlaybackSourceImpl),
|
|
51691
52795
|
loudnessNormalizationController: any30(
|
|
@@ -52839,228 +53943,145 @@ x`);
|
|
|
52839
53943
|
afterEach(() => {
|
|
52840
53944
|
player.dispose();
|
|
52841
53945
|
});
|
|
53946
|
+
function info(overrides = {}) {
|
|
53947
|
+
return __spreadValues({
|
|
53948
|
+
id: "t1",
|
|
53949
|
+
kind: "subtitles",
|
|
53950
|
+
language: "en",
|
|
53951
|
+
label: "A",
|
|
53952
|
+
default: false,
|
|
53953
|
+
forced: false,
|
|
53954
|
+
characteristics: [],
|
|
53955
|
+
uri: "a.vtt",
|
|
53956
|
+
mimeType: "text/vtt"
|
|
53957
|
+
}, overrides);
|
|
53958
|
+
}
|
|
52842
53959
|
function makeTrackWithController() {
|
|
52843
53960
|
const track2 = new MockTrack();
|
|
52844
|
-
const controller = new
|
|
53961
|
+
const controller = new MockTextTrackController();
|
|
52845
53962
|
track2.textTrackController = controller;
|
|
52846
53963
|
return { track: track2, controller };
|
|
52847
53964
|
}
|
|
53965
|
+
function setTracks(controller, tracks) {
|
|
53966
|
+
const previous = controller.textTracks;
|
|
53967
|
+
controller.textTracks = tracks;
|
|
53968
|
+
controller.dispatch("textTracksChange", { previous, current: tracks });
|
|
53969
|
+
}
|
|
53970
|
+
function setActive(controller, current) {
|
|
53971
|
+
const previous = controller.activeTextTrack;
|
|
53972
|
+
controller.activeTextTrack = current;
|
|
53973
|
+
controller.dispatch("activeTextTrackChange", { previous, current });
|
|
53974
|
+
}
|
|
53975
|
+
function activate(track2) {
|
|
53976
|
+
deps.trackController.activeTrack = track2;
|
|
53977
|
+
deps.trackController.dispatch("trackActivated", { track: track2 });
|
|
53978
|
+
}
|
|
52848
53979
|
it("returns empty defaults when no current track", () => {
|
|
52849
53980
|
expect(player.textTracks).toEqual([]);
|
|
52850
53981
|
expect(player.activeTextTrack).toBeNull();
|
|
52851
53982
|
});
|
|
52852
53983
|
it("returns text tracks from the current track", () => {
|
|
52853
53984
|
const { track: track2, controller } = makeTrackWithController();
|
|
52854
|
-
controller.
|
|
52855
|
-
|
|
52856
|
-
id: "t1",
|
|
52857
|
-
kind: "subtitles",
|
|
52858
|
-
language: "en",
|
|
52859
|
-
label: "English",
|
|
52860
|
-
default: false,
|
|
52861
|
-
forced: false,
|
|
52862
|
-
characteristics: [],
|
|
52863
|
-
uri: "https://x/sub.vtt",
|
|
52864
|
-
mimeType: "text/vtt"
|
|
52865
|
-
}
|
|
52866
|
-
]);
|
|
52867
|
-
deps.trackController.activeTrack = track2;
|
|
52868
|
-
deps.trackController.dispatch("trackActivated", { track: track2 });
|
|
53985
|
+
controller.textTracks = [info()];
|
|
53986
|
+
activate(track2);
|
|
52869
53987
|
expect(player.textTracks.length).toBe(1);
|
|
52870
53988
|
});
|
|
52871
|
-
it("
|
|
53989
|
+
it("reflects the current track controller active selection", () => {
|
|
52872
53990
|
var _a3;
|
|
52873
53991
|
const { track: track2, controller } = makeTrackWithController();
|
|
52874
|
-
controller.
|
|
52875
|
-
|
|
52876
|
-
|
|
52877
|
-
kind: "subtitles",
|
|
52878
|
-
language: "en",
|
|
52879
|
-
label: "English",
|
|
52880
|
-
default: false,
|
|
52881
|
-
forced: false,
|
|
52882
|
-
characteristics: [],
|
|
52883
|
-
uri: "https://x/sub.vtt",
|
|
52884
|
-
mimeType: "text/vtt"
|
|
52885
|
-
}
|
|
52886
|
-
]);
|
|
52887
|
-
deps.trackController.activeTrack = track2;
|
|
52888
|
-
deps.trackController.dispatch("trackActivated", { track: track2 });
|
|
52889
|
-
player.setActiveTextTrack("t1");
|
|
53992
|
+
controller.textTracks = [info()];
|
|
53993
|
+
activate(track2);
|
|
53994
|
+
setActive(controller, info());
|
|
52890
53995
|
expect((_a3 = player.activeTextTrack) == null ? void 0 : _a3.id).toBe("t1");
|
|
52891
|
-
|
|
53996
|
+
setActive(controller, null);
|
|
52892
53997
|
expect(player.activeTextTrack).toBeNull();
|
|
52893
53998
|
});
|
|
52894
|
-
it("
|
|
53999
|
+
it("reports null active when the current track has no controller", () => {
|
|
52895
54000
|
const track2 = new MockTrack();
|
|
52896
|
-
|
|
52897
|
-
deps.trackController.dispatch("trackActivated", { track: track2 });
|
|
52898
|
-
player.setActiveTextTrack("anything");
|
|
54001
|
+
activate(track2);
|
|
52899
54002
|
expect(player.activeTextTrack).toBeNull();
|
|
52900
54003
|
});
|
|
52901
54004
|
it("redispatches textTracksChange events from the active controller", () => {
|
|
52902
54005
|
const { track: track2, controller } = makeTrackWithController();
|
|
52903
|
-
|
|
52904
|
-
deps.trackController.dispatch("trackActivated", { track: track2 });
|
|
54006
|
+
activate(track2);
|
|
52905
54007
|
const spy = createEventSpy(player, "textTracksChange");
|
|
52906
|
-
controller
|
|
52907
|
-
{
|
|
52908
|
-
id: "t1",
|
|
52909
|
-
kind: "subtitles",
|
|
52910
|
-
language: null,
|
|
52911
|
-
label: "A",
|
|
52912
|
-
default: false,
|
|
52913
|
-
forced: false,
|
|
52914
|
-
characteristics: [],
|
|
52915
|
-
uri: "a.vtt",
|
|
52916
|
-
mimeType: "text/vtt"
|
|
52917
|
-
}
|
|
52918
|
-
]);
|
|
54008
|
+
setTracks(controller, [info({ language: null })]);
|
|
52919
54009
|
expect(spy).toHaveBeenCalled();
|
|
52920
54010
|
});
|
|
52921
|
-
it("fires textTracksChange when switching to a track with different list", () => {
|
|
54011
|
+
it("fires textTracksChange when switching to a track with a different list", () => {
|
|
52922
54012
|
const a = makeTrackWithController();
|
|
52923
54013
|
const b = makeTrackWithController();
|
|
52924
|
-
|
|
52925
|
-
|
|
52926
|
-
|
|
52927
|
-
|
|
52928
|
-
|
|
52929
|
-
default: false,
|
|
52930
|
-
forced: false,
|
|
52931
|
-
characteristics: [],
|
|
52932
|
-
uri: "a.vtt",
|
|
52933
|
-
mimeType: "text/vtt"
|
|
52934
|
-
};
|
|
52935
|
-
const trackInfoB = {
|
|
52936
|
-
id: "t2",
|
|
52937
|
-
kind: "subtitles",
|
|
52938
|
-
language: "fr",
|
|
52939
|
-
label: "B",
|
|
52940
|
-
default: false,
|
|
52941
|
-
forced: false,
|
|
52942
|
-
characteristics: [],
|
|
52943
|
-
uri: "b.vtt",
|
|
52944
|
-
mimeType: "text/vtt"
|
|
52945
|
-
};
|
|
52946
|
-
a.controller.setTextTracks([trackInfoA]);
|
|
52947
|
-
b.controller.setTextTracks([trackInfoB]);
|
|
52948
|
-
deps.trackController.activeTrack = a.track;
|
|
52949
|
-
deps.trackController.dispatch("trackActivated", { track: a.track });
|
|
54014
|
+
setTracks(a.controller, [info({ id: "t1", language: "en" })]);
|
|
54015
|
+
setTracks(b.controller, [
|
|
54016
|
+
info({ id: "t2", language: "fr", label: "B" })
|
|
54017
|
+
]);
|
|
54018
|
+
activate(a.track);
|
|
52950
54019
|
const spy = createEventSpy(player, "textTracksChange");
|
|
52951
|
-
|
|
52952
|
-
deps.trackController.dispatch("trackActivated", { track: b.track });
|
|
54020
|
+
activate(b.track);
|
|
52953
54021
|
expect(spy).toHaveBeenCalled();
|
|
52954
54022
|
});
|
|
52955
|
-
it("deactivates the previous track's captions on trackDeactivated", () => {
|
|
52956
|
-
var _a3;
|
|
52957
|
-
const a = makeTrackWithController();
|
|
52958
|
-
a.controller.setTextTracks([
|
|
52959
|
-
{
|
|
52960
|
-
id: "t1",
|
|
52961
|
-
kind: "subtitles",
|
|
52962
|
-
language: "en",
|
|
52963
|
-
label: "A",
|
|
52964
|
-
default: false,
|
|
52965
|
-
forced: false,
|
|
52966
|
-
characteristics: [],
|
|
52967
|
-
uri: "a.vtt",
|
|
52968
|
-
mimeType: "text/vtt"
|
|
52969
|
-
}
|
|
52970
|
-
]);
|
|
52971
|
-
deps.trackController.activeTrack = a.track;
|
|
52972
|
-
deps.trackController.dispatch("trackActivated", { track: a.track });
|
|
52973
|
-
a.controller.setActiveTextTrack("t1");
|
|
52974
|
-
expect((_a3 = a.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("t1");
|
|
52975
|
-
deps.trackController.activeTrack = null;
|
|
52976
|
-
deps.trackController.dispatch("trackDeactivated", { track: a.track });
|
|
52977
|
-
expect(a.controller.activeTextTrack).toBeNull();
|
|
52978
|
-
});
|
|
52979
54023
|
it("fires activeTextTrackChange when switching tracks", () => {
|
|
52980
54024
|
const a = makeTrackWithController();
|
|
52981
54025
|
const b = makeTrackWithController();
|
|
52982
|
-
a.controller
|
|
52983
|
-
|
|
52984
|
-
|
|
52985
|
-
kind: "subtitles",
|
|
52986
|
-
language: "en",
|
|
52987
|
-
label: "A",
|
|
52988
|
-
default: false,
|
|
52989
|
-
forced: false,
|
|
52990
|
-
characteristics: [],
|
|
52991
|
-
uri: "a.vtt",
|
|
52992
|
-
mimeType: "text/vtt"
|
|
52993
|
-
}
|
|
52994
|
-
]);
|
|
52995
|
-
a.controller.setActiveTextTrack("t1");
|
|
52996
|
-
deps.trackController.activeTrack = a.track;
|
|
52997
|
-
deps.trackController.dispatch("trackActivated", { track: a.track });
|
|
54026
|
+
setTracks(a.controller, [info({ id: "t1" })]);
|
|
54027
|
+
setActive(a.controller, info({ id: "t1" }));
|
|
54028
|
+
activate(a.track);
|
|
52998
54029
|
const spy = createEventSpy(player, "activeTextTrackChange");
|
|
52999
|
-
|
|
53000
|
-
deps.trackController.dispatch("trackActivated", { track: b.track });
|
|
54030
|
+
activate(b.track);
|
|
53001
54031
|
expect(spy).toHaveBeenCalled();
|
|
53002
54032
|
});
|
|
53003
54033
|
it("re-announces the content caption after an ad break so it does not read as off", () => {
|
|
53004
|
-
var _a3, _b
|
|
54034
|
+
var _a3, _b;
|
|
53005
54035
|
const content = makeTrackWithController();
|
|
53006
|
-
|
|
53007
|
-
|
|
53008
|
-
|
|
53009
|
-
|
|
53010
|
-
|
|
53011
|
-
|
|
53012
|
-
default: true,
|
|
53013
|
-
forced: true,
|
|
53014
|
-
characteristics: [],
|
|
53015
|
-
uri: "c.vtt",
|
|
53016
|
-
mimeType: "text/vtt"
|
|
53017
|
-
}
|
|
53018
|
-
]);
|
|
53019
|
-
expect((_a3 = content.controller.activeTextTrack) == null ? void 0 : _a3.id).toBe("c-forced");
|
|
53020
|
-
const ad = makeTrackWithController();
|
|
53021
|
-
ad.controller.setTextTracks([
|
|
53022
|
-
{
|
|
53023
|
-
id: "ad-cc",
|
|
53024
|
-
kind: "subtitles",
|
|
53025
|
-
language: "en",
|
|
53026
|
-
label: "English",
|
|
53027
|
-
default: false,
|
|
53028
|
-
forced: false,
|
|
53029
|
-
characteristics: [],
|
|
53030
|
-
uri: "ad.vtt",
|
|
53031
|
-
mimeType: "text/vtt"
|
|
53032
|
-
}
|
|
53033
|
-
]);
|
|
53034
|
-
deps.trackController.activeTrack = content.track;
|
|
53035
|
-
deps.trackController.dispatch("trackActivated", {
|
|
53036
|
-
track: content.track
|
|
54036
|
+
const contentForced = info({
|
|
54037
|
+
id: "c-forced",
|
|
54038
|
+
label: "English (Forced)",
|
|
54039
|
+
default: true,
|
|
54040
|
+
forced: true,
|
|
54041
|
+
uri: "c.vtt"
|
|
53037
54042
|
});
|
|
54043
|
+
setTracks(content.controller, [contentForced]);
|
|
54044
|
+
setActive(content.controller, contentForced);
|
|
54045
|
+
const ad = makeTrackWithController();
|
|
54046
|
+
const adCc = info({ id: "ad-cc", label: "English", uri: "ad.vtt" });
|
|
54047
|
+
setTracks(ad.controller, [adCc]);
|
|
54048
|
+
activate(content.track);
|
|
53038
54049
|
deps.adController.currentAdBreak = { placement: "midroll" };
|
|
53039
|
-
content.controller.
|
|
53040
|
-
|
|
53041
|
-
|
|
53042
|
-
player.
|
|
53043
|
-
expect((_b = player.activeTextTrack) == null ? void 0 : _b.id).toBe("ad-cc");
|
|
54050
|
+
content.controller.deactivate();
|
|
54051
|
+
activate(ad.track);
|
|
54052
|
+
setActive(ad.controller, adCc);
|
|
54053
|
+
expect((_a3 = player.activeTextTrack) == null ? void 0 : _a3.id).toBe("ad-cc");
|
|
53044
54054
|
deps.adController.currentAdBreak = null;
|
|
53045
54055
|
const spy = createEventSpy(player, "activeTextTrackChange");
|
|
53046
|
-
|
|
53047
|
-
content.
|
|
53048
|
-
|
|
53049
|
-
track: content.track
|
|
53050
|
-
});
|
|
53051
|
-
expect((_c = player.activeTextTrack) == null ? void 0 : _c.id).toBe("c-forced");
|
|
54056
|
+
content.controller.activate();
|
|
54057
|
+
activate(content.track);
|
|
54058
|
+
expect((_b = player.activeTextTrack) == null ? void 0 : _b.id).toBe("c-forced");
|
|
53052
54059
|
expect(spy).toHaveBeenCalled();
|
|
53053
54060
|
const lastCurrent = spy.calls.mostRecent().args[0].current;
|
|
53054
54061
|
expect(lastCurrent == null ? void 0 : lastCurrent.id).toBe("c-forced");
|
|
53055
54062
|
});
|
|
54063
|
+
it("announces empty tracks and null active when the track is torn down", () => {
|
|
54064
|
+
const a = makeTrackWithController();
|
|
54065
|
+
const t = info({ id: "t1" });
|
|
54066
|
+
setTracks(a.controller, [t]);
|
|
54067
|
+
setActive(a.controller, t);
|
|
54068
|
+
activate(a.track);
|
|
54069
|
+
const tracksSpy = createEventSpy(player, "textTracksChange");
|
|
54070
|
+
const activeSpy = createEventSpy(player, "activeTextTrackChange");
|
|
54071
|
+
deps.trackController.activeTrack = null;
|
|
54072
|
+
deps.trackController.dispatch("trackDeactivated", { track: a.track });
|
|
54073
|
+
expect(tracksSpy).toHaveBeenCalled();
|
|
54074
|
+
expect(tracksSpy.calls.mostRecent().args[0].current).toEqual([]);
|
|
54075
|
+
expect(activeSpy).toHaveBeenCalled();
|
|
54076
|
+
expect(activeSpy.calls.mostRecent().args[0].current).toBeNull();
|
|
54077
|
+
expect(player.activeTextTrack).toBeNull();
|
|
54078
|
+
});
|
|
53056
54079
|
it("does not emit activeTextTrackChange when neither track has an active selection", () => {
|
|
53057
54080
|
const a = makeTrackWithController();
|
|
53058
54081
|
const b = makeTrackWithController();
|
|
53059
|
-
|
|
53060
|
-
deps.trackController.dispatch("trackActivated", { track: a.track });
|
|
54082
|
+
activate(a.track);
|
|
53061
54083
|
const spy = createEventSpy(player, "activeTextTrackChange");
|
|
53062
|
-
|
|
53063
|
-
deps.trackController.dispatch("trackActivated", { track: b.track });
|
|
54084
|
+
activate(b.track);
|
|
53064
54085
|
expect(spy).not.toHaveBeenCalled();
|
|
53065
54086
|
});
|
|
53066
54087
|
});
|
|
@@ -54893,7 +55914,9 @@ x`);
|
|
|
54893
55914
|
await loadAndAwaitAdBreaks();
|
|
54894
55915
|
await poll(() => player.textTracks.length > 0, { timeout: 15 });
|
|
54895
55916
|
const track2 = player.textTracks[0];
|
|
54896
|
-
player.
|
|
55917
|
+
player.configure({
|
|
55918
|
+
text: { enabled: "on", selection: { id: track2.id } }
|
|
55919
|
+
});
|
|
54897
55920
|
const showingWithCues = () => {
|
|
54898
55921
|
const dom = mediaRef.value.textTracks;
|
|
54899
55922
|
return Array.from({ length: dom.length }, (_, i) => dom[i]).filter(
|
|
@@ -55033,7 +56056,9 @@ x`);
|
|
|
55033
56056
|
}
|
|
55034
56057
|
async function loadAndPlay(preferredAudioLanguage, lang) {
|
|
55035
56058
|
const player = suite.player;
|
|
55036
|
-
player.configure({
|
|
56059
|
+
player.configure({
|
|
56060
|
+
audio: { selection: { language: preferredAudioLanguage } }
|
|
56061
|
+
});
|
|
55037
56062
|
player.load({ type: "hls", uri: ASSET });
|
|
55038
56063
|
await player.play().catch(() => void 0);
|
|
55039
56064
|
expect(
|
|
@@ -55066,7 +56091,7 @@ x`);
|
|
|
55066
56091
|
await loadAndPlay("ja", "ja");
|
|
55067
56092
|
expect(await poll(() => player.currentTime > 1, { timeout: 30 })).withContext("content advanced").toBeTrue();
|
|
55068
56093
|
const resumeBefore = player.currentTime;
|
|
55069
|
-
player.configure({
|
|
56094
|
+
player.configure({ audio: { selection: { language: "de" } } });
|
|
55070
56095
|
expect(await poll(() => audioIsOnly(player, "de"), { timeout: 30 })).withContext(`content audio switched to 'de'`).toBeTrue();
|
|
55071
56096
|
expect(
|
|
55072
56097
|
await poll(
|
|
@@ -55205,6 +56230,9 @@ x`);
|
|
|
55205
56230
|
await new Promise((r) => setTimeout(r, 50));
|
|
55206
56231
|
}
|
|
55207
56232
|
}
|
|
56233
|
+
function selectTextById(id) {
|
|
56234
|
+
suite.player.configure({ text: { enabled: "on", selection: { id } } });
|
|
56235
|
+
}
|
|
55208
56236
|
it("DASH discovers sidecar text tracks", async () => {
|
|
55209
56237
|
await loadAndAwaitTextTracks(dashPlaylist);
|
|
55210
56238
|
const tracks = suite.player.textTracks;
|
|
@@ -55218,7 +56246,7 @@ x`);
|
|
|
55218
56246
|
const tracks = suite.player.textTracks;
|
|
55219
56247
|
if (tracks.length === 0) pending("no text tracks discovered");
|
|
55220
56248
|
const target = (_a3 = tracks.find((t) => t.language === "en")) != null ? _a3 : tracks[0];
|
|
55221
|
-
|
|
56249
|
+
selectTextById(target.id);
|
|
55222
56250
|
const deadline = Date.now() + 1e4;
|
|
55223
56251
|
let found = false;
|
|
55224
56252
|
while (!found && Date.now() < deadline) {
|
|
@@ -55243,11 +56271,40 @@ x`);
|
|
|
55243
56271
|
await loadAndAwaitTextTracks(dashPlaylist);
|
|
55244
56272
|
const tracks = suite.player.textTracks;
|
|
55245
56273
|
if (tracks.length === 0) pending("no text tracks discovered");
|
|
55246
|
-
|
|
56274
|
+
selectTextById(tracks[0].id);
|
|
55247
56275
|
expect(suite.player.activeTextTrack).not.toBeNull();
|
|
55248
|
-
suite.player.
|
|
56276
|
+
suite.player.configure({ text: { enabled: "off" } });
|
|
55249
56277
|
expect(suite.player.activeTextTrack).toBeNull();
|
|
55250
56278
|
});
|
|
56279
|
+
async function awaitCuesShowing() {
|
|
56280
|
+
const deadline = Date.now() + 1e4;
|
|
56281
|
+
while (Date.now() < deadline) {
|
|
56282
|
+
if (showingWithCues() > 0) return true;
|
|
56283
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
56284
|
+
}
|
|
56285
|
+
return false;
|
|
56286
|
+
}
|
|
56287
|
+
function showingWithCues() {
|
|
56288
|
+
var _a3, _b;
|
|
56289
|
+
const dom = mediaRef.value.textTracks;
|
|
56290
|
+
let n = 0;
|
|
56291
|
+
for (let i = 0; i < dom.length; i++) {
|
|
56292
|
+
if (dom[i].mode === "showing" && ((_b = (_a3 = dom[i].cues) == null ? void 0 : _a3.length) != null ? _b : 0) > 0) n++;
|
|
56293
|
+
}
|
|
56294
|
+
return n;
|
|
56295
|
+
}
|
|
56296
|
+
it("cycling languages never leaves more than one caption track showing", async () => {
|
|
56297
|
+
await loadAndAwaitTextTracks(dashPlaylist);
|
|
56298
|
+
const tracks = suite.player.textTracks;
|
|
56299
|
+
if (tracks.length < 2) pending("need at least two text tracks");
|
|
56300
|
+
for (const track2 of [...tracks, ...tracks]) {
|
|
56301
|
+
selectTextById(track2.id);
|
|
56302
|
+
await awaitCuesShowing();
|
|
56303
|
+
expect(showingWithCues()).withContext(
|
|
56304
|
+
`more than one caption track rendering after selecting ${track2.language}`
|
|
56305
|
+
).toBeLessThanOrEqual(1);
|
|
56306
|
+
}
|
|
56307
|
+
});
|
|
55251
56308
|
});
|
|
55252
56309
|
|
|
55253
56310
|
// test/integ/track/TrackController.test.ts
|