@intellectif/lk-react 15.0.0 → 16.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/CHANGELOG.md +51 -0
- package/README.md +2 -2
- package/dist/{WrittenResponse-BPbhERgk.d.cts → WrittenResponse-C2zw-1jK.d.cts} +1 -1
- package/dist/{WrittenResponse-ZAxLR_AU.d.ts → WrittenResponse-CTiNpSCC.d.ts} +1 -1
- package/dist/{chunk-ZH5WT2IE.cjs → chunk-2WM2KSO7.cjs} +25 -25
- package/dist/{chunk-ZH5WT2IE.cjs.map → chunk-2WM2KSO7.cjs.map} +1 -1
- package/dist/{chunk-XMJE34FY.cjs → chunk-2YZZL5FV.cjs} +13 -13
- package/dist/{chunk-XMJE34FY.cjs.map → chunk-2YZZL5FV.cjs.map} +1 -1
- package/dist/{chunk-2HAEQDHQ.js → chunk-3IHVDQRC.js} +492 -139
- package/dist/chunk-3IHVDQRC.js.map +1 -0
- package/dist/{chunk-IE5ZNCWN.js → chunk-4AQ4SWJ3.js} +5 -5
- package/dist/{chunk-IE5ZNCWN.js.map → chunk-4AQ4SWJ3.js.map} +1 -1
- package/dist/{chunk-RT5FTHUU.js → chunk-4X7NFEDB.js} +5 -5
- package/dist/{chunk-RT5FTHUU.js.map → chunk-4X7NFEDB.js.map} +1 -1
- package/dist/{chunk-SRD4KVAP.cjs → chunk-A2CPCOJA.cjs} +503 -149
- package/dist/chunk-A2CPCOJA.cjs.map +1 -0
- package/dist/{chunk-AF72QUBI.js → chunk-AEUP63CZ.js} +5 -5
- package/dist/{chunk-AF72QUBI.js.map → chunk-AEUP63CZ.js.map} +1 -1
- package/dist/{chunk-DQ3VVU5T.cjs → chunk-ALDGRDYD.cjs} +24 -24
- package/dist/{chunk-DQ3VVU5T.cjs.map → chunk-ALDGRDYD.cjs.map} +1 -1
- package/dist/{chunk-WKOV25NP.cjs → chunk-BO64SLHL.cjs} +8 -8
- package/dist/{chunk-WKOV25NP.cjs.map → chunk-BO64SLHL.cjs.map} +1 -1
- package/dist/{chunk-L7NE7PR4.cjs → chunk-DMCFARGP.cjs} +13 -13
- package/dist/{chunk-L7NE7PR4.cjs.map → chunk-DMCFARGP.cjs.map} +1 -1
- package/dist/{chunk-QM4AV6EG.js → chunk-HJLVWZOM.js} +4 -4
- package/dist/{chunk-QM4AV6EG.js.map → chunk-HJLVWZOM.js.map} +1 -1
- package/dist/{chunk-ZJMTHPHY.js → chunk-ICAYWVNF.js} +3 -3
- package/dist/{chunk-ZJMTHPHY.js.map → chunk-ICAYWVNF.js.map} +1 -1
- package/dist/{chunk-26GQO5I2.cjs → chunk-JYC2VV3Y.cjs} +12 -12
- package/dist/{chunk-26GQO5I2.cjs.map → chunk-JYC2VV3Y.cjs.map} +1 -1
- package/dist/{chunk-UCKA22ZJ.cjs → chunk-K63VPFOV.cjs} +9 -2
- package/dist/chunk-K63VPFOV.cjs.map +1 -0
- package/dist/{chunk-YWQH35VO.js → chunk-NOF6HJDV.js} +4 -4
- package/dist/{chunk-YWQH35VO.js.map → chunk-NOF6HJDV.js.map} +1 -1
- package/dist/{chunk-I2QGW4GD.js → chunk-OOYKKO3V.js} +11 -11
- package/dist/{chunk-I2QGW4GD.js.map → chunk-OOYKKO3V.js.map} +1 -1
- package/dist/{chunk-FKGUKFC7.cjs → chunk-OXUUE3HX.cjs} +5 -5
- package/dist/{chunk-FKGUKFC7.cjs.map → chunk-OXUUE3HX.cjs.map} +1 -1
- package/dist/{chunk-53YOZOGN.cjs → chunk-P5FGJMMI.cjs} +5 -5
- package/dist/{chunk-53YOZOGN.cjs.map → chunk-P5FGJMMI.cjs.map} +1 -1
- package/dist/{chunk-UAYWLYFW.cjs → chunk-QADR2XMQ.cjs} +18 -18
- package/dist/{chunk-UAYWLYFW.cjs.map → chunk-QADR2XMQ.cjs.map} +1 -1
- package/dist/{chunk-52GXJNQ3.js → chunk-QIA5FAAW.js} +8 -2
- package/dist/chunk-QIA5FAAW.js.map +1 -0
- package/dist/{chunk-EKBEKDWO.cjs → chunk-RJGCT35F.cjs} +13 -13
- package/dist/{chunk-EKBEKDWO.cjs.map → chunk-RJGCT35F.cjs.map} +1 -1
- package/dist/{chunk-33BQNWAQ.js → chunk-T33AJYLW.js} +5 -5
- package/dist/{chunk-33BQNWAQ.js.map → chunk-T33AJYLW.js.map} +1 -1
- package/dist/{chunk-EVBYEWHO.js → chunk-UHKZXOXD.js} +9 -2
- package/dist/chunk-UHKZXOXD.js.map +1 -0
- package/dist/{chunk-HA3VRF6E.js → chunk-UIGSH2XB.js} +3 -3
- package/dist/{chunk-HA3VRF6E.js.map → chunk-UIGSH2XB.js.map} +1 -1
- package/dist/{chunk-Z6TVOAO5.cjs → chunk-UISZWFI7.cjs} +8 -2
- package/dist/chunk-UISZWFI7.cjs.map +1 -0
- package/dist/{chunk-CE6OGJE6.js → chunk-UYRQEIB7.js} +5 -5
- package/dist/{chunk-CE6OGJE6.js.map → chunk-UYRQEIB7.js.map} +1 -1
- package/dist/{chunk-QEB6O6GU.js → chunk-W32ND3ZR.js} +6 -6
- package/dist/{chunk-QEB6O6GU.js.map → chunk-W32ND3ZR.js.map} +1 -1
- package/dist/{chunk-H6I2JDNW.js → chunk-WBLRUJ77.js} +10 -10
- package/dist/{chunk-H6I2JDNW.js.map → chunk-WBLRUJ77.js.map} +1 -1
- package/dist/{chunk-DP3DM7FR.cjs → chunk-WGIMLXRW.cjs} +6 -6
- package/dist/{chunk-DP3DM7FR.cjs.map → chunk-WGIMLXRW.cjs.map} +1 -1
- package/dist/{chunk-4H3JTX74.cjs → chunk-ZA5QUQ2V.cjs} +13 -13
- package/dist/{chunk-4H3JTX74.cjs.map → chunk-ZA5QUQ2V.cjs.map} +1 -1
- package/dist/components/ActivityPreview.cjs +12 -12
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +11 -11
- package/dist/components/ActivitySequence.cjs +13 -13
- package/dist/components/ActivitySequence.d.cts +2 -2
- package/dist/components/ActivitySequence.d.ts +2 -2
- package/dist/components/ActivitySequence.js +12 -12
- package/dist/components/Dictation.cjs +5 -5
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +4 -4
- package/dist/components/FillInTheBlanks.cjs +5 -5
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +4 -4
- package/dist/components/GapSelect.cjs +5 -5
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +4 -4
- package/dist/components/InteractiveVideo.cjs +15 -11
- package/dist/components/InteractiveVideo.d.cts +50 -4
- package/dist/components/InteractiveVideo.d.ts +50 -4
- package/dist/components/InteractiveVideo.js +10 -10
- package/dist/components/MultipleChoice.cjs +5 -5
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +4 -4
- package/dist/components/PronunciationFeedback.cjs +4 -4
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +3 -3
- package/dist/components/ReadAloud.cjs +6 -6
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +5 -5
- package/dist/components/StimulusPanel.cjs +4 -4
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/StimulusPanel.js +3 -3
- package/dist/components/WrittenResponse.cjs +5 -5
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +4 -4
- package/dist/i18n/LkIntlProvider.cjs +7 -7
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/i18n/LkIntlProvider.js +1 -1
- package/dist/index.cjs +41 -37
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -15
- package/dist/{strings-D0IWcsUm.d.ts → strings-Dgc5-DeA.d.ts} +21 -2
- package/dist/{strings-5588Q4Te.d.cts → strings-zY2QQYpU.d.cts} +21 -2
- package/dist/theme/ThemeProvider.cjs +6 -6
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/theme/defaults.css +7 -0
- package/dist/theme/skin.css +44 -6
- package/package.json +3 -3
- package/dist/chunk-2HAEQDHQ.js.map +0 -1
- package/dist/chunk-52GXJNQ3.js.map +0 -1
- package/dist/chunk-EVBYEWHO.js.map +0 -1
- package/dist/chunk-SRD4KVAP.cjs.map +0 -1
- package/dist/chunk-UCKA22ZJ.cjs.map +0 -1
- package/dist/chunk-Z6TVOAO5.cjs.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { MultipleChoice } from './chunk-
|
|
3
|
-
import { FillInTheBlanks } from './chunk-
|
|
4
|
-
import { GapSelect } from './chunk-
|
|
5
|
-
import { Dictation } from './chunk-
|
|
6
|
-
import { stopCaptureGroup, SequenceSlotContext, ReadAloud } from './chunk-
|
|
7
|
-
import { ActivityErrorBoundary, isDevelopment } from './chunk-
|
|
8
|
-
import { useLkStrings } from './chunk-
|
|
2
|
+
import { MultipleChoice } from './chunk-4X7NFEDB.js';
|
|
3
|
+
import { FillInTheBlanks } from './chunk-4AQ4SWJ3.js';
|
|
4
|
+
import { GapSelect } from './chunk-UYRQEIB7.js';
|
|
5
|
+
import { Dictation } from './chunk-T33AJYLW.js';
|
|
6
|
+
import { stopCaptureGroup, SequenceSlotContext, ReadAloud } from './chunk-W32ND3ZR.js';
|
|
7
|
+
import { ActivityErrorBoundary, isDevelopment } from './chunk-ICAYWVNF.js';
|
|
8
|
+
import { useLkStrings } from './chunk-QIA5FAAW.js';
|
|
9
9
|
import { assertRedactedItemGroup, validateItemGroup, ActivitySchemaError, flattenSequence, readMediaProgress } from '@intellectif/lk-core';
|
|
10
10
|
import { useMemo, useRef, useState, useEffect, useCallback } from 'react';
|
|
11
11
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -107,6 +107,7 @@ var SHORTCUT_KEYS = [
|
|
|
107
107
|
{ action: "chapter", keys: "[ / ]" },
|
|
108
108
|
{ action: "mute", keys: "M" },
|
|
109
109
|
{ action: "captions", keys: "C" },
|
|
110
|
+
{ action: "secondary-captions", keys: "Shift + C" },
|
|
110
111
|
{ action: "transcript", keys: "T" },
|
|
111
112
|
{ action: "picture-in-picture", keys: "P" },
|
|
112
113
|
{ action: "fullscreen", keys: "F" },
|
|
@@ -114,17 +115,87 @@ var SHORTCUT_KEYS = [
|
|
|
114
115
|
];
|
|
115
116
|
var JUMP_SECONDS = 10;
|
|
116
117
|
var FRAME_SECONDS = 1 / 30;
|
|
118
|
+
|
|
119
|
+
// src/components/InteractiveVideo/tracks.ts
|
|
120
|
+
var primarySubtag = (tag) => (tag.split("-")[0] ?? tag).toLowerCase();
|
|
121
|
+
function preferCaptions(tracks) {
|
|
122
|
+
return tracks.find((track) => track.kind === "captions") ?? tracks[0];
|
|
123
|
+
}
|
|
124
|
+
function matchLanguage(tracks, language) {
|
|
125
|
+
const wanted = language.toLowerCase();
|
|
126
|
+
const exact = tracks.filter((track) => track.srclang.toLowerCase() === wanted);
|
|
127
|
+
if (exact.length > 0) {
|
|
128
|
+
return preferCaptions(exact);
|
|
129
|
+
}
|
|
130
|
+
const family = primarySubtag(wanted);
|
|
131
|
+
return preferCaptions(tracks.filter((track) => primarySubtag(track.srclang) === family));
|
|
132
|
+
}
|
|
133
|
+
function drawable(tracks) {
|
|
134
|
+
return tracks.filter((track) => track.kind === "captions" || track.kind === "subtitles");
|
|
135
|
+
}
|
|
136
|
+
function secondaryCandidates(tracks, primary) {
|
|
137
|
+
const taken = primary?.srclang.toLowerCase();
|
|
138
|
+
return drawable(tracks).filter((track) => track.srclang.toLowerCase() !== taken);
|
|
139
|
+
}
|
|
140
|
+
function resolveCaptionTracks(tracks, preferences) {
|
|
141
|
+
const usable = drawable(tracks);
|
|
142
|
+
if (usable.length === 0) {
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
const primary = (preferences.captionLanguage === null ? void 0 : matchLanguage(usable, preferences.captionLanguage)) ?? usable.find((track) => track.default === true) ?? usable[0];
|
|
146
|
+
const second = preferences.secondaryCaptionLanguage;
|
|
147
|
+
const secondary = second === null ? void 0 : matchLanguage(secondaryCandidates(usable, primary), second);
|
|
148
|
+
return secondary === void 0 ? { primary } : { primary, secondary };
|
|
149
|
+
}
|
|
150
|
+
var overlap = (a, b) => Math.min(a.end, b.end) - Math.max(a.start, b.start);
|
|
151
|
+
var LOOKBACK = 8;
|
|
152
|
+
function pairSecondaryCues(primary, secondary) {
|
|
153
|
+
const rows = primary.map(() => []);
|
|
154
|
+
for (const cue of secondary) {
|
|
155
|
+
let low = 0;
|
|
156
|
+
let high = primary.length;
|
|
157
|
+
while (low < high) {
|
|
158
|
+
const middle = low + high >> 1;
|
|
159
|
+
if (primary[middle].start < cue.end) {
|
|
160
|
+
low = middle + 1;
|
|
161
|
+
} else {
|
|
162
|
+
high = middle;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let best = -1;
|
|
166
|
+
let bestShare = 0;
|
|
167
|
+
let misses = 0;
|
|
168
|
+
for (let index = low - 1; index >= 0 && misses <= LOOKBACK; index -= 1) {
|
|
169
|
+
const share = overlap(primary[index], cue);
|
|
170
|
+
if (share > 0) {
|
|
171
|
+
misses = 0;
|
|
172
|
+
if (share >= bestShare) {
|
|
173
|
+
best = index;
|
|
174
|
+
bestShare = share;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
misses += 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (best !== -1) {
|
|
181
|
+
rows[best].push(cue);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return rows;
|
|
185
|
+
}
|
|
117
186
|
function Menu({
|
|
118
187
|
label,
|
|
119
188
|
onClose,
|
|
120
|
-
children
|
|
189
|
+
children,
|
|
190
|
+
returnTo
|
|
121
191
|
}) {
|
|
122
192
|
const ref = useRef(null);
|
|
123
193
|
useEffect(() => {
|
|
124
194
|
const items = ref.current?.querySelectorAll('[role^="menuitem"]');
|
|
195
|
+
const back = returnTo === void 0 ? null : ref.current?.querySelector(`[data-menu-key="${returnTo}"]`);
|
|
125
196
|
const checked = ref.current?.querySelector('[aria-checked="true"]');
|
|
126
|
-
(checked ?? items?.[0])?.focus();
|
|
127
|
-
}, []);
|
|
197
|
+
(back ?? checked ?? items?.[0])?.focus();
|
|
198
|
+
}, [returnTo]);
|
|
128
199
|
const onKeyDown = (event) => {
|
|
129
200
|
const items = Array.from(
|
|
130
201
|
ref.current?.querySelectorAll('[role^="menuitem"]') ?? []
|
|
@@ -176,66 +247,136 @@ function SpeedMenu({
|
|
|
176
247
|
function SettingsMenu({
|
|
177
248
|
preferences,
|
|
178
249
|
tracks,
|
|
179
|
-
|
|
250
|
+
primaryTrack,
|
|
251
|
+
secondaryTrack,
|
|
180
252
|
captionsFailed,
|
|
181
253
|
strings,
|
|
182
254
|
onChange,
|
|
255
|
+
onCaptionTrack,
|
|
256
|
+
onSecondaryTrack,
|
|
183
257
|
onShowShortcuts,
|
|
184
258
|
onClose
|
|
185
259
|
}) {
|
|
186
260
|
const [page, setPage] = useState("main");
|
|
187
|
-
const
|
|
261
|
+
const [returnTo, setReturnTo] = useState(void 0);
|
|
262
|
+
const open = (next) => {
|
|
263
|
+
setReturnTo(void 0);
|
|
264
|
+
setPage(next);
|
|
265
|
+
};
|
|
266
|
+
const leave = () => {
|
|
267
|
+
setReturnTo(page);
|
|
268
|
+
setPage(page === "second" ? "captions" : "main");
|
|
269
|
+
};
|
|
270
|
+
const captionsValue = !preferences.captions || primaryTrack === void 0 ? strings.videoOff : secondaryTrack === void 0 ? primaryTrack.label : strings.videoCaptionPair(primaryTrack.label, secondaryTrack.label);
|
|
271
|
+
const partners = secondaryCandidates(tracks, primaryTrack);
|
|
272
|
+
const failedNotes = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
273
|
+
captionsFailed.primary && page === "captions" ? /* @__PURE__ */ jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoCaptionsFailed }) : null,
|
|
274
|
+
captionsFailed.secondary ? /* @__PURE__ */ jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoSecondaryCaptionsFailed }) : null
|
|
275
|
+
] });
|
|
188
276
|
const back = /* @__PURE__ */ jsxs(
|
|
189
277
|
"button",
|
|
190
278
|
{
|
|
191
279
|
type: "button",
|
|
192
280
|
role: "menuitem",
|
|
193
281
|
className: "lk-iv-menu-item lk-iv-menu-back",
|
|
194
|
-
onClick:
|
|
282
|
+
onClick: leave,
|
|
195
283
|
children: [
|
|
196
284
|
/* @__PURE__ */ jsx(ChevronIcon, { direction: "left" }),
|
|
197
|
-
/* @__PURE__ */ jsx("span", { children: page === "captions" ? strings.videoCaptionLanguage : strings.videoCaptionSize })
|
|
285
|
+
/* @__PURE__ */ jsx("span", { children: page === "captions" ? strings.videoCaptionLanguage : page === "second" ? strings.videoSecondCaptionLanguage : strings.videoCaptionSize })
|
|
198
286
|
]
|
|
199
287
|
}
|
|
200
288
|
);
|
|
289
|
+
const trackRadio = (track, on, choose) => /* @__PURE__ */ jsxs(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
type: "button",
|
|
293
|
+
role: "menuitemradio",
|
|
294
|
+
"aria-checked": on,
|
|
295
|
+
className: "lk-iv-menu-item",
|
|
296
|
+
lang: track.srclang,
|
|
297
|
+
onClick: choose,
|
|
298
|
+
children: [
|
|
299
|
+
/* @__PURE__ */ jsx("span", { dir: "auto", children: track.label }),
|
|
300
|
+
on ? /* @__PURE__ */ jsx(CheckIcon, {}) : null
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
`${track.kind}:${track.srclang}`
|
|
304
|
+
);
|
|
201
305
|
if (page === "captions") {
|
|
202
|
-
return /* @__PURE__ */ jsxs(
|
|
306
|
+
return /* @__PURE__ */ jsxs(
|
|
307
|
+
Menu,
|
|
308
|
+
{
|
|
309
|
+
label: strings.videoCaptionLanguage,
|
|
310
|
+
onClose,
|
|
311
|
+
returnTo,
|
|
312
|
+
children: [
|
|
313
|
+
back,
|
|
314
|
+
/* @__PURE__ */ jsxs(
|
|
315
|
+
"button",
|
|
316
|
+
{
|
|
317
|
+
type: "button",
|
|
318
|
+
role: "menuitemradio",
|
|
319
|
+
"aria-checked": !preferences.captions,
|
|
320
|
+
className: "lk-iv-menu-item",
|
|
321
|
+
onClick: () => onChange({ captions: false }),
|
|
322
|
+
children: [
|
|
323
|
+
/* @__PURE__ */ jsx("span", { children: strings.videoOff }),
|
|
324
|
+
!preferences.captions ? /* @__PURE__ */ jsx(CheckIcon, {}) : null
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
tracks.map(
|
|
329
|
+
(track) => trackRadio(
|
|
330
|
+
track,
|
|
331
|
+
preferences.captions && primaryTrack === track,
|
|
332
|
+
() => onCaptionTrack(track)
|
|
333
|
+
)
|
|
334
|
+
),
|
|
335
|
+
partners.length > 0 ? /* @__PURE__ */ jsxs(
|
|
336
|
+
"button",
|
|
337
|
+
{
|
|
338
|
+
type: "button",
|
|
339
|
+
role: "menuitem",
|
|
340
|
+
className: "lk-iv-menu-item",
|
|
341
|
+
"data-menu-key": "second",
|
|
342
|
+
onClick: () => open("second"),
|
|
343
|
+
children: [
|
|
344
|
+
/* @__PURE__ */ jsx("span", { children: strings.videoSecondCaptionLanguage }),
|
|
345
|
+
/* @__PURE__ */ jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
346
|
+
/* @__PURE__ */ jsx("span", { dir: "auto", lang: secondaryTrack?.srclang, children: secondaryTrack === void 0 ? strings.videoOff : secondaryTrack.label }),
|
|
347
|
+
/* @__PURE__ */ jsx(ChevronIcon, { direction: "right" })
|
|
348
|
+
] })
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
) : null,
|
|
352
|
+
failedNotes
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
"captions"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
if (page === "second") {
|
|
359
|
+
return /* @__PURE__ */ jsxs(Menu, { label: strings.videoSecondCaptionLanguage, onClose, children: [
|
|
203
360
|
back,
|
|
204
361
|
/* @__PURE__ */ jsxs(
|
|
205
362
|
"button",
|
|
206
363
|
{
|
|
207
364
|
type: "button",
|
|
208
365
|
role: "menuitemradio",
|
|
209
|
-
"aria-checked":
|
|
366
|
+
"aria-checked": secondaryTrack === void 0,
|
|
210
367
|
className: "lk-iv-menu-item",
|
|
211
|
-
onClick: () =>
|
|
368
|
+
onClick: () => onSecondaryTrack(null),
|
|
212
369
|
children: [
|
|
213
370
|
/* @__PURE__ */ jsx("span", { children: strings.videoOff }),
|
|
214
|
-
|
|
371
|
+
secondaryTrack === void 0 ? /* @__PURE__ */ jsx(CheckIcon, {}) : null
|
|
215
372
|
]
|
|
216
373
|
}
|
|
217
374
|
),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
type: "button",
|
|
224
|
-
role: "menuitemradio",
|
|
225
|
-
"aria-checked": on,
|
|
226
|
-
className: "lk-iv-menu-item",
|
|
227
|
-
lang: track.srclang,
|
|
228
|
-
onClick: () => onChange({ captions: true, captionLanguage: track.srclang }),
|
|
229
|
-
children: [
|
|
230
|
-
/* @__PURE__ */ jsx("span", { children: track.label }),
|
|
231
|
-
on ? /* @__PURE__ */ jsx(CheckIcon, {}) : null
|
|
232
|
-
]
|
|
233
|
-
},
|
|
234
|
-
`${track.kind}:${track.srclang}`
|
|
235
|
-
);
|
|
236
|
-
}),
|
|
237
|
-
captionsFailed ? /* @__PURE__ */ jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoCaptionsFailed }) : null
|
|
238
|
-
] }, "captions");
|
|
375
|
+
partners.map(
|
|
376
|
+
(track) => trackRadio(track, secondaryTrack === track, () => onSecondaryTrack(track))
|
|
377
|
+
),
|
|
378
|
+
failedNotes
|
|
379
|
+
] }, "second");
|
|
239
380
|
}
|
|
240
381
|
if (page === "size") {
|
|
241
382
|
return /* @__PURE__ */ jsxs(Menu, { label: strings.videoCaptionSize, onClose, children: [
|
|
@@ -257,7 +398,7 @@ function SettingsMenu({
|
|
|
257
398
|
))
|
|
258
399
|
] }, "size");
|
|
259
400
|
}
|
|
260
|
-
return /* @__PURE__ */ jsxs(Menu, { label: strings.videoSettings, onClose, children: [
|
|
401
|
+
return /* @__PURE__ */ jsxs(Menu, { label: strings.videoSettings, onClose, returnTo, children: [
|
|
261
402
|
tracks.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
262
403
|
/* @__PURE__ */ jsxs(
|
|
263
404
|
"button",
|
|
@@ -265,11 +406,12 @@ function SettingsMenu({
|
|
|
265
406
|
type: "button",
|
|
266
407
|
role: "menuitem",
|
|
267
408
|
className: "lk-iv-menu-item",
|
|
268
|
-
|
|
409
|
+
"data-menu-key": "captions",
|
|
410
|
+
onClick: () => open("captions"),
|
|
269
411
|
children: [
|
|
270
412
|
/* @__PURE__ */ jsx("span", { children: strings.videoCaptionLanguage }),
|
|
271
413
|
/* @__PURE__ */ jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
272
|
-
captionsValue,
|
|
414
|
+
/* @__PURE__ */ jsx("span", { dir: "auto", children: captionsValue }),
|
|
273
415
|
/* @__PURE__ */ jsx(ChevronIcon, { direction: "right" })
|
|
274
416
|
] })
|
|
275
417
|
]
|
|
@@ -281,7 +423,8 @@ function SettingsMenu({
|
|
|
281
423
|
type: "button",
|
|
282
424
|
role: "menuitem",
|
|
283
425
|
className: "lk-iv-menu-item",
|
|
284
|
-
|
|
426
|
+
"data-menu-key": "size",
|
|
427
|
+
onClick: () => open("size"),
|
|
285
428
|
children: [
|
|
286
429
|
/* @__PURE__ */ jsx("span", { children: strings.videoCaptionSize }),
|
|
287
430
|
/* @__PURE__ */ jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
@@ -375,6 +518,9 @@ function ContentsPanel({
|
|
|
375
518
|
chapters,
|
|
376
519
|
quizzes,
|
|
377
520
|
cues,
|
|
521
|
+
primaryLanguage,
|
|
522
|
+
secondaryCues,
|
|
523
|
+
secondaryLanguage,
|
|
378
524
|
activeCue,
|
|
379
525
|
limit,
|
|
380
526
|
strings,
|
|
@@ -386,10 +532,18 @@ function ContentsPanel({
|
|
|
386
532
|
const followUntil = useRef(0);
|
|
387
533
|
const hasTranscript = cues.length > 0;
|
|
388
534
|
const shownTab = hasTranscript ? tab : "contents";
|
|
535
|
+
const paired = useMemo(() => pairSecondaryCues(cues, secondaryCues), [cues, secondaryCues]);
|
|
389
536
|
const lines = useMemo(() => {
|
|
390
537
|
const trimmed = query.trim().toLowerCase();
|
|
391
|
-
return cues.map((cue, index) => ({
|
|
392
|
-
|
|
538
|
+
return cues.map((cue, index) => ({
|
|
539
|
+
cue,
|
|
540
|
+
index,
|
|
541
|
+
// The spoiler limit holds for the second language as for the first.
|
|
542
|
+
second: (paired[index] ?? []).filter((line) => line.start <= limit).map((line) => line.text).join(" ")
|
|
543
|
+
})).filter(({ cue }) => cue.start <= limit).filter(
|
|
544
|
+
({ cue, second }) => trimmed === "" || cue.text.toLowerCase().includes(trimmed) || second.toLowerCase().includes(trimmed)
|
|
545
|
+
);
|
|
546
|
+
}, [cues, paired, limit, query]);
|
|
393
547
|
useEffect(() => {
|
|
394
548
|
if (shownTab !== "transcript" || query.trim() !== "" || Date.now() < followUntil.current) {
|
|
395
549
|
return;
|
|
@@ -512,7 +666,7 @@ function ContentsPanel({
|
|
|
512
666
|
onTouchMove: () => {
|
|
513
667
|
followUntil.current = Date.now() + 4e3;
|
|
514
668
|
},
|
|
515
|
-
children: lines.length === 0 ? /* @__PURE__ */ jsx("p", { className: "lk-iv-empty", children: strings.videoTranscriptNoMatch }) : lines.map(({ cue, index }) => /* @__PURE__ */ jsxs(
|
|
669
|
+
children: lines.length === 0 ? /* @__PURE__ */ jsx("p", { className: "lk-iv-empty", children: strings.videoTranscriptNoMatch }) : lines.map(({ cue, index, second }) => /* @__PURE__ */ jsxs(
|
|
516
670
|
"button",
|
|
517
671
|
{
|
|
518
672
|
type: "button",
|
|
@@ -521,7 +675,10 @@ function ContentsPanel({
|
|
|
521
675
|
onClick: () => onSeek(cue.start),
|
|
522
676
|
children: [
|
|
523
677
|
/* @__PURE__ */ jsx("span", { className: "lk-iv-row-time", children: clock(cue.start) }),
|
|
524
|
-
/* @__PURE__ */ jsx("span", { children: highlight(cue.text) })
|
|
678
|
+
secondaryCues.length === 0 ? /* @__PURE__ */ jsx("span", { lang: primaryLanguage, dir: "auto", children: highlight(cue.text) }) : /* @__PURE__ */ jsxs("span", { className: "lk-iv-line-text", children: [
|
|
679
|
+
/* @__PURE__ */ jsx("span", { lang: primaryLanguage, dir: "auto", children: highlight(cue.text) }),
|
|
680
|
+
second === "" ? null : /* @__PURE__ */ jsx("span", { className: "lk-iv-line-secondary", lang: secondaryLanguage, dir: "auto", children: highlight(second) })
|
|
681
|
+
] })
|
|
525
682
|
]
|
|
526
683
|
},
|
|
527
684
|
`${cue.start}-${index}`
|
|
@@ -586,45 +743,73 @@ var DEFAULT_PREFERENCES = {
|
|
|
586
743
|
muted: false,
|
|
587
744
|
captions: true,
|
|
588
745
|
captionLanguage: null,
|
|
746
|
+
secondaryCaptionLanguage: null,
|
|
589
747
|
captionSize: "medium",
|
|
590
748
|
captionBackground: true,
|
|
591
749
|
panel: false,
|
|
592
750
|
shortcuts: true
|
|
593
751
|
};
|
|
594
752
|
var STORAGE_KEY = "lk.video.v1";
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
753
|
+
var LANGUAGE_TAG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
754
|
+
var BOOLEAN_FIELDS = ["muted", "captions", "captionBackground", "panel", "shortcuts"];
|
|
755
|
+
var LANGUAGE_FIELDS = ["captionLanguage", "secondaryCaptionLanguage"];
|
|
756
|
+
function sanitizePartialPreferences(raw) {
|
|
757
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
758
|
+
return {};
|
|
598
759
|
}
|
|
599
760
|
const value = raw;
|
|
600
|
-
const
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
761
|
+
const has = (key) => Object.hasOwn(value, key);
|
|
762
|
+
const out = {};
|
|
763
|
+
if (has("speed") && SPEEDS.includes(value.speed)) {
|
|
764
|
+
out.speed = value.speed;
|
|
765
|
+
}
|
|
766
|
+
if (has("volume") && typeof value.volume === "number" && Number.isFinite(value.volume)) {
|
|
767
|
+
out.volume = Math.min(1, Math.max(0, value.volume));
|
|
768
|
+
}
|
|
769
|
+
for (const field of BOOLEAN_FIELDS) {
|
|
770
|
+
if (has(field) && typeof value[field] === "boolean") {
|
|
771
|
+
out[field] = value[field];
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for (const field of LANGUAGE_FIELDS) {
|
|
775
|
+
const tag = value[field];
|
|
776
|
+
if (has(field) && (tag === null || typeof tag === "string" && LANGUAGE_TAG.test(tag))) {
|
|
777
|
+
out[field] = tag;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (has("captionSize") && (value.captionSize === "small" || value.captionSize === "medium" || value.captionSize === "large")) {
|
|
781
|
+
out.captionSize = value.captionSize;
|
|
782
|
+
}
|
|
783
|
+
return out;
|
|
784
|
+
}
|
|
785
|
+
function normalised(preferences) {
|
|
786
|
+
const { captionLanguage: first, secondaryCaptionLanguage: second } = preferences;
|
|
787
|
+
return first !== null && second !== null && first.toLowerCase() === second.toLowerCase() ? { ...preferences, secondaryCaptionLanguage: null } : preferences;
|
|
788
|
+
}
|
|
789
|
+
function resolvePreferences(layers) {
|
|
790
|
+
return normalised({
|
|
791
|
+
...DEFAULT_PREFERENCES,
|
|
792
|
+
...sanitizePartialPreferences(layers.defaults),
|
|
793
|
+
...sanitizePartialPreferences(layers.stored),
|
|
794
|
+
...sanitizePartialPreferences(layers.force),
|
|
795
|
+
...sanitizePartialPreferences(layers.chosen)
|
|
796
|
+
});
|
|
616
797
|
}
|
|
617
|
-
function
|
|
798
|
+
function applyPreferenceChange(previous, change) {
|
|
799
|
+
return normalised({ ...previous, ...sanitizePartialPreferences(change) });
|
|
800
|
+
}
|
|
801
|
+
function readStoredPreferences() {
|
|
618
802
|
try {
|
|
619
803
|
const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
620
|
-
return stored ?
|
|
804
|
+
return stored ? sanitizePartialPreferences(JSON.parse(stored)) : {};
|
|
621
805
|
} catch {
|
|
622
|
-
return
|
|
806
|
+
return {};
|
|
623
807
|
}
|
|
624
808
|
}
|
|
625
|
-
function
|
|
809
|
+
function rememberPreferences(change) {
|
|
626
810
|
try {
|
|
627
|
-
|
|
811
|
+
const next = { ...readStoredPreferences(), ...sanitizePartialPreferences(change) };
|
|
812
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
628
813
|
} catch {
|
|
629
814
|
}
|
|
630
815
|
}
|
|
@@ -944,6 +1129,55 @@ function onNextFrame(run) {
|
|
|
944
1129
|
const timer = setTimeout(run, 33);
|
|
945
1130
|
return () => clearTimeout(timer);
|
|
946
1131
|
}
|
|
1132
|
+
function trackKey(track) {
|
|
1133
|
+
return `${track.kind}:${track.srclang}:${track.src}`;
|
|
1134
|
+
}
|
|
1135
|
+
var NO_CUES = [];
|
|
1136
|
+
function useCaptionLine(track, load, peek) {
|
|
1137
|
+
const key = track === void 0 ? void 0 : trackKey(track);
|
|
1138
|
+
const [line, setLine] = useState({
|
|
1139
|
+
cues: NO_CUES,
|
|
1140
|
+
failed: false
|
|
1141
|
+
});
|
|
1142
|
+
const trackRef = useRef(track);
|
|
1143
|
+
trackRef.current = track;
|
|
1144
|
+
useEffect(() => {
|
|
1145
|
+
const wanted = trackRef.current;
|
|
1146
|
+
if (key === void 0 || wanted === void 0) {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
let live = true;
|
|
1150
|
+
load(wanted).then(
|
|
1151
|
+
(cues) => {
|
|
1152
|
+
if (live) {
|
|
1153
|
+
setLine({ key, cues, failed: false });
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
() => {
|
|
1157
|
+
if (live) {
|
|
1158
|
+
setLine({ key, cues: NO_CUES, failed: true });
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
);
|
|
1162
|
+
return () => {
|
|
1163
|
+
live = false;
|
|
1164
|
+
};
|
|
1165
|
+
}, [key, load]);
|
|
1166
|
+
if (key === void 0) {
|
|
1167
|
+
return { cues: NO_CUES, failed: false };
|
|
1168
|
+
}
|
|
1169
|
+
if (line.key === key) {
|
|
1170
|
+
return line;
|
|
1171
|
+
}
|
|
1172
|
+
return { cues: peek(key) ?? NO_CUES, failed: false };
|
|
1173
|
+
}
|
|
1174
|
+
function captionsShown(tracks, preferences) {
|
|
1175
|
+
if (!preferences.captions) {
|
|
1176
|
+
return { srclang: null, secondary: null };
|
|
1177
|
+
}
|
|
1178
|
+
const shown = resolveCaptionTracks(tracks, preferences);
|
|
1179
|
+
return { srclang: shown.primary?.srclang ?? null, secondary: shown.secondary?.srclang ?? null };
|
|
1180
|
+
}
|
|
947
1181
|
var playerCount = 0;
|
|
948
1182
|
var ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
949
1183
|
"multiple-choice",
|
|
@@ -1021,21 +1255,25 @@ function Player(props) {
|
|
|
1021
1255
|
playerCount += 1;
|
|
1022
1256
|
return `lk-iv-${playerCount}`;
|
|
1023
1257
|
});
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
})
|
|
1028
|
-
const
|
|
1258
|
+
const forced = useRef(props.preferences).current;
|
|
1259
|
+
const defaultsKey = JSON.stringify(props.defaultPreferences ?? null);
|
|
1260
|
+
const defaults = useMemo(() => JSON.parse(defaultsKey), [defaultsKey]);
|
|
1261
|
+
const chosen = useRef({});
|
|
1262
|
+
const [preferences, setPreferences] = useState(
|
|
1263
|
+
() => resolvePreferences({ force: forced, defaults: props.defaultPreferences })
|
|
1264
|
+
);
|
|
1265
|
+
const preferencesRef = useRef(preferences);
|
|
1266
|
+
preferencesRef.current = preferences;
|
|
1029
1267
|
useEffect(() => {
|
|
1030
|
-
setPreferences(
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
}, []);
|
|
1268
|
+
setPreferences(
|
|
1269
|
+
resolvePreferences({
|
|
1270
|
+
force: forced,
|
|
1271
|
+
stored: readStoredPreferences(),
|
|
1272
|
+
defaults,
|
|
1273
|
+
chosen: chosen.current
|
|
1274
|
+
})
|
|
1275
|
+
);
|
|
1276
|
+
}, [forced, defaults]);
|
|
1039
1277
|
const [submitted] = useState(() => new Set(props.submittedSlotIds ?? []));
|
|
1040
1278
|
const [answered, setAnswered] = useState(() => {
|
|
1041
1279
|
const seeded = /* @__PURE__ */ new Set();
|
|
@@ -1304,43 +1542,124 @@ function Player(props) {
|
|
|
1304
1542
|
}
|
|
1305
1543
|
] : [];
|
|
1306
1544
|
}, [media, locale, strings.videoCaptionLanguage]);
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
|
|
1545
|
+
const { primary: primaryTrack, secondary: secondaryTrack } = useMemo(
|
|
1546
|
+
() => resolveCaptionTracks(tracks, {
|
|
1547
|
+
captionLanguage: preferences.captionLanguage,
|
|
1548
|
+
secondaryCaptionLanguage: preferences.secondaryCaptionLanguage
|
|
1549
|
+
}),
|
|
1550
|
+
[tracks, preferences.captionLanguage, preferences.secondaryCaptionLanguage]
|
|
1551
|
+
);
|
|
1310
1552
|
const loaderRef = useRef(captionsLoader);
|
|
1311
1553
|
loaderRef.current = captionsLoader;
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1554
|
+
const loading = useRef(/* @__PURE__ */ new Map());
|
|
1555
|
+
const loaded = useRef(/* @__PURE__ */ new Map());
|
|
1556
|
+
const loadCues = useCallback((track) => {
|
|
1557
|
+
const key = trackKey(track);
|
|
1558
|
+
const pending = loading.current.get(key);
|
|
1559
|
+
if (pending !== void 0) {
|
|
1560
|
+
return pending;
|
|
1316
1561
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1562
|
+
const request = Promise.resolve().then(
|
|
1563
|
+
() => loaderRef.current ? loaderRef.current(track) : fetch(track.src, { credentials: "same-origin" }).then((response) => {
|
|
1564
|
+
if (!response.ok) {
|
|
1565
|
+
throw new Error(String(response.status));
|
|
1566
|
+
}
|
|
1567
|
+
return response.text();
|
|
1568
|
+
})
|
|
1569
|
+
).then((text) => {
|
|
1570
|
+
const parsed = parseWebVtt(text);
|
|
1571
|
+
if (parsed.length === 0) {
|
|
1572
|
+
throw new Error("The caption file held no cues.");
|
|
1322
1573
|
}
|
|
1323
|
-
|
|
1574
|
+
loaded.current.set(key, parsed);
|
|
1575
|
+
return parsed;
|
|
1324
1576
|
});
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1577
|
+
loading.current.set(key, request);
|
|
1578
|
+
request.catch(() => {
|
|
1579
|
+
loading.current.delete(key);
|
|
1580
|
+
});
|
|
1581
|
+
return request;
|
|
1582
|
+
}, []);
|
|
1583
|
+
const peekCues = useCallback((key) => loaded.current.get(key), []);
|
|
1584
|
+
const primaryLine = useCaptionLine(primaryTrack, loadCues, peekCues);
|
|
1585
|
+
const secondaryLine = useCaptionLine(secondaryTrack, loadCues, peekCues);
|
|
1586
|
+
const primaryCues = primaryLine.cues;
|
|
1587
|
+
const secondaryCues = secondaryLine.cues;
|
|
1588
|
+
const captionsFailed = { primary: primaryLine.failed, secondary: secondaryLine.failed };
|
|
1589
|
+
const hasCaptions = primaryCues.length > 0 || secondaryCues.length > 0;
|
|
1590
|
+
const hasTranscript = primaryCues.length > 0;
|
|
1591
|
+
const activeCueIndex = useMemo(() => cueIndexAt(primaryCues, current), [primaryCues, current]);
|
|
1592
|
+
const secondaryCueIndex = useMemo(
|
|
1593
|
+
() => cueIndexAt(secondaryCues, current),
|
|
1594
|
+
[secondaryCues, current]
|
|
1595
|
+
);
|
|
1596
|
+
const spoilerLimit = timeline.navigation === "no-skip-ahead" && renderMode !== "review" ? Math.max(furthestShown, current) : Number.POSITIVE_INFINITY;
|
|
1597
|
+
const onPreferencesChangeRef = useRef(props.onPreferencesChange);
|
|
1598
|
+
onPreferencesChangeRef.current = props.onPreferencesChange;
|
|
1599
|
+
const tracksRef = useRef(tracks);
|
|
1600
|
+
tracksRef.current = tracks;
|
|
1601
|
+
const lastSecondary = useRef(null);
|
|
1602
|
+
const changePreferences = useCallback(
|
|
1603
|
+
(change) => {
|
|
1604
|
+
const previous = preferencesRef.current;
|
|
1605
|
+
const next = applyPreferenceChange(previous, change);
|
|
1606
|
+
if (previous.secondaryCaptionLanguage !== null) {
|
|
1607
|
+
lastSecondary.current = previous.secondaryCaptionLanguage;
|
|
1330
1608
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1609
|
+
chosen.current = { ...chosen.current, ...change };
|
|
1610
|
+
preferencesRef.current = next;
|
|
1611
|
+
setPreferences(next);
|
|
1612
|
+
rememberPreferences(change);
|
|
1613
|
+
onPreferencesChangeRef.current?.(next, change);
|
|
1614
|
+
const before = captionsShown(tracksRef.current, previous);
|
|
1615
|
+
const after = captionsShown(tracksRef.current, next);
|
|
1616
|
+
if (before.srclang !== after.srclang || before.secondary !== after.secondary) {
|
|
1617
|
+
emit("video-captions-changed", after);
|
|
1335
1618
|
}
|
|
1619
|
+
},
|
|
1620
|
+
[emit]
|
|
1621
|
+
);
|
|
1622
|
+
const chooseCaptionTrack = (track) => {
|
|
1623
|
+
const swaps = primaryTrack !== void 0 && secondaryTrack !== void 0 && track.srclang.toLowerCase() === secondaryTrack.srclang.toLowerCase();
|
|
1624
|
+
changePreferences({
|
|
1625
|
+
captions: true,
|
|
1626
|
+
captionLanguage: track.srclang,
|
|
1627
|
+
...swaps ? { secondaryCaptionLanguage: primaryTrack.srclang } : {}
|
|
1336
1628
|
});
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const
|
|
1629
|
+
};
|
|
1630
|
+
const chooseSecondaryTrack = (track) => {
|
|
1631
|
+
changePreferences(
|
|
1632
|
+
track === null ? { secondaryCaptionLanguage: null } : { secondaryCaptionLanguage: track.srclang, captions: true }
|
|
1633
|
+
);
|
|
1634
|
+
};
|
|
1635
|
+
const toggleSecondary = () => {
|
|
1636
|
+
if (secondaryTrack !== void 0) {
|
|
1637
|
+
changePreferences(
|
|
1638
|
+
preferences.captions ? { secondaryCaptionLanguage: null } : { captions: true }
|
|
1639
|
+
);
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
const wanted = [
|
|
1643
|
+
lastSecondary.current,
|
|
1644
|
+
preferences.secondaryCaptionLanguage,
|
|
1645
|
+
sanitizePartialPreferences(defaults).secondaryCaptionLanguage ?? null,
|
|
1646
|
+
secondaryCandidates(tracks, primaryTrack)[0]?.srclang ?? null
|
|
1647
|
+
];
|
|
1648
|
+
for (const language of wanted) {
|
|
1649
|
+
if (language === null) {
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
const { secondary } = resolveCaptionTracks(tracks, {
|
|
1653
|
+
captionLanguage: preferences.captionLanguage,
|
|
1654
|
+
secondaryCaptionLanguage: language
|
|
1655
|
+
});
|
|
1656
|
+
if (secondary !== void 0) {
|
|
1657
|
+
changePreferences({ secondaryCaptionLanguage: secondary.srclang, captions: true });
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
announce(strings.videoNoSecondLanguage);
|
|
1662
|
+
};
|
|
1344
1663
|
useEffect(() => {
|
|
1345
1664
|
const video = videoRef.current;
|
|
1346
1665
|
if (video !== null) {
|
|
@@ -1507,14 +1826,17 @@ function Player(props) {
|
|
|
1507
1826
|
return;
|
|
1508
1827
|
case "c":
|
|
1509
1828
|
case "C":
|
|
1510
|
-
if (
|
|
1829
|
+
if (event.shiftKey) {
|
|
1830
|
+
done();
|
|
1831
|
+
toggleSecondary();
|
|
1832
|
+
} else if (hasCaptions) {
|
|
1511
1833
|
done();
|
|
1512
1834
|
changePreferences({ captions: !preferences.captions });
|
|
1513
1835
|
}
|
|
1514
1836
|
return;
|
|
1515
1837
|
case "t":
|
|
1516
1838
|
case "T":
|
|
1517
|
-
if (
|
|
1839
|
+
if (hasTranscript) {
|
|
1518
1840
|
done();
|
|
1519
1841
|
setPanelTab("transcript");
|
|
1520
1842
|
changePreferences({ panel: !(preferences.panel && panelTab === "transcript") });
|
|
@@ -1718,7 +2040,9 @@ function Player(props) {
|
|
|
1718
2040
|
emit("video-quiz-question-shown", { cueId: openCue.id, slotId: slot.slotId });
|
|
1719
2041
|
}
|
|
1720
2042
|
};
|
|
1721
|
-
const
|
|
2043
|
+
const captionsHidden = !preferences.captions || openQuiz !== null || ended || errorCode !== null;
|
|
2044
|
+
const primaryText = captionsHidden ? void 0 : primaryCues[activeCueIndex]?.text;
|
|
2045
|
+
const secondaryText = captionsHidden ? void 0 : secondaryCues[secondaryCueIndex]?.text;
|
|
1722
2046
|
const chromeVisible = !playing || chromeShown || menu !== null || scrubbing || focusInChrome || sheet || openQuiz !== null;
|
|
1723
2047
|
const volumeLevel = preferences.muted || preferences.volume === 0 ? "muted" : preferences.volume < 0.5 ? "low" : "high";
|
|
1724
2048
|
return (
|
|
@@ -1830,15 +2154,43 @@ function Player(props) {
|
|
|
1830
2154
|
}
|
|
1831
2155
|
),
|
|
1832
2156
|
label !== void 0 ? /* @__PURE__ */ jsx("span", { className: "lk-iv-label", children: label }) : null,
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
"
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2157
|
+
primaryText !== void 0 || secondaryText !== void 0 ? (
|
|
2158
|
+
// The lines keep `.lk-iv-caption`, `data-size` and `data-background`
|
|
2159
|
+
// as well, so a stylesheet written against 15.x still matches them.
|
|
2160
|
+
/* @__PURE__ */ jsxs(
|
|
2161
|
+
"div",
|
|
2162
|
+
{
|
|
2163
|
+
className: "lk-iv-captions",
|
|
2164
|
+
"data-size": preferences.captionSize,
|
|
2165
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2166
|
+
children: [
|
|
2167
|
+
primaryText !== void 0 ? /* @__PURE__ */ jsx(
|
|
2168
|
+
"div",
|
|
2169
|
+
{
|
|
2170
|
+
className: "lk-iv-caption",
|
|
2171
|
+
"data-role": "primary",
|
|
2172
|
+
"data-size": preferences.captionSize,
|
|
2173
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2174
|
+
lang: primaryTrack?.srclang,
|
|
2175
|
+
dir: "auto",
|
|
2176
|
+
children: /* @__PURE__ */ jsx("span", { children: primaryText })
|
|
2177
|
+
}
|
|
2178
|
+
) : null,
|
|
2179
|
+
secondaryText !== void 0 ? /* @__PURE__ */ jsx(
|
|
2180
|
+
"div",
|
|
2181
|
+
{
|
|
2182
|
+
className: "lk-iv-caption",
|
|
2183
|
+
"data-role": "secondary",
|
|
2184
|
+
"data-size": preferences.captionSize,
|
|
2185
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2186
|
+
lang: secondaryTrack?.srclang,
|
|
2187
|
+
dir: "auto",
|
|
2188
|
+
children: /* @__PURE__ */ jsx("span", { children: secondaryText })
|
|
2189
|
+
}
|
|
2190
|
+
) : null
|
|
2191
|
+
]
|
|
2192
|
+
}
|
|
2193
|
+
)
|
|
1842
2194
|
) : null,
|
|
1843
2195
|
waiting && errorCode === null && openQuiz === null ? /* @__PURE__ */ jsx("span", { className: "lk-iv-spinner", "aria-hidden": "true" }) : null,
|
|
1844
2196
|
errorCode !== null ? /* @__PURE__ */ jsxs("div", { className: "lk-iv-error", role: "alert", children: [
|
|
@@ -1887,7 +2239,7 @@ function Player(props) {
|
|
|
1887
2239
|
buffered,
|
|
1888
2240
|
chapters,
|
|
1889
2241
|
markers,
|
|
1890
|
-
cues:
|
|
2242
|
+
cues: primaryCues,
|
|
1891
2243
|
previewLimit: spoilerLimit,
|
|
1892
2244
|
strings,
|
|
1893
2245
|
onSeek: seek,
|
|
@@ -1972,7 +2324,7 @@ function Player(props) {
|
|
|
1972
2324
|
)
|
|
1973
2325
|
] }),
|
|
1974
2326
|
/* @__PURE__ */ jsxs("div", { className: "lk-iv-bar-group", children: [
|
|
1975
|
-
quizzes.length > 0 || chapters.length > 0 ||
|
|
2327
|
+
quizzes.length > 0 || chapters.length > 0 || hasTranscript ? /* @__PURE__ */ jsx(
|
|
1976
2328
|
"button",
|
|
1977
2329
|
{
|
|
1978
2330
|
type: "button",
|
|
@@ -1990,12 +2342,7 @@ function Player(props) {
|
|
|
1990
2342
|
className: "lk-iv-button",
|
|
1991
2343
|
"aria-label": preferences.captions ? strings.videoCaptionsHide : strings.videoCaptionsShow,
|
|
1992
2344
|
"aria-pressed": preferences.captions,
|
|
1993
|
-
onClick: () => {
|
|
1994
|
-
changePreferences({ captions: !preferences.captions });
|
|
1995
|
-
emit("video-captions-changed", {
|
|
1996
|
-
srclang: preferences.captions ? null : activeTrack?.srclang ?? null
|
|
1997
|
-
});
|
|
1998
|
-
},
|
|
2345
|
+
onClick: () => changePreferences({ captions: !preferences.captions }),
|
|
1999
2346
|
children: /* @__PURE__ */ jsx(CaptionsIcon, { on: preferences.captions })
|
|
2000
2347
|
}
|
|
2001
2348
|
) : null,
|
|
@@ -2043,8 +2390,11 @@ function Player(props) {
|
|
|
2043
2390
|
{
|
|
2044
2391
|
preferences,
|
|
2045
2392
|
tracks,
|
|
2046
|
-
|
|
2393
|
+
primaryTrack,
|
|
2394
|
+
secondaryTrack,
|
|
2047
2395
|
captionsFailed,
|
|
2396
|
+
onCaptionTrack: chooseCaptionTrack,
|
|
2397
|
+
onSecondaryTrack: chooseSecondaryTrack,
|
|
2048
2398
|
strings,
|
|
2049
2399
|
onChange: changePreferences,
|
|
2050
2400
|
onShowShortcuts: () => {
|
|
@@ -2225,7 +2575,10 @@ function Player(props) {
|
|
|
2225
2575
|
onTab: setPanelTab,
|
|
2226
2576
|
chapters,
|
|
2227
2577
|
quizzes,
|
|
2228
|
-
cues:
|
|
2578
|
+
cues: primaryCues,
|
|
2579
|
+
primaryLanguage: primaryTrack?.srclang,
|
|
2580
|
+
secondaryCues: secondaryTrack === void 0 ? NO_CUES : secondaryCues,
|
|
2581
|
+
secondaryLanguage: secondaryTrack?.srclang,
|
|
2229
2582
|
activeCue: activeCueIndex,
|
|
2230
2583
|
limit: spoilerLimit,
|
|
2231
2584
|
strings,
|
|
@@ -2325,6 +2678,6 @@ function InteractiveVideo2(props) {
|
|
|
2325
2678
|
);
|
|
2326
2679
|
}
|
|
2327
2680
|
|
|
2328
|
-
export { InteractiveVideo2 as InteractiveVideo };
|
|
2329
|
-
//# sourceMappingURL=chunk-
|
|
2330
|
-
//# sourceMappingURL=chunk-
|
|
2681
|
+
export { InteractiveVideo2 as InteractiveVideo, resolveCaptionTracks };
|
|
2682
|
+
//# sourceMappingURL=chunk-3IHVDQRC.js.map
|
|
2683
|
+
//# sourceMappingURL=chunk-3IHVDQRC.js.map
|