@kkcompany/player 2.25.0-canary.24 → 2.25.0-canary.25
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 +2 -0
- package/dist/StallReload-BFlQRphx.mjs +717 -0
- package/dist/Video-CMbK-cxg.mjs +120 -0
- package/dist/adaptation-BcTsh-wx.mjs +74 -0
- package/dist/api-2BOrEA5d.mjs +1057 -0
- package/dist/debugUtil-IF7p5TSI.mjs +23 -0
- package/dist/events-B3vI3Srm.mjs +16 -0
- package/dist/fixDashManifest-CJ63KKaA.mjs +56 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -10153
- package/dist/loadPlayer-CQdGA3Te.mjs +1560 -0
- package/dist/loadScript-Ct19kU9g.mjs +13 -0
- package/dist/mediaBindings-CoY60lQw.mjs +542 -0
- package/dist/modules.d.mts +51 -0
- package/dist/modules.mjs +631 -2201
- package/dist/playerCore/index.d.mts +3 -0
- package/dist/playerCore/index.mjs +4 -0
- package/dist/plugins/index.d.mts +2 -0
- package/dist/plugins/index.mjs +3 -0
- package/dist/reactEntry.d.mts +20 -0
- package/dist/reactEntry.mjs +6339 -0
- package/dist/util-B2YBSBjR.mjs +29 -0
- package/package.json +24 -19
- package/dist/core.mjs +0 -3075
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -20943
- package/dist/modules.d.ts +0 -89
- package/dist/plugins.d.ts +0 -5
- package/dist/plugins.mjs +0 -1105
- package/dist/react.d.ts +0 -178
- package/dist/react.mjs +0 -13066
|
@@ -0,0 +1,1560 @@
|
|
|
1
|
+
import { r as getVersion } from "./util-B2YBSBjR.mjs";
|
|
2
|
+
import { b as isIOS, i as isLiveDuration, k as on, x as isSafari } from "./mediaBindings-CoY60lQw.mjs";
|
|
3
|
+
import { t as fixDashManifest_default } from "./fixDashManifest-CJ63KKaA.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/playerCore/errors.js
|
|
6
|
+
const handleError = async ({ nativeEvent: event }, { media, displayError, delay = 5e3 }) => {
|
|
7
|
+
console.warn(event);
|
|
8
|
+
if (event.defaultPrevented) return;
|
|
9
|
+
const { code, name, data, message } = event.error || {};
|
|
10
|
+
if ([
|
|
11
|
+
code,
|
|
12
|
+
name,
|
|
13
|
+
data,
|
|
14
|
+
message
|
|
15
|
+
].every((it) => typeof it === "undefined")) {
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
if (!(media.readyState >= 1)) displayError({
|
|
18
|
+
code: `VIDEO:${media.error?.code || 0}`,
|
|
19
|
+
message: media.error?.message
|
|
20
|
+
});
|
|
21
|
+
}, delay);
|
|
22
|
+
console.warn("error from video element, let base player handle it", event);
|
|
23
|
+
} else displayError({
|
|
24
|
+
code,
|
|
25
|
+
name,
|
|
26
|
+
data,
|
|
27
|
+
message
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const reloadOnLiveStall = (media, { reload, stallDuration = 6e3 }) => {
|
|
31
|
+
const getBufferedEnd = () => Math.max(0, ...Array.from({ length: media.buffered?.length || 0 }, (_, i) => media.buffered.end(i)));
|
|
32
|
+
let checkTimer;
|
|
33
|
+
const state = { lastBufferedEnd: 0 };
|
|
34
|
+
const removeListener = on(media, "loadeddata", () => {
|
|
35
|
+
if (!isLiveDuration(media.duration)) return;
|
|
36
|
+
const checkStall = () => {
|
|
37
|
+
const currentBufferedEnd = getBufferedEnd();
|
|
38
|
+
if (!media.paused && state.lastBufferedEnd === currentBufferedEnd && (media.buffered.length < 1 || state.lastPlayed === media.currentTime)) {
|
|
39
|
+
console.warn("Live stream stall, reload to recover");
|
|
40
|
+
state.lastBufferedEnd = 0;
|
|
41
|
+
reload();
|
|
42
|
+
}
|
|
43
|
+
state.lastBufferedEnd = currentBufferedEnd;
|
|
44
|
+
state.lastPlayed = media.currentTime;
|
|
45
|
+
clearTimeout(checkTimer);
|
|
46
|
+
checkTimer = setTimeout(checkStall, stallDuration);
|
|
47
|
+
};
|
|
48
|
+
clearTimeout(checkTimer);
|
|
49
|
+
checkTimer = setTimeout(checkStall, stallDuration);
|
|
50
|
+
});
|
|
51
|
+
return () => {
|
|
52
|
+
removeListener();
|
|
53
|
+
clearTimeout(checkTimer);
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/util/printVersion.js
|
|
59
|
+
const printVersion = () => {
|
|
60
|
+
console.log([`%KKCompany Web Player SDK\n-----------\nVersion: ${getVersion()}`, `We are hiring, and looking for geeks like you! please join us at https://www.kkcompany.com/zh-tw/career/`].join("\n"), "color: #0E78F4");
|
|
61
|
+
};
|
|
62
|
+
var printVersion_default = printVersion;
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/playerCore/native.js
|
|
66
|
+
const loadNative = ({ videoElement }) => ({
|
|
67
|
+
load: ({ native: url }) => {
|
|
68
|
+
videoElement.src = url;
|
|
69
|
+
videoElement.style.height = "100%";
|
|
70
|
+
videoElement.style.width = "100%";
|
|
71
|
+
},
|
|
72
|
+
play: () => videoElement.play(),
|
|
73
|
+
pause: () => videoElement.pause(),
|
|
74
|
+
seek: (time) => {
|
|
75
|
+
videoElement.currentTime = time;
|
|
76
|
+
},
|
|
77
|
+
getVideoElement: () => videoElement,
|
|
78
|
+
getVideoQuality: () => ({}),
|
|
79
|
+
destroy: () => {}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/util/getUrlObject.js
|
|
84
|
+
const getUrlObject = (fn) => {
|
|
85
|
+
const createObjectURL = window.URL.createObjectURL.bind();
|
|
86
|
+
window.URL.createObjectURL = (blob) => {
|
|
87
|
+
if (blob.addSourceBuffer) fn(blob);
|
|
88
|
+
return createObjectURL(blob);
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
var getUrlObject_default = getUrlObject;
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/plugins/shaka/HttpFetchPlugin.js
|
|
95
|
+
/*! @license
|
|
96
|
+
* Shaka Player
|
|
97
|
+
* Copyright 2016 Google LLC
|
|
98
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
99
|
+
*/
|
|
100
|
+
let shaka$1;
|
|
101
|
+
const shakaLog$1 = { v1: () => {} };
|
|
102
|
+
const asMap = (object) => {
|
|
103
|
+
const map = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const key of Object.keys(object)) map.set(key, object[key]);
|
|
105
|
+
return map;
|
|
106
|
+
};
|
|
107
|
+
const makeResponse = (headers, data, status, uri, responseURL, requestType) => {
|
|
108
|
+
if (status >= 200 && status <= 299 && status != 202) return {
|
|
109
|
+
uri: responseURL || uri,
|
|
110
|
+
originalUri: uri,
|
|
111
|
+
data,
|
|
112
|
+
status,
|
|
113
|
+
headers,
|
|
114
|
+
fromCache: !!headers["x-shaka-from-cache"]
|
|
115
|
+
};
|
|
116
|
+
let responseText = null;
|
|
117
|
+
try {
|
|
118
|
+
responseText = shaka$1.util.StringUtils.fromBytesAutoDetect(data);
|
|
119
|
+
} catch (exception) {}
|
|
120
|
+
const severity = status == 401 || status == 403 ? shaka$1.util.Error.Severity.CRITICAL : shaka$1.util.Error.Severity.RECOVERABLE;
|
|
121
|
+
throw new shaka$1.util.Error(severity, shaka$1.util.Error.Category.NETWORK, shaka$1.util.Error.Code.BAD_HTTP_STATUS, uri, status, responseText, headers, requestType);
|
|
122
|
+
};
|
|
123
|
+
const goog$2 = { asserts: { assert: () => {} } };
|
|
124
|
+
/**
|
|
125
|
+
* @summary A networking plugin to handle http and https URIs via the Fetch API.
|
|
126
|
+
* @export
|
|
127
|
+
*/
|
|
128
|
+
var HttpFetchPlugin = class HttpFetchPlugin {
|
|
129
|
+
/**
|
|
130
|
+
* @param {string} uri
|
|
131
|
+
* @param {shaka.extern.Request} request
|
|
132
|
+
* @param {shaka.net.NetworkingEngine.RequestType} requestType
|
|
133
|
+
* @param {shaka.extern.ProgressUpdated} progressUpdated Called when a
|
|
134
|
+
* progress event happened.
|
|
135
|
+
* @param {shaka.extern.HeadersReceived} headersReceived Called when the
|
|
136
|
+
* headers for the download are received, but before the body is.
|
|
137
|
+
* @return {!shaka.extern.IAbortableOperation.<shaka.extern.Response>}
|
|
138
|
+
* @export
|
|
139
|
+
*/
|
|
140
|
+
static parse(uri, request, requestType, progressUpdated, headersReceived) {
|
|
141
|
+
const headers = new HttpFetchPlugin.Headers_();
|
|
142
|
+
asMap(request.headers).forEach((value, key) => {
|
|
143
|
+
headers.append(key, value);
|
|
144
|
+
});
|
|
145
|
+
const controller = new HttpFetchPlugin.AbortController_();
|
|
146
|
+
/** @type {!RequestInit} */
|
|
147
|
+
const init = {
|
|
148
|
+
body: request.body || void 0,
|
|
149
|
+
headers,
|
|
150
|
+
method: request.method,
|
|
151
|
+
signal: controller.signal,
|
|
152
|
+
credentials: request.allowCrossSiteCredentials ? "include" : void 0
|
|
153
|
+
};
|
|
154
|
+
/** @type {shaka.net.HttpFetchPlugin.AbortStatus} */
|
|
155
|
+
const abortStatus = {
|
|
156
|
+
canceled: false,
|
|
157
|
+
timedOut: false
|
|
158
|
+
};
|
|
159
|
+
const pendingRequest = HttpFetchPlugin.request_(uri, requestType, init, abortStatus, progressUpdated, headersReceived, request.streamDataCallback);
|
|
160
|
+
/** @type {!shaka.util.AbortableOperation} */
|
|
161
|
+
const op = new shaka$1.util.AbortableOperation(pendingRequest, () => {
|
|
162
|
+
abortStatus.canceled = true;
|
|
163
|
+
controller.abort();
|
|
164
|
+
return Promise.resolve();
|
|
165
|
+
});
|
|
166
|
+
const timeoutMs = request.retryParameters.timeout;
|
|
167
|
+
if (timeoutMs) {
|
|
168
|
+
const timer = new shaka$1.util.Timer(() => {
|
|
169
|
+
abortStatus.timedOut = true;
|
|
170
|
+
controller.abort();
|
|
171
|
+
});
|
|
172
|
+
timer.tickAfter(timeoutMs / 1e3);
|
|
173
|
+
op.finally(() => {
|
|
174
|
+
timer.stop();
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return op;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* @param {string} uri
|
|
181
|
+
* @param {shaka.net.NetworkingEngine.RequestType} requestType
|
|
182
|
+
* @param {!RequestInit} init
|
|
183
|
+
* @param {shaka.net.HttpFetchPlugin.AbortStatus} abortStatus
|
|
184
|
+
* @param {shaka.extern.ProgressUpdated} progressUpdated
|
|
185
|
+
* @param {shaka.extern.HeadersReceived} headersReceived
|
|
186
|
+
* @param {?function(BufferSource):!Promise} streamDataCallback
|
|
187
|
+
* @return {!Promise<!shaka.extern.Response>}
|
|
188
|
+
* @private
|
|
189
|
+
*/
|
|
190
|
+
static async request_(uri, requestType, init, abortStatus, progressUpdated, headersReceived, streamDataCallback) {
|
|
191
|
+
const fetch = HttpFetchPlugin.fetch_;
|
|
192
|
+
const ReadableStream = HttpFetchPlugin.ReadableStream_;
|
|
193
|
+
let response;
|
|
194
|
+
let arrayBuffer;
|
|
195
|
+
let loaded = 0;
|
|
196
|
+
let lastLoaded = 0;
|
|
197
|
+
let lastTime = Date.now();
|
|
198
|
+
try {
|
|
199
|
+
response = await fetch(uri, init);
|
|
200
|
+
headersReceived(HttpFetchPlugin.headersToGenericObject_(response.headers));
|
|
201
|
+
const reader = response.clone().body.getReader();
|
|
202
|
+
const contentLengthRaw = response.headers.get("Content-Length");
|
|
203
|
+
const contentLength = contentLengthRaw ? parseInt(contentLengthRaw, 10) : 0;
|
|
204
|
+
const start = (controller) => {
|
|
205
|
+
const push = async () => {
|
|
206
|
+
let readObj;
|
|
207
|
+
try {
|
|
208
|
+
readObj = await reader.read();
|
|
209
|
+
} catch (e) {
|
|
210
|
+
shakaLog$1.v1("error reading from stream", e.message);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (!readObj.done) {
|
|
214
|
+
loaded += readObj.value.byteLength;
|
|
215
|
+
if (response.status === 200 && streamDataCallback) await streamDataCallback(readObj.value);
|
|
216
|
+
}
|
|
217
|
+
const currentTime = Date.now();
|
|
218
|
+
if (currentTime - lastTime > 100 || readObj.done) {
|
|
219
|
+
progressUpdated(currentTime - lastTime, loaded - lastLoaded, contentLength - loaded);
|
|
220
|
+
lastLoaded = loaded;
|
|
221
|
+
lastTime = currentTime;
|
|
222
|
+
}
|
|
223
|
+
if (readObj.done) {
|
|
224
|
+
goog$2.asserts.assert(!readObj.value, "readObj should be unset when \"done\" is true.");
|
|
225
|
+
controller.close();
|
|
226
|
+
} else {
|
|
227
|
+
controller.enqueue(readObj.value);
|
|
228
|
+
push();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
push();
|
|
232
|
+
};
|
|
233
|
+
new ReadableStream({ start });
|
|
234
|
+
arrayBuffer = await response.arrayBuffer();
|
|
235
|
+
} catch (error) {
|
|
236
|
+
if (abortStatus.canceled) throw new shaka$1.util.Error(shaka$1.util.Error.Severity.RECOVERABLE, shaka$1.util.Error.Category.NETWORK, shaka$1.util.Error.Code.OPERATION_ABORTED, uri, requestType);
|
|
237
|
+
else if (abortStatus.timedOut) throw new shaka$1.util.Error(shaka$1.util.Error.Severity.RECOVERABLE, shaka$1.util.Error.Category.NETWORK, shaka$1.util.Error.Code.TIMEOUT, uri, requestType);
|
|
238
|
+
else throw new shaka$1.util.Error(shaka$1.util.Error.Severity.RECOVERABLE, shaka$1.util.Error.Category.NETWORK, shaka$1.util.Error.Code.HTTP_ERROR, uri, error, requestType);
|
|
239
|
+
}
|
|
240
|
+
return makeResponse(HttpFetchPlugin.headersToGenericObject_(response.headers), arrayBuffer, response.status, uri, response.url, requestType);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* @param {!Headers} headers
|
|
244
|
+
* @return {!Object.<string, string>}
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
static headersToGenericObject_(headers) {
|
|
248
|
+
const headersObj = {};
|
|
249
|
+
headers.forEach((value, key) => {
|
|
250
|
+
headersObj[key.trim()] = value;
|
|
251
|
+
});
|
|
252
|
+
return headersObj;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
HttpFetchPlugin.register = (shakaNamespace) => {
|
|
256
|
+
shaka$1 = shakaNamespace;
|
|
257
|
+
/**
|
|
258
|
+
* Overridden in unit tests, but compiled out in production.
|
|
259
|
+
*
|
|
260
|
+
* @const {function(string, !RequestInit)}
|
|
261
|
+
* @private
|
|
262
|
+
*/
|
|
263
|
+
HttpFetchPlugin.fetch_ = window.fetch;
|
|
264
|
+
/**
|
|
265
|
+
* Overridden in unit tests, but compiled out in production.
|
|
266
|
+
*
|
|
267
|
+
* @const {function(new: AbortController)}
|
|
268
|
+
* @private
|
|
269
|
+
*/
|
|
270
|
+
HttpFetchPlugin.AbortController_ = window.AbortController;
|
|
271
|
+
/**
|
|
272
|
+
* Overridden in unit tests, but compiled out in production.
|
|
273
|
+
*
|
|
274
|
+
* @const {function(new: ReadableStream, !Object)}
|
|
275
|
+
* @private
|
|
276
|
+
*/
|
|
277
|
+
HttpFetchPlugin.ReadableStream_ = window.ReadableStream;
|
|
278
|
+
/**
|
|
279
|
+
* Overridden in unit tests, but compiled out in production.
|
|
280
|
+
*
|
|
281
|
+
* @const {function(new: Headers)}
|
|
282
|
+
* @private
|
|
283
|
+
*/
|
|
284
|
+
HttpFetchPlugin.Headers_ = window.Headers;
|
|
285
|
+
shaka$1.net.NetworkingEngine.registerScheme("http", HttpFetchPlugin.parse);
|
|
286
|
+
shaka$1.net.NetworkingEngine.registerScheme("https", HttpFetchPlugin.parse);
|
|
287
|
+
shaka$1.net.NetworkingEngine.registerScheme("blob", HttpFetchPlugin.parse);
|
|
288
|
+
};
|
|
289
|
+
var HttpFetchPlugin_default = HttpFetchPlugin;
|
|
290
|
+
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/plugins/shaka/setupKKFariplay.js
|
|
293
|
+
const defaultInitDataTransform = (initData, initDataType, drmInfo) => {
|
|
294
|
+
if (initDataType === "skd") {
|
|
295
|
+
const { defaultGetContentId, initDataTransform } = shaka.util.FairPlayUtils;
|
|
296
|
+
const cert = drmInfo.serverCertificate;
|
|
297
|
+
return initDataTransform(initData, defaultGetContentId(initData), cert);
|
|
298
|
+
}
|
|
299
|
+
return initData;
|
|
300
|
+
};
|
|
301
|
+
const wrapFairplayLicenseRequest = (request) => {
|
|
302
|
+
const base64Payload = encodeURIComponent(btoa(String.fromCharCode(...new Uint8Array(request.body))));
|
|
303
|
+
const contentId = encodeURIComponent(new TextDecoder("utf-8").decode(request.initData).slice(6));
|
|
304
|
+
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
305
|
+
request.body = `spc=${base64Payload}&asset_id=${contentId}`;
|
|
306
|
+
};
|
|
307
|
+
const requestHandler = (type, request, player, extensionOptions) => {
|
|
308
|
+
const { LICENSE, SERVER_CERTIFICATE } = shaka.net.NetworkingEngine.RequestType;
|
|
309
|
+
if (type === SERVER_CERTIFICATE) Object.assign(request.headers, extensionOptions.drm[player.drmInfo().keySystem]?.headers);
|
|
310
|
+
if (type === LICENSE) wrapFairplayLicenseRequest(request);
|
|
311
|
+
return request;
|
|
312
|
+
};
|
|
313
|
+
const stripResponseCkc = (type, response) => {
|
|
314
|
+
if (type !== shaka.net.NetworkingEngine.RequestType.LICENSE) return response;
|
|
315
|
+
const keyMessage = new TextDecoder("utf-8").decode(response.data).trim();
|
|
316
|
+
if (keyMessage.slice(0, 5) === "<ckc>" && keyMessage.slice(-6) === "</ckc>") response.data = Uint8Array.from(atob(keyMessage.slice(5, -6)), (c) => c.charCodeAt(0));
|
|
317
|
+
return response;
|
|
318
|
+
};
|
|
319
|
+
const setupKKFariplay = (player, extensionOptions) => {
|
|
320
|
+
if (!window.WebKitMediaKeys) return;
|
|
321
|
+
shaka.polyfill.PatchedMediaKeysApple.install();
|
|
322
|
+
player.configure({ drm: { initDataTransform: defaultInitDataTransform } });
|
|
323
|
+
extensionOptions.requestHandlers.push(requestHandler);
|
|
324
|
+
extensionOptions.responseHandlers.push(stripResponseCkc);
|
|
325
|
+
};
|
|
326
|
+
var setupKKFariplay_default = setupKKFariplay;
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/plugins/shaka/VttTextParser.js
|
|
330
|
+
/*! @license
|
|
331
|
+
* Shaka Player
|
|
332
|
+
* Copyright 2016 Google LLC
|
|
333
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
334
|
+
*/
|
|
335
|
+
const goog$1 = { asserts: { assert: (condition, message) => {
|
|
336
|
+
if (!condition) console.warn("GOOG Assert!", message);
|
|
337
|
+
} } };
|
|
338
|
+
const addDefaultTextColor = (styles) => {
|
|
339
|
+
const textColor = shaka.text.Cue.defaultTextColor;
|
|
340
|
+
for (const [key, value] of Object.entries(textColor)) {
|
|
341
|
+
const cue = new shaka.text.Cue(0, 0, "");
|
|
342
|
+
cue.color = value;
|
|
343
|
+
styles.set(`.${key}`, cue);
|
|
344
|
+
}
|
|
345
|
+
const bgColor = shaka.text.Cue.defaultTextBackgroundColor;
|
|
346
|
+
for (const [key, value] of Object.entries(bgColor)) {
|
|
347
|
+
const cue = new shaka.text.Cue(0, 0, "");
|
|
348
|
+
cue.backgroundColor = value;
|
|
349
|
+
styles.set(`.${key}`, cue);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const parseTime = (text) => {
|
|
353
|
+
const results = Array.from(text.matchAll(/(?:(\d{1,}):)?(\d{2}):(\d{2})((\.(\d{1,3})))?/g))?.[0];
|
|
354
|
+
if (results == null) return null;
|
|
355
|
+
const hours = Number(results[1]) || 0;
|
|
356
|
+
const minutes = Number(results[2]);
|
|
357
|
+
const seconds = Number(results[3]);
|
|
358
|
+
const milliseconds = Number(results[6]) || 0;
|
|
359
|
+
if (minutes > 59 || seconds > 59) return null;
|
|
360
|
+
return milliseconds / 1e3 + seconds + minutes * 60 + hours * 3600;
|
|
361
|
+
};
|
|
362
|
+
const parseRegion = (block) => {
|
|
363
|
+
if (block[0].trim() !== "REGION") return [];
|
|
364
|
+
const region = new shaka.text.CueRegion();
|
|
365
|
+
block.slice(1).forEach((word) => {
|
|
366
|
+
let results = null;
|
|
367
|
+
if (results = /^id:(.*)$/.exec(word)) region.id = results[1];
|
|
368
|
+
else if (results = /^width:(\d{1,2}|100)%$/.exec(word)) region.width = Number(results[1]);
|
|
369
|
+
else if (results = /^lines:(\d+)$/.exec(word)) {
|
|
370
|
+
region.height = Number(results[1]);
|
|
371
|
+
region.heightUnits = shaka.text.CueRegion.units.LINES;
|
|
372
|
+
} else if (results = /^regionanchor:(\d{1,2}|100)%,(\d{1,2}|100)%$/.exec(word)) {
|
|
373
|
+
region.regionAnchorX = Number(results[1]);
|
|
374
|
+
region.regionAnchorY = Number(results[2]);
|
|
375
|
+
} else if (results = /^viewportanchor:(\d{1,2}|100)%,(\d{1,2}|100)%$/.exec(word)) {
|
|
376
|
+
region.viewportAnchorX = Number(results[1]);
|
|
377
|
+
region.viewportAnchorY = Number(results[2]);
|
|
378
|
+
} else if (results = /^scroll:up$/.exec(word)) region.scroll = shaka.text.CueRegion.scrollMode.UP;
|
|
379
|
+
else shaka.log.warning("VTT parser encountered an invalid VTTRegion setting: ", word, " The setting will be ignored.");
|
|
380
|
+
});
|
|
381
|
+
return [region];
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* @implements {shaka.extern.TextParser}
|
|
385
|
+
* @export
|
|
386
|
+
*/
|
|
387
|
+
var VttTextParser = class VttTextParser {
|
|
388
|
+
/** Constructs a VTT parser. */
|
|
389
|
+
constructor() {
|
|
390
|
+
/** @private {boolean} */
|
|
391
|
+
this.sequenceMode_ = false;
|
|
392
|
+
/** @private {string} */
|
|
393
|
+
this.manifestType_ = shaka.media.ManifestParser.UNKNOWN;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* @override
|
|
397
|
+
* @export
|
|
398
|
+
*/
|
|
399
|
+
parseInit() {
|
|
400
|
+
goog$1.asserts.assert(false, "VTT does not have init segments");
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* @override
|
|
404
|
+
* @export
|
|
405
|
+
*/
|
|
406
|
+
setSequenceMode(sequenceMode) {
|
|
407
|
+
this.sequenceMode_ = sequenceMode;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* @override
|
|
411
|
+
* @export
|
|
412
|
+
*/
|
|
413
|
+
setManifestType(manifestType) {
|
|
414
|
+
this.manifestType_ = manifestType;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* @override
|
|
418
|
+
* @export
|
|
419
|
+
*/
|
|
420
|
+
parseMedia(data, time) {
|
|
421
|
+
let str = shaka.util.StringUtils.fromUTF8(data);
|
|
422
|
+
str = str.replace(/\r\n|\r(?=[^\n]|$)/gm, "\n");
|
|
423
|
+
const blocks = str.split(/\n{2,}/m);
|
|
424
|
+
if (!/^WEBVTT($|[ \t\n])/m.test(blocks[0])) throw new shaka.util.Error(shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.TEXT, shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
425
|
+
let offset = time.vttOffset;
|
|
426
|
+
if (this.manifestType_ == shaka.media.ManifestParser.HLS) {
|
|
427
|
+
if (blocks[0].includes("X-TIMESTAMP-MAP")) offset = this.computeHlsOffset_(blocks[0], time);
|
|
428
|
+
else if (time.periodStart && time.vttOffset == time.periodStart) offset = 0;
|
|
429
|
+
}
|
|
430
|
+
const regions = [];
|
|
431
|
+
/** @type {!Map<string, !shaka.text.Cue>} */
|
|
432
|
+
const styles = /* @__PURE__ */ new Map();
|
|
433
|
+
addDefaultTextColor(styles);
|
|
434
|
+
const ret = [];
|
|
435
|
+
for (const block of blocks.slice(1)) {
|
|
436
|
+
const lines = block.split("\n");
|
|
437
|
+
VttTextParser.parseStyle_(lines, styles);
|
|
438
|
+
regions.push(...parseRegion(lines));
|
|
439
|
+
const cue = VttTextParser.parseCue_(lines, offset, regions, styles);
|
|
440
|
+
if (cue) ret.push(cue);
|
|
441
|
+
}
|
|
442
|
+
return ret;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* @param {string} headerBlock Contains X-TIMESTAMP-MAP.
|
|
446
|
+
* @param {shaka.extern.TextParser.TimeContext} time
|
|
447
|
+
* @return {number}
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
computeHlsOffset_(headerBlock, time) {
|
|
451
|
+
const cueTimeMatch = headerBlock.match(/LOCAL:((?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{3}))/m);
|
|
452
|
+
const mpegTimeMatch = headerBlock.match(/MPEGTS:(\d+)/m);
|
|
453
|
+
if (!cueTimeMatch || !mpegTimeMatch) throw new shaka.util.Error(shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.TEXT, shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
454
|
+
const cueTime = parseTime(cueTimeMatch[1]);
|
|
455
|
+
if (cueTime == null) throw new shaka.util.Error(shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.TEXT, shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
456
|
+
let mpegTime = Number(mpegTimeMatch[1]);
|
|
457
|
+
const mpegTimescale = VttTextParser.MPEG_TIMESCALE_;
|
|
458
|
+
const rolloverSeconds = VttTextParser.TS_ROLLOVER_ / mpegTimescale;
|
|
459
|
+
let segmentStart = time.segmentStart - time.periodStart;
|
|
460
|
+
while (segmentStart >= rolloverSeconds) {
|
|
461
|
+
segmentStart -= rolloverSeconds;
|
|
462
|
+
mpegTime += VttTextParser.TS_ROLLOVER_;
|
|
463
|
+
}
|
|
464
|
+
return time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Parses a style block into a Cue object.
|
|
468
|
+
*
|
|
469
|
+
* @param {!Array<string>} text
|
|
470
|
+
* @param {!Map<string, !shaka.text.Cue>} styles
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
static parseStyle_(text, styles) {
|
|
474
|
+
if (text.length == 1 && !text[0]) return;
|
|
475
|
+
if (/^NOTE($|[ \t])/.test(text[0])) return;
|
|
476
|
+
if (text[0] != "STYLE") return;
|
|
477
|
+
/** @type {!Array<!Array<string>>} */
|
|
478
|
+
const styleBlocks = [];
|
|
479
|
+
let lastBlockIndex = -1;
|
|
480
|
+
for (let i = 1; i < text.length; i++) {
|
|
481
|
+
if (text[i].includes("::cue")) {
|
|
482
|
+
styleBlocks.push([]);
|
|
483
|
+
lastBlockIndex = styleBlocks.length - 1;
|
|
484
|
+
}
|
|
485
|
+
if (lastBlockIndex == -1) continue;
|
|
486
|
+
styleBlocks[lastBlockIndex].push(text[i]);
|
|
487
|
+
if (text[i].includes("}")) lastBlockIndex = -1;
|
|
488
|
+
}
|
|
489
|
+
for (const styleBlock of styleBlocks) {
|
|
490
|
+
let styleSelector = "global";
|
|
491
|
+
const selector = styleBlock[0].match(/\((.*)\)/);
|
|
492
|
+
if (selector) styleSelector = selector.pop();
|
|
493
|
+
let propertyLines = styleBlock.slice(1, -1);
|
|
494
|
+
if (styleBlock[0].includes("}")) {
|
|
495
|
+
const payload = /\{(.*?)\}/.exec(styleBlock[0]);
|
|
496
|
+
if (payload) propertyLines = payload[1].split(";");
|
|
497
|
+
}
|
|
498
|
+
let cue = styles.get(styleSelector);
|
|
499
|
+
if (!cue) cue = new shaka.text.Cue(0, 0, "");
|
|
500
|
+
let validStyle = false;
|
|
501
|
+
for (let i = 0; i < propertyLines.length; i++) {
|
|
502
|
+
const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
|
|
503
|
+
if (lineParts) {
|
|
504
|
+
const name = lineParts[1].trim();
|
|
505
|
+
const value = lineParts[2].trim().replace(";", "");
|
|
506
|
+
switch (name) {
|
|
507
|
+
case "background-color":
|
|
508
|
+
case "background":
|
|
509
|
+
validStyle = true;
|
|
510
|
+
cue.backgroundColor = value;
|
|
511
|
+
break;
|
|
512
|
+
case "color":
|
|
513
|
+
validStyle = true;
|
|
514
|
+
cue.color = value;
|
|
515
|
+
break;
|
|
516
|
+
case "font-family":
|
|
517
|
+
validStyle = true;
|
|
518
|
+
cue.fontFamily = value;
|
|
519
|
+
break;
|
|
520
|
+
case "font-size":
|
|
521
|
+
validStyle = true;
|
|
522
|
+
cue.fontSize = value;
|
|
523
|
+
break;
|
|
524
|
+
case "font-weight":
|
|
525
|
+
if (parseInt(value, 10) >= 700 || value == "bold") {
|
|
526
|
+
validStyle = true;
|
|
527
|
+
cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
case "font-style":
|
|
531
|
+
switch (value) {
|
|
532
|
+
case "normal":
|
|
533
|
+
validStyle = true;
|
|
534
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
|
|
535
|
+
break;
|
|
536
|
+
case "italic":
|
|
537
|
+
validStyle = true;
|
|
538
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
|
|
539
|
+
break;
|
|
540
|
+
case "oblique":
|
|
541
|
+
validStyle = true;
|
|
542
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
case "opacity":
|
|
547
|
+
validStyle = true;
|
|
548
|
+
cue.opacity = parseFloat(value);
|
|
549
|
+
break;
|
|
550
|
+
case "text-combine-upright":
|
|
551
|
+
validStyle = true;
|
|
552
|
+
cue.textCombineUpright = value;
|
|
553
|
+
break;
|
|
554
|
+
case "text-shadow":
|
|
555
|
+
validStyle = true;
|
|
556
|
+
cue.textShadow = value;
|
|
557
|
+
break;
|
|
558
|
+
case "white-space":
|
|
559
|
+
validStyle = true;
|
|
560
|
+
cue.wrapLine = value != "noWrap";
|
|
561
|
+
break;
|
|
562
|
+
default:
|
|
563
|
+
shaka.log.warning("VTT parser encountered an unsupported style: ", lineParts);
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (validStyle) styles.set(styleSelector, cue);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Parses a text block into a Cue object.
|
|
573
|
+
*
|
|
574
|
+
* @param {!Array<string>} text
|
|
575
|
+
* @param {number} timeOffset
|
|
576
|
+
* @param {!Array<!shaka.text.CueRegion>} regions
|
|
577
|
+
* @param {!Map<string, !shaka.text.Cue>} styles
|
|
578
|
+
* @return {shaka.text.Cue}
|
|
579
|
+
* @private
|
|
580
|
+
*/
|
|
581
|
+
static parseCue_(text, timeOffset, regions, styles) {
|
|
582
|
+
if (text.length == 1 && !text[0]) return null;
|
|
583
|
+
if (/^NOTE($|[ \t])/.test(text[0])) return null;
|
|
584
|
+
if (text[0] == "STYLE" || text[0] == "REGION") return null;
|
|
585
|
+
const skipIndex = text.findIndex((line) => /^#/.test(line.trim()));
|
|
586
|
+
if (skipIndex > 0) text = text.slice(skipIndex);
|
|
587
|
+
if (text.length < 2) return;
|
|
588
|
+
let id = null;
|
|
589
|
+
if (!text[0].includes("-->")) {
|
|
590
|
+
id = text[0];
|
|
591
|
+
text.splice(0, 1);
|
|
592
|
+
}
|
|
593
|
+
let [start, end] = text[0].split("-->").map((part) => parseTime(part.trim()));
|
|
594
|
+
if (start == null || end == null) {
|
|
595
|
+
shaka.log.alwaysWarn("Failed to parse VTT time code. Cue skipped:", id, text);
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
start += timeOffset;
|
|
599
|
+
end += timeOffset;
|
|
600
|
+
const payload = text.slice(1).join("\n").trim();
|
|
601
|
+
let cue = null;
|
|
602
|
+
if (styles.has("global")) {
|
|
603
|
+
cue = styles.get("global").clone();
|
|
604
|
+
cue.startTime = start;
|
|
605
|
+
cue.endTime = end;
|
|
606
|
+
cue.payload = payload;
|
|
607
|
+
} else cue = new shaka.text.Cue(start, end, payload);
|
|
608
|
+
text[0].split(/\s+/g).slice(3).forEach((word) => {
|
|
609
|
+
if (!word.trim()) return;
|
|
610
|
+
if (!VttTextParser.parseCueSetting(cue, word, regions)) shaka.log.warning("VTT parser encountered an invalid VTT setting: ", word, " The setting will be ignored.");
|
|
611
|
+
});
|
|
612
|
+
shaka.text.Cue.parseCuePayload(cue, styles);
|
|
613
|
+
if (id != null) cue.id = id;
|
|
614
|
+
return cue;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Parses a WebVTT setting from the given word.
|
|
618
|
+
*
|
|
619
|
+
* @param {!shaka.text.Cue} cue
|
|
620
|
+
* @param {string} word
|
|
621
|
+
* @param {!Array<!shaka.text.CueRegion>} regions
|
|
622
|
+
* @return {boolean} True on success.
|
|
623
|
+
*/
|
|
624
|
+
static parseCueSetting(cue, word, regions) {
|
|
625
|
+
let results = null;
|
|
626
|
+
if (results = /^align:(start|middle|center|end|left|right)$/.exec(word)) VttTextParser.setTextAlign_(cue, results[1]);
|
|
627
|
+
else if (results = /^vertical:(lr|rl)$/.exec(word)) VttTextParser.setVerticalWritingMode_(cue, results[1]);
|
|
628
|
+
else if (results = /^size:([\d.]+)%$/.exec(word)) cue.size = Number(results[1]);
|
|
629
|
+
else if (results = /^position:([\d.]+)%(?:,(line-left|line-right|middle|center|start|end|auto))?$/.exec(word)) {
|
|
630
|
+
cue.position = Number(results[1]);
|
|
631
|
+
if (results[2]) VttTextParser.setPositionAlign_(cue, results[2]);
|
|
632
|
+
} else if (results = /^region:(.*)$/.exec(word)) {
|
|
633
|
+
const region = VttTextParser.getRegionById_(regions, results[1]);
|
|
634
|
+
if (region) cue.region = region;
|
|
635
|
+
} else return VttTextParser.parsedLineValueAndInterpretation_(cue, word);
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
*
|
|
640
|
+
* @param {!Array<!shaka.text.CueRegion>} regions
|
|
641
|
+
* @param {string} id
|
|
642
|
+
* @return {?shaka.text.CueRegion}
|
|
643
|
+
* @private
|
|
644
|
+
*/
|
|
645
|
+
static getRegionById_(regions, id) {
|
|
646
|
+
const regionsWithId = regions.filter((region) => region.id == id);
|
|
647
|
+
if (!regionsWithId.length) {
|
|
648
|
+
shaka.log.warning("VTT parser could not find a region with id: ", id, " The region will be ignored.");
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
goog$1.asserts.assert(regionsWithId.length == 1, "VTTRegion ids should be unique!");
|
|
652
|
+
return regionsWithId[0];
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* @param {!shaka.text.Cue} cue
|
|
656
|
+
* @param {string} align
|
|
657
|
+
* @private
|
|
658
|
+
*/
|
|
659
|
+
static setTextAlign_(cue, align) {
|
|
660
|
+
const Cue = shaka.text.Cue;
|
|
661
|
+
if (align == "middle") cue.textAlign = Cue.textAlign.CENTER;
|
|
662
|
+
else {
|
|
663
|
+
goog$1.asserts.assert(align.toUpperCase() in Cue.textAlign, `${align.toUpperCase()} Should be in Cue.textAlign values!`);
|
|
664
|
+
cue.textAlign = Cue.textAlign[align.toUpperCase()];
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* @param {!shaka.text.Cue} cue
|
|
669
|
+
* @param {string} align
|
|
670
|
+
* @private
|
|
671
|
+
*/
|
|
672
|
+
static setPositionAlign_(cue, align) {
|
|
673
|
+
const Cue = shaka.text.Cue;
|
|
674
|
+
if (align == "line-left" || align == "start") cue.positionAlign = Cue.positionAlign.LEFT;
|
|
675
|
+
else if (align == "line-right" || align == "end") cue.positionAlign = Cue.positionAlign.RIGHT;
|
|
676
|
+
else if (align == "center" || align == "middle") cue.positionAlign = Cue.positionAlign.CENTER;
|
|
677
|
+
else cue.positionAlign = Cue.positionAlign.AUTO;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* @param {!shaka.text.Cue} cue
|
|
681
|
+
* @param {string} value
|
|
682
|
+
* @private
|
|
683
|
+
*/
|
|
684
|
+
static setVerticalWritingMode_(cue, value) {
|
|
685
|
+
const Cue = shaka.text.Cue;
|
|
686
|
+
if (value == "lr") cue.writingMode = Cue.writingMode.VERTICAL_LEFT_TO_RIGHT;
|
|
687
|
+
else cue.writingMode = Cue.writingMode.VERTICAL_RIGHT_TO_LEFT;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* @param {!shaka.text.Cue} cue
|
|
691
|
+
* @param {string} word
|
|
692
|
+
* @return {boolean}
|
|
693
|
+
* @private
|
|
694
|
+
*/
|
|
695
|
+
static parsedLineValueAndInterpretation_(cue, word) {
|
|
696
|
+
const Cue = shaka.text.Cue;
|
|
697
|
+
let results = null;
|
|
698
|
+
if (results = /^line:([\d.]+)%(?:,(start|end|center))?$/.exec(word)) {
|
|
699
|
+
cue.lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
|
700
|
+
cue.line = Number(results[1]);
|
|
701
|
+
if (results[2]) {
|
|
702
|
+
goog$1.asserts.assert(results[2].toUpperCase() in Cue.lineAlign, `${results[2].toUpperCase()} Should be in Cue.lineAlign values!`);
|
|
703
|
+
cue.lineAlign = Cue.lineAlign[results[2].toUpperCase()];
|
|
704
|
+
}
|
|
705
|
+
} else if (results = /^line:(-?\d+)(?:,(start|end|center))?$/.exec(word)) {
|
|
706
|
+
cue.lineInterpretation = Cue.lineInterpretation.LINE_NUMBER;
|
|
707
|
+
cue.line = Number(results[1]);
|
|
708
|
+
if (results[2]) {
|
|
709
|
+
goog$1.asserts.assert(results[2].toUpperCase() in Cue.lineAlign, `${results[2].toUpperCase()} Should be in Cue.lineAlign values!`);
|
|
710
|
+
cue.lineAlign = Cue.lineAlign[results[2].toUpperCase()];
|
|
711
|
+
}
|
|
712
|
+
} else return false;
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
/**
|
|
717
|
+
* @const {number}
|
|
718
|
+
* @private
|
|
719
|
+
*/
|
|
720
|
+
VttTextParser.MPEG_TIMESCALE_ = 9e4;
|
|
721
|
+
/**
|
|
722
|
+
* At this value, timestamps roll over in TS content.
|
|
723
|
+
* @const {number}
|
|
724
|
+
* @private
|
|
725
|
+
*/
|
|
726
|
+
VttTextParser.TS_ROLLOVER_ = 8589934592;
|
|
727
|
+
VttTextParser.register = (shakaNameSpace) => {
|
|
728
|
+
shakaNameSpace.text.TextEngine.registerParser("text/vtt", () => new VttTextParser());
|
|
729
|
+
shakaNameSpace.text.TextEngine.registerParser("text/vtt; codecs=\"vtt\"", () => new VttTextParser());
|
|
730
|
+
shakaNameSpace.text.TextEngine.registerParser("text/vtt; codecs=\"wvtt\"", () => new VttTextParser());
|
|
731
|
+
};
|
|
732
|
+
var VttTextParser_default = VttTextParser;
|
|
733
|
+
|
|
734
|
+
//#endregion
|
|
735
|
+
//#region src/plugins/shaka/UITextDisplayer.js
|
|
736
|
+
/*! @license
|
|
737
|
+
* Shaka Player
|
|
738
|
+
* Copyright 2016 Google LLC
|
|
739
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
740
|
+
*/
|
|
741
|
+
const shakaLog = {
|
|
742
|
+
debug: (...messages) => console.warn(...messages),
|
|
743
|
+
error: (...messages) => console.warn(...messages),
|
|
744
|
+
info: (...messages) => console.warn(...messages),
|
|
745
|
+
warning: (...messages) => console.warn(...messages),
|
|
746
|
+
alwaysWarn: (...messages) => console.warn(...messages)
|
|
747
|
+
};
|
|
748
|
+
const goog = { asserts: { assert: (result, message) => {
|
|
749
|
+
result || console.warn(message);
|
|
750
|
+
} } };
|
|
751
|
+
/**
|
|
752
|
+
* Returns info about provided lengthValue
|
|
753
|
+
* @example 100px => { value: 100, unit: 'px' }
|
|
754
|
+
* @param {?string} lengthValue
|
|
755
|
+
*
|
|
756
|
+
* @return {?{ value: number, unit: string }}
|
|
757
|
+
* @private
|
|
758
|
+
*/
|
|
759
|
+
const getLengthValueInfo_ = (lengthValue) => {
|
|
760
|
+
const matches = /(\d*\.?\d+)([a-z]+|%+)/.exec(lengthValue);
|
|
761
|
+
if (!matches) return null;
|
|
762
|
+
return {
|
|
763
|
+
value: Number(matches[1]),
|
|
764
|
+
unit: matches[2]
|
|
765
|
+
};
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
* Returns computed absolute length value in pixels based on cell
|
|
769
|
+
* and a video container size
|
|
770
|
+
* @param {number} value
|
|
771
|
+
* @param {!shaka.text.Cue} cue
|
|
772
|
+
* @param {HTMLElement} videoContainer
|
|
773
|
+
* @return {string}
|
|
774
|
+
*
|
|
775
|
+
* @private
|
|
776
|
+
*/
|
|
777
|
+
const getAbsoluteLengthInPixels_ = (value, cue, videoContainer) => {
|
|
778
|
+
return `${videoContainer.clientHeight * value / cue.cellResolution.rows}px`;
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* Inherits a property from the parent Cue elements. If the value is falsy,
|
|
782
|
+
* it is assumed to be inherited from the parent. This returns null if the
|
|
783
|
+
* value isn't found.
|
|
784
|
+
*
|
|
785
|
+
* @param {!Array<!shaka.text.Cue>} parents
|
|
786
|
+
* @param {function(!shaka.text.Cue):?T} cb
|
|
787
|
+
* @return {?T}
|
|
788
|
+
* @template T
|
|
789
|
+
* @private
|
|
790
|
+
*/
|
|
791
|
+
const inheritProperty_ = (parents, cb) => {
|
|
792
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
793
|
+
const val = cb(parents[i]);
|
|
794
|
+
if (val || val === 0) return val;
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
};
|
|
798
|
+
/**
|
|
799
|
+
* Converts length value to an absolute value in pixels.
|
|
800
|
+
* If lengthValue is already an absolute value it will not
|
|
801
|
+
* be modified. Relative lengthValue will be converted to an
|
|
802
|
+
* absolute value in pixels based on Computed Cell Size
|
|
803
|
+
*
|
|
804
|
+
* @param {string} lengthValue
|
|
805
|
+
* @param {!shaka.text.Cue} cue
|
|
806
|
+
* @param {HTMLElement} videoContainer
|
|
807
|
+
* @return {string}
|
|
808
|
+
* @private
|
|
809
|
+
*/
|
|
810
|
+
const convertLengthValue_ = (lengthValue, cue, videoContainer) => {
|
|
811
|
+
const lengthValueInfo = getLengthValueInfo_(lengthValue);
|
|
812
|
+
if (!lengthValueInfo) return lengthValue;
|
|
813
|
+
const { unit, value } = lengthValueInfo;
|
|
814
|
+
switch (unit) {
|
|
815
|
+
case "%": return getAbsoluteLengthInPixels_(value / 100, cue, videoContainer);
|
|
816
|
+
case "c": return getAbsoluteLengthInPixels_(value, cue, videoContainer);
|
|
817
|
+
default: return lengthValue;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
const removeDuplicates = (cues) => {
|
|
821
|
+
const uniqueCues = [];
|
|
822
|
+
for (const cue of cues) if (!uniqueCues.some((existingCue) => shaka.text.Cue.equal(cue, existingCue))) uniqueCues.push(cue);
|
|
823
|
+
return uniqueCues;
|
|
824
|
+
};
|
|
825
|
+
/**
|
|
826
|
+
* The text displayer plugin for the Shaka Player UI. Can also be used directly
|
|
827
|
+
* by providing an appropriate container element.
|
|
828
|
+
*
|
|
829
|
+
* @implements {shaka.extern.TextDisplayer}
|
|
830
|
+
* @final
|
|
831
|
+
* @export
|
|
832
|
+
*/
|
|
833
|
+
var UITextDisplayer = class {
|
|
834
|
+
/**
|
|
835
|
+
* Constructor.
|
|
836
|
+
* @param {HTMLMediaElement} video
|
|
837
|
+
* @param {HTMLElement} videoContainer
|
|
838
|
+
*/
|
|
839
|
+
constructor(video, videoContainer) {
|
|
840
|
+
window.shaka.log = shakaLog;
|
|
841
|
+
goog.asserts.assert(videoContainer, "videoContainer should be valid.");
|
|
842
|
+
if (!document.fullscreenEnabled) shaka.log.alwaysWarn("Using UITextDisplayer in a browser without Fullscreen API support causes subtitles to not be rendered in fullscreen");
|
|
843
|
+
/** @private {boolean} */
|
|
844
|
+
this.isTextVisible_ = false;
|
|
845
|
+
/** @private {!Array<!shaka.text.Cue>} */
|
|
846
|
+
this.cues_ = [];
|
|
847
|
+
/** @private {HTMLMediaElement} */
|
|
848
|
+
this.video_ = video;
|
|
849
|
+
/** @private {HTMLElement} */
|
|
850
|
+
this.videoContainer_ = videoContainer;
|
|
851
|
+
/** @private {?number} */
|
|
852
|
+
this.aspectRatio_ = null;
|
|
853
|
+
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
|
854
|
+
this.config_ = null;
|
|
855
|
+
/** @type {HTMLElement} */
|
|
856
|
+
this.textContainer_ = document.createElement("div");
|
|
857
|
+
this.textContainer_.classList.add("shaka-text-container");
|
|
858
|
+
this.textContainer_.style.textAlign = "center";
|
|
859
|
+
this.textContainer_.style.display = "flex";
|
|
860
|
+
this.textContainer_.style.flexDirection = "column";
|
|
861
|
+
this.textContainer_.style.alignItems = "center";
|
|
862
|
+
this.textContainer_.style.justifyContent = "flex-end";
|
|
863
|
+
this.videoContainer_.appendChild(this.textContainer_);
|
|
864
|
+
/** @private {shaka.util.Timer} */
|
|
865
|
+
this.captionsTimer_ = new shaka.util.Timer(() => {
|
|
866
|
+
if (!this.video_.paused) this.updateCaptions_();
|
|
867
|
+
});
|
|
868
|
+
this.configureCaptionsTimer_();
|
|
869
|
+
/**
|
|
870
|
+
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
871
|
+
* the cue (e.g. the HTML element to put nested cues inside).
|
|
872
|
+
* @private {Map<!shaka.text.Cue, !{
|
|
873
|
+
* cueElement: !HTMLElement,
|
|
874
|
+
* regionElement: HTMLElement,
|
|
875
|
+
* wrapper: !HTMLElement
|
|
876
|
+
* }>}
|
|
877
|
+
*/
|
|
878
|
+
this.currentCuesMap_ = /* @__PURE__ */ new Map();
|
|
879
|
+
/** @private {shaka.util.EventManager} */
|
|
880
|
+
this.eventManager_ = new shaka.util.EventManager();
|
|
881
|
+
this.eventManager_.listen(document, "fullscreenchange", () => {
|
|
882
|
+
this.updateCaptions_(true);
|
|
883
|
+
});
|
|
884
|
+
this.eventManager_.listen(this.video_, "seeking", () => {
|
|
885
|
+
this.updateCaptions_(true);
|
|
886
|
+
});
|
|
887
|
+
this.eventManager_.listen(this.video_, "ratechange", () => {
|
|
888
|
+
this.configureCaptionsTimer_();
|
|
889
|
+
});
|
|
890
|
+
this.eventManager_.listen(this.video_, "resize", () => {
|
|
891
|
+
const element = this.video_;
|
|
892
|
+
const width = element.videoWidth;
|
|
893
|
+
const height = element.videoHeight;
|
|
894
|
+
if (width && height) this.aspectRatio_ = width / height;
|
|
895
|
+
else this.aspectRatio_ = null;
|
|
896
|
+
});
|
|
897
|
+
/** @private {ResizeObserver} */
|
|
898
|
+
this.resizeObserver_ = null;
|
|
899
|
+
if ("ResizeObserver" in window) {
|
|
900
|
+
this.resizeObserver_ = new ResizeObserver(() => {
|
|
901
|
+
this.updateCaptions_(true);
|
|
902
|
+
});
|
|
903
|
+
this.resizeObserver_.observe(this.textContainer_);
|
|
904
|
+
}
|
|
905
|
+
/** @private {Map<string, !HTMLElement>} */
|
|
906
|
+
this.regionElements_ = /* @__PURE__ */ new Map();
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* @override
|
|
910
|
+
* @export
|
|
911
|
+
*/
|
|
912
|
+
configure(config) {
|
|
913
|
+
this.config_ = config;
|
|
914
|
+
this.configureCaptionsTimer_();
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* @override
|
|
918
|
+
* @export
|
|
919
|
+
*/
|
|
920
|
+
append(cues) {
|
|
921
|
+
const cuesList = [...this.cues_];
|
|
922
|
+
for (const cue of removeDuplicates(cues)) if (!cuesList.some((cueInList) => shaka.text.Cue.equal(cueInList, cue))) this.cues_.push(cue);
|
|
923
|
+
this.updateCaptions_();
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* @override
|
|
927
|
+
* @export
|
|
928
|
+
*/
|
|
929
|
+
destroy() {
|
|
930
|
+
if (!this.textContainer_) return Promise.resolve();
|
|
931
|
+
this.videoContainer_.removeChild(this.textContainer_);
|
|
932
|
+
this.textContainer_ = null;
|
|
933
|
+
this.isTextVisible_ = false;
|
|
934
|
+
this.cues_ = [];
|
|
935
|
+
if (this.captionsTimer_) {
|
|
936
|
+
this.captionsTimer_.stop();
|
|
937
|
+
this.captionsTimer_ = null;
|
|
938
|
+
}
|
|
939
|
+
this.currentCuesMap_.clear();
|
|
940
|
+
if (this.eventManager_) {
|
|
941
|
+
this.eventManager_.release();
|
|
942
|
+
this.eventManager_ = null;
|
|
943
|
+
}
|
|
944
|
+
if (this.resizeObserver_) {
|
|
945
|
+
this.resizeObserver_.disconnect();
|
|
946
|
+
this.resizeObserver_ = null;
|
|
947
|
+
}
|
|
948
|
+
return Promise.resolve();
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* @override
|
|
952
|
+
* @export
|
|
953
|
+
*/
|
|
954
|
+
remove(start, end) {
|
|
955
|
+
if (!this.textContainer_) return false;
|
|
956
|
+
const oldNumCues = this.cues_.length;
|
|
957
|
+
this.cues_ = this.cues_.filter((cue) => cue.startTime < start || cue.endTime >= end);
|
|
958
|
+
const forceUpdate = oldNumCues > this.cues_.length;
|
|
959
|
+
this.updateCaptions_(forceUpdate);
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* @override
|
|
964
|
+
* @export
|
|
965
|
+
*/
|
|
966
|
+
isTextVisible() {
|
|
967
|
+
return this.isTextVisible_;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* @override
|
|
971
|
+
* @export
|
|
972
|
+
*/
|
|
973
|
+
setTextVisibility(on$1) {
|
|
974
|
+
this.isTextVisible_ = on$1;
|
|
975
|
+
this.updateCaptions_(true);
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* @override
|
|
979
|
+
* @export
|
|
980
|
+
*/
|
|
981
|
+
setTextLanguage(language) {
|
|
982
|
+
if (language && language != "und") this.textContainer_.setAttribute("lang", language);
|
|
983
|
+
else this.textContainer_.setAttribute("lang", "");
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* @override
|
|
987
|
+
* @export
|
|
988
|
+
*/
|
|
989
|
+
enableTextDisplayer() {}
|
|
990
|
+
/**
|
|
991
|
+
* @private
|
|
992
|
+
*/
|
|
993
|
+
configureCaptionsTimer_() {
|
|
994
|
+
if (this.captionsTimer_) {
|
|
995
|
+
const updateTime = (this.config_ ? this.config_.captionsUpdatePeriod : .25) / Math.max(1, Math.abs(this.video_.playbackRate));
|
|
996
|
+
this.captionsTimer_.tickEvery(updateTime);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* @private
|
|
1001
|
+
*/
|
|
1002
|
+
isElementUnderTextContainer_(elemToCheck) {
|
|
1003
|
+
while (elemToCheck != null) {
|
|
1004
|
+
if (elemToCheck == this.textContainer_) return true;
|
|
1005
|
+
elemToCheck = elemToCheck.parentElement;
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* @param {!Array<!shaka.text.Cue>} cues
|
|
1011
|
+
* @param {!HTMLElement} container
|
|
1012
|
+
* @param {number} currentTime
|
|
1013
|
+
* @param {!Array<!shaka.text.Cue>} parents
|
|
1014
|
+
* @private
|
|
1015
|
+
*/
|
|
1016
|
+
updateCuesRecursive_(cues, container, currentTime, parents) {
|
|
1017
|
+
let updateDOM = false;
|
|
1018
|
+
/**
|
|
1019
|
+
* The elements to remove from the DOM.
|
|
1020
|
+
* Some of these elements may be added back again, if their corresponding
|
|
1021
|
+
* cue is in toPlant.
|
|
1022
|
+
* These elements are only removed if updateDOM is true.
|
|
1023
|
+
* @type {!Array<!HTMLElement>}
|
|
1024
|
+
*/
|
|
1025
|
+
const toUproot = [];
|
|
1026
|
+
/**
|
|
1027
|
+
* The cues whose corresponding elements should be in the DOM.
|
|
1028
|
+
* Some of these might be new, some might have been displayed beforehand.
|
|
1029
|
+
* These will only be added if updateDOM is true.
|
|
1030
|
+
* @type {!Array<!shaka.text.Cue>}
|
|
1031
|
+
*/
|
|
1032
|
+
const toPlant = [];
|
|
1033
|
+
for (const cue of cues) {
|
|
1034
|
+
parents.push(cue);
|
|
1035
|
+
let cueRegistry = this.currentCuesMap_.get(cue);
|
|
1036
|
+
const shouldBeDisplayed = cue.startTime <= currentTime && cue.endTime > currentTime;
|
|
1037
|
+
let wrapper = cueRegistry ? cueRegistry.wrapper : null;
|
|
1038
|
+
if (cueRegistry) {
|
|
1039
|
+
toUproot.push(cueRegistry.cueElement);
|
|
1040
|
+
if (cueRegistry.regionElement) toUproot.push(cueRegistry.regionElement);
|
|
1041
|
+
if (!shouldBeDisplayed) {
|
|
1042
|
+
updateDOM = true;
|
|
1043
|
+
this.currentCuesMap_.delete(cue);
|
|
1044
|
+
cueRegistry = null;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (shouldBeDisplayed) {
|
|
1048
|
+
toPlant.push(cue);
|
|
1049
|
+
if (!cueRegistry) {
|
|
1050
|
+
this.createCue_(cue, parents);
|
|
1051
|
+
cueRegistry = this.currentCuesMap_.get(cue);
|
|
1052
|
+
wrapper = cueRegistry.wrapper;
|
|
1053
|
+
updateDOM = true;
|
|
1054
|
+
} else if (!this.isElementUnderTextContainer_(wrapper)) updateDOM = true;
|
|
1055
|
+
}
|
|
1056
|
+
if (cue.nestedCues.length > 0 && wrapper) this.updateCuesRecursive_(cue.nestedCues, wrapper, currentTime, parents);
|
|
1057
|
+
const topCue = parents.pop();
|
|
1058
|
+
goog.asserts.assert(topCue == cue, "Parent cues should be kept in order");
|
|
1059
|
+
}
|
|
1060
|
+
if (updateDOM) {
|
|
1061
|
+
for (const element of toUproot) if (element.parentElement) element.parentElement.removeChild(element);
|
|
1062
|
+
toPlant.sort((a, b) => {
|
|
1063
|
+
if (a.startTime != b.startTime) return a.startTime - b.startTime;
|
|
1064
|
+
return a.endTime - b.endTime;
|
|
1065
|
+
});
|
|
1066
|
+
for (const cue of toPlant) {
|
|
1067
|
+
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
1068
|
+
goog.asserts.assert(cueRegistry, "cueRegistry should exist.");
|
|
1069
|
+
if (cueRegistry.regionElement) {
|
|
1070
|
+
if (cueRegistry.regionElement.contains(container)) cueRegistry.regionElement.removeChild(container);
|
|
1071
|
+
container.appendChild(cueRegistry.regionElement);
|
|
1072
|
+
cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
|
|
1073
|
+
} else container.appendChild(cueRegistry.cueElement);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Display the current captions.
|
|
1079
|
+
* @param {boolean=} forceUpdate
|
|
1080
|
+
* @private
|
|
1081
|
+
*/
|
|
1082
|
+
updateCaptions_(forceUpdate = false) {
|
|
1083
|
+
if (!this.textContainer_) return;
|
|
1084
|
+
const { currentTime } = this.video_;
|
|
1085
|
+
if (!this.isTextVisible_ || forceUpdate) {
|
|
1086
|
+
for (const regionElement of this.regionElements_.values()) regionElement.replaceChildren();
|
|
1087
|
+
this.textContainer_.replaceChildren();
|
|
1088
|
+
this.currentCuesMap_.clear();
|
|
1089
|
+
this.regionElements_.clear();
|
|
1090
|
+
}
|
|
1091
|
+
if (this.isTextVisible_) {
|
|
1092
|
+
const previousCuesMap = /* @__PURE__ */ new Map();
|
|
1093
|
+
if (goog.DEBUG) for (const cue of this.currentCuesMap_.keys()) previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
|
|
1094
|
+
this.updateCuesRecursive_(this.cues_, this.textContainer_, currentTime, []);
|
|
1095
|
+
if (goog.DEBUG) {
|
|
1096
|
+
for (const cue of previousCuesMap.keys()) if (!this.currentCuesMap_.has(cue)) {
|
|
1097
|
+
const { cueElement } = previousCuesMap.get(cue);
|
|
1098
|
+
goog.asserts.assert(!cueElement.parentNode, "Cue was not properly removed!");
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Compute a unique internal id:
|
|
1105
|
+
* Regions can reuse the id but have different dimensions, we need to
|
|
1106
|
+
* consider those differences
|
|
1107
|
+
* @param {shaka.text.CueRegion} region
|
|
1108
|
+
* @private
|
|
1109
|
+
*/
|
|
1110
|
+
generateRegionId_(region) {
|
|
1111
|
+
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
1112
|
+
const heightUnit = region.heightUnits == percentageUnit ? "%" : "px";
|
|
1113
|
+
const viewportAnchorUnit = region.viewportAnchorUnits == percentageUnit ? "%" : "px";
|
|
1114
|
+
return `${region.id}_${region.width}x${region.height}${heightUnit}-${region.viewportAnchorX}x${region.viewportAnchorY}${viewportAnchorUnit}`;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Get or create a region element corresponding to the cue region. These are
|
|
1118
|
+
* cached by ID.
|
|
1119
|
+
*
|
|
1120
|
+
* @param {!shaka.text.Cue} cue
|
|
1121
|
+
* @return {!HTMLElement}
|
|
1122
|
+
* @private
|
|
1123
|
+
*/
|
|
1124
|
+
getRegionElement_(cue) {
|
|
1125
|
+
const { region } = cue;
|
|
1126
|
+
const lineWidthMultiple = this.aspectRatio_ === 4 / 3 ? 2.5 : 1.9;
|
|
1127
|
+
const lineHeightMultiple = 5.33;
|
|
1128
|
+
const regionId = this.generateRegionId_(region);
|
|
1129
|
+
if (this.regionElements_.has(regionId)) return this.regionElements_.get(regionId);
|
|
1130
|
+
const regionElement = document.createElement("span");
|
|
1131
|
+
const linesUnit = shaka.text.CueRegion.units.LINES;
|
|
1132
|
+
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
1133
|
+
const pixelUnit = shaka.text.CueRegion.units.PX;
|
|
1134
|
+
let heightUnit = region.heightUnits == percentageUnit ? "%" : "px";
|
|
1135
|
+
let widthUnit = region.widthUnits == percentageUnit ? "%" : "px";
|
|
1136
|
+
const viewportAnchorUnit = region.viewportAnchorUnits == percentageUnit ? "%" : "px";
|
|
1137
|
+
regionElement.id = `shaka-text-region---${regionId}`;
|
|
1138
|
+
regionElement.classList.add("shaka-text-region");
|
|
1139
|
+
regionElement.style.position = "absolute";
|
|
1140
|
+
let regionHeight = region.height;
|
|
1141
|
+
let regionWidth = region.width;
|
|
1142
|
+
if (region.heightUnits === linesUnit) {
|
|
1143
|
+
regionHeight = region.height * lineHeightMultiple;
|
|
1144
|
+
heightUnit = "%";
|
|
1145
|
+
}
|
|
1146
|
+
if (region.widthUnits === linesUnit) {
|
|
1147
|
+
regionWidth = region.width * lineWidthMultiple;
|
|
1148
|
+
widthUnit = "%";
|
|
1149
|
+
}
|
|
1150
|
+
regionElement.style.height = regionHeight + heightUnit;
|
|
1151
|
+
regionElement.style.width = regionWidth + widthUnit;
|
|
1152
|
+
if (region.viewportAnchorUnits === linesUnit) {
|
|
1153
|
+
let top = region.viewportAnchorY / 75 * 100;
|
|
1154
|
+
const windowWidth = this.aspectRatio_ === 4 / 3 ? 160 : 210;
|
|
1155
|
+
let left = region.viewportAnchorX / windowWidth * 100;
|
|
1156
|
+
top -= region.regionAnchorY * regionHeight / 100;
|
|
1157
|
+
left -= region.regionAnchorX * regionWidth / 100;
|
|
1158
|
+
regionElement.style.top = `${top}%`;
|
|
1159
|
+
regionElement.style.left = `${left}%`;
|
|
1160
|
+
} else {
|
|
1161
|
+
regionElement.style.top = region.viewportAnchorY - region.regionAnchorY * regionHeight / 100 + viewportAnchorUnit;
|
|
1162
|
+
regionElement.style.left = region.viewportAnchorX - region.regionAnchorX * regionWidth / 100 + viewportAnchorUnit;
|
|
1163
|
+
}
|
|
1164
|
+
if (region.heightUnits !== pixelUnit && region.widthUnits !== pixelUnit && region.viewportAnchorUnits !== pixelUnit) {
|
|
1165
|
+
const top = parseInt(regionElement.style.top.slice(0, -1), 10) || 0;
|
|
1166
|
+
const left = parseInt(regionElement.style.left.slice(0, -1), 10) || 0;
|
|
1167
|
+
const height = parseInt(regionElement.style.height.slice(0, -1), 10) || 0;
|
|
1168
|
+
const width = parseInt(regionElement.style.width.slice(0, -1), 10) || 0;
|
|
1169
|
+
const realTop = Math.max(0, Math.min(100 - height, top));
|
|
1170
|
+
const realLeft = Math.max(0, Math.min(100 - width, left));
|
|
1171
|
+
regionElement.style.top = `${realTop}%`;
|
|
1172
|
+
regionElement.style.left = `${realLeft}%`;
|
|
1173
|
+
}
|
|
1174
|
+
regionElement.style.display = "flex";
|
|
1175
|
+
regionElement.style.flexDirection = "column";
|
|
1176
|
+
regionElement.style.alignItems = "center";
|
|
1177
|
+
if (cue.displayAlign == shaka.text.Cue.displayAlign.BEFORE) regionElement.style.justifyContent = "flex-start";
|
|
1178
|
+
else if (cue.displayAlign == shaka.text.Cue.displayAlign.CENTER) regionElement.style.justifyContent = "center";
|
|
1179
|
+
else regionElement.style.justifyContent = "flex-end";
|
|
1180
|
+
this.regionElements_.set(regionId, regionElement);
|
|
1181
|
+
return regionElement;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Creates the object for a cue.
|
|
1185
|
+
*
|
|
1186
|
+
* @param {!shaka.text.Cue} cue
|
|
1187
|
+
* @param {!Array<!shaka.text.Cue>} parents
|
|
1188
|
+
* @private
|
|
1189
|
+
*/
|
|
1190
|
+
createCue_(cue, parents) {
|
|
1191
|
+
const isNested = parents.length > 1;
|
|
1192
|
+
let type = isNested ? "span" : "div";
|
|
1193
|
+
if (cue.lineBreak) type = "br";
|
|
1194
|
+
if (cue.rubyTag) type = cue.rubyTag;
|
|
1195
|
+
const needWrapper = !isNested && cue.nestedCues.length > 0;
|
|
1196
|
+
const cueElement = document.createElement(type);
|
|
1197
|
+
if (type != "br") {
|
|
1198
|
+
this.setCaptionStyles_(cueElement, cue, parents, needWrapper);
|
|
1199
|
+
if (!cue.line) cueElement.classList.add(`shaka-cue-${cue.writingMode}`);
|
|
1200
|
+
}
|
|
1201
|
+
let regionElement = null;
|
|
1202
|
+
if (cue.region && cue.region.id) regionElement = this.getRegionElement_(cue);
|
|
1203
|
+
let wrapper = cueElement;
|
|
1204
|
+
if (needWrapper) {
|
|
1205
|
+
wrapper = document.createElement("span");
|
|
1206
|
+
wrapper.classList.add("shaka-text-wrapper");
|
|
1207
|
+
wrapper.style.backgroundColor = cue.backgroundColor;
|
|
1208
|
+
wrapper.style.lineHeight = "normal";
|
|
1209
|
+
cueElement.appendChild(wrapper);
|
|
1210
|
+
}
|
|
1211
|
+
this.currentCuesMap_.set(cue, {
|
|
1212
|
+
cueElement,
|
|
1213
|
+
wrapper,
|
|
1214
|
+
regionElement
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Compute cue position alignment
|
|
1219
|
+
* See https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment
|
|
1220
|
+
*
|
|
1221
|
+
* @param {!shaka.text.Cue} cue
|
|
1222
|
+
* @private
|
|
1223
|
+
*/
|
|
1224
|
+
computeCuePositionAlignment_(cue) {
|
|
1225
|
+
const { Cue } = shaka.text;
|
|
1226
|
+
const { direction, positionAlign, textAlign } = cue;
|
|
1227
|
+
if (positionAlign !== Cue.positionAlign.AUTO) return positionAlign;
|
|
1228
|
+
if (textAlign === Cue.textAlign.LEFT || textAlign === Cue.textAlign.START && direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT || textAlign === Cue.textAlign.END && direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT) return Cue.positionAlign.LEFT;
|
|
1229
|
+
if (textAlign === Cue.textAlign.RIGHT || textAlign === Cue.textAlign.START && direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT || textAlign === Cue.textAlign.END && direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT) return Cue.positionAlign.RIGHT;
|
|
1230
|
+
return Cue.positionAlign.CENTER;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* @param {!HTMLElement} cueElement
|
|
1234
|
+
* @param {!shaka.text.Cue} cue
|
|
1235
|
+
* @param {!Array<!shaka.text.Cue>} parents
|
|
1236
|
+
* @param {boolean} hasWrapper
|
|
1237
|
+
* @private
|
|
1238
|
+
*/
|
|
1239
|
+
setCaptionStyles_(cueElement, cue, parents, hasWrapper) {
|
|
1240
|
+
const { Cue } = shaka.text;
|
|
1241
|
+
const inherit = (cb) => inheritProperty_(parents, cb);
|
|
1242
|
+
const { style } = cueElement;
|
|
1243
|
+
const isLeaf = cue.nestedCues.length == 0;
|
|
1244
|
+
const isNested = parents.length > 1;
|
|
1245
|
+
style.whiteSpace = "pre-wrap";
|
|
1246
|
+
const text = cue.payload.replace(/\s+$/g, (match) => {
|
|
1247
|
+
return "\xA0".repeat(match.length);
|
|
1248
|
+
});
|
|
1249
|
+
style.webkitTextStrokeColor = cue.textStrokeColor;
|
|
1250
|
+
style.webkitTextStrokeWidth = cue.textStrokeWidth;
|
|
1251
|
+
style.color = cue.color;
|
|
1252
|
+
style.direction = cue.direction;
|
|
1253
|
+
style.opacity = cue.opacity;
|
|
1254
|
+
style.paddingLeft = convertLengthValue_(cue.linePadding, cue, this.videoContainer_);
|
|
1255
|
+
style.paddingRight = convertLengthValue_(cue.linePadding, cue, this.videoContainer_);
|
|
1256
|
+
style.textCombineUpright = cue.textCombineUpright;
|
|
1257
|
+
style.textShadow = cue.textShadow;
|
|
1258
|
+
if (cue.backgroundImage) {
|
|
1259
|
+
style.backgroundImage = `url('${cue.backgroundImage}')`;
|
|
1260
|
+
style.backgroundRepeat = "no-repeat";
|
|
1261
|
+
style.backgroundSize = "contain";
|
|
1262
|
+
style.backgroundPosition = "center";
|
|
1263
|
+
if (cue.backgroundColor) style.backgroundColor = cue.backgroundColor;
|
|
1264
|
+
style.width = "100%";
|
|
1265
|
+
style.height = "100%";
|
|
1266
|
+
} else {
|
|
1267
|
+
let elem;
|
|
1268
|
+
if (cue.nestedCues.length) elem = cueElement;
|
|
1269
|
+
else {
|
|
1270
|
+
elem = document.createElement("span");
|
|
1271
|
+
cueElement.appendChild(elem);
|
|
1272
|
+
}
|
|
1273
|
+
if (cue.border) elem.style.border = cue.border;
|
|
1274
|
+
if (!hasWrapper) {
|
|
1275
|
+
const bgColor = inherit((c) => c.backgroundColor);
|
|
1276
|
+
if (bgColor) elem.style.backgroundColor = bgColor;
|
|
1277
|
+
else if (text) elem.style.backgroundColor = "rgba(0, 0, 0, 0)";
|
|
1278
|
+
}
|
|
1279
|
+
if (text) elem.textContent = text;
|
|
1280
|
+
}
|
|
1281
|
+
if (/ruby|rt/i.test(cueElement.tagName)) {} else if (isNested && !parents[parents.length - 1].isContainer) style.display = "inline";
|
|
1282
|
+
else {
|
|
1283
|
+
style.display = "flex";
|
|
1284
|
+
style.flexDirection = "column";
|
|
1285
|
+
style.alignItems = "center";
|
|
1286
|
+
if (cue.textAlign == Cue.textAlign.LEFT || cue.textAlign == Cue.textAlign.START) {
|
|
1287
|
+
style.width = "100%";
|
|
1288
|
+
style.alignItems = "start";
|
|
1289
|
+
} else if (cue.textAlign == Cue.textAlign.RIGHT || cue.textAlign == Cue.textAlign.END) {
|
|
1290
|
+
style.width = "100%";
|
|
1291
|
+
style.alignItems = "end";
|
|
1292
|
+
}
|
|
1293
|
+
if (/vertical/.test(cue.writingMode)) style.justifyContent = "flex-start";
|
|
1294
|
+
else if (cue.displayAlign == Cue.displayAlign.BEFORE) style.justifyContent = "flex-start";
|
|
1295
|
+
else if (cue.displayAlign == Cue.displayAlign.CENTER) style.justifyContent = "center";
|
|
1296
|
+
else style.justifyContent = "flex-end";
|
|
1297
|
+
}
|
|
1298
|
+
if (!isLeaf) style.margin = "0";
|
|
1299
|
+
style.fontFamily = cue.fontFamily;
|
|
1300
|
+
style.fontWeight = cue.fontWeight.toString();
|
|
1301
|
+
style.fontStyle = cue.fontStyle;
|
|
1302
|
+
style.letterSpacing = cue.letterSpacing;
|
|
1303
|
+
style.fontSize = convertLengthValue_(cue.fontSize, cue, this.videoContainer_);
|
|
1304
|
+
let line = !isNested && /vertical/.test(cue.writingMode) && cue.line == null ? 0 : cue.line;
|
|
1305
|
+
if (line != null) {
|
|
1306
|
+
let { lineInterpretation } = cue;
|
|
1307
|
+
if (lineInterpretation == Cue.lineInterpretation.LINE_NUMBER) {
|
|
1308
|
+
lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
|
1309
|
+
let maxLines = 16;
|
|
1310
|
+
if (this.aspectRatio_ && this.aspectRatio_ < 1) maxLines = 32;
|
|
1311
|
+
if (line < 0) line = 100 + line / maxLines * 100;
|
|
1312
|
+
else line = line / maxLines * 100;
|
|
1313
|
+
}
|
|
1314
|
+
if (lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
1315
|
+
style.position = "absolute";
|
|
1316
|
+
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
1317
|
+
style.width = "100%";
|
|
1318
|
+
if (cue.lineAlign == Cue.lineAlign.START) style.top = `${line}%`;
|
|
1319
|
+
else if (cue.lineAlign == Cue.lineAlign.END) style.bottom = `${100 - line}%`;
|
|
1320
|
+
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
1321
|
+
style.height = "100%";
|
|
1322
|
+
if (cue.lineAlign == Cue.lineAlign.START) style.left = `${3 + line}%`;
|
|
1323
|
+
else if (cue.lineAlign == Cue.lineAlign.END) style.right = `${97 - line}%`;
|
|
1324
|
+
} else {
|
|
1325
|
+
style.height = "100%";
|
|
1326
|
+
if (cue.lineAlign == Cue.lineAlign.START) style.right = `${3 + line}%`;
|
|
1327
|
+
else if (cue.lineAlign == Cue.lineAlign.END) style.left = `${97 - line}%`;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
style.lineHeight = cue.lineHeight;
|
|
1332
|
+
if (!isNested && /vertical/.test(cue.writingMode) && cue.position == null) cue.position = 1;
|
|
1333
|
+
const computedPositionAlign = this.computeCuePositionAlignment_(cue);
|
|
1334
|
+
const computedCuePosition = !isNested && /vertical/.test(cue.writingMode) && cue.position == null ? 1 : cue.position;
|
|
1335
|
+
if (/ruby|rt/i.test(cueElement.tagName)) {} else if (computedPositionAlign == Cue.positionAlign.LEFT) {
|
|
1336
|
+
style.cssFloat = "left";
|
|
1337
|
+
if (computedCuePosition !== null) {
|
|
1338
|
+
style.position = "absolute";
|
|
1339
|
+
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
1340
|
+
style.left = `${computedCuePosition}%`;
|
|
1341
|
+
style.width = "auto";
|
|
1342
|
+
} else style.top = `${computedCuePosition}%`;
|
|
1343
|
+
}
|
|
1344
|
+
} else if (computedPositionAlign == Cue.positionAlign.RIGHT) {
|
|
1345
|
+
style.cssFloat = "right";
|
|
1346
|
+
if (computedCuePosition !== null) {
|
|
1347
|
+
style.position = "absolute";
|
|
1348
|
+
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
1349
|
+
style.right = `${100 - computedCuePosition}%`;
|
|
1350
|
+
style.width = "auto";
|
|
1351
|
+
} else style.bottom = `${computedCuePosition}%`;
|
|
1352
|
+
}
|
|
1353
|
+
} else if (computedCuePosition !== null && computedCuePosition != 50) {
|
|
1354
|
+
style.position = "absolute";
|
|
1355
|
+
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
1356
|
+
style.left = `${computedCuePosition}%`;
|
|
1357
|
+
style.width = "auto";
|
|
1358
|
+
} else style.top = `${computedCuePosition}%`;
|
|
1359
|
+
}
|
|
1360
|
+
style.textAlign = cue.textAlign;
|
|
1361
|
+
style.textDecoration = cue.textDecoration.join(" ");
|
|
1362
|
+
style.writingMode = cue.writingMode;
|
|
1363
|
+
if (!("writingMode" in document.documentElement.style) || style.writingMode != cue.writingMode) style.webkitWritingMode = cue.writingMode;
|
|
1364
|
+
if (cue.size) {
|
|
1365
|
+
if (cue.writingMode != Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) style.height = `${cue.size}%`;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
var UITextDisplayer_default = UITextDisplayer;
|
|
1370
|
+
|
|
1371
|
+
//#endregion
|
|
1372
|
+
//#region src/playerCore/shaka.js
|
|
1373
|
+
const getQualityItem = (track) => ({
|
|
1374
|
+
id: track.originalVideoId,
|
|
1375
|
+
bitrate: track.videoBandwidth,
|
|
1376
|
+
width: track.width,
|
|
1377
|
+
height: track.height,
|
|
1378
|
+
codec: track.videoCodec,
|
|
1379
|
+
frameRate: track.frameRate
|
|
1380
|
+
});
|
|
1381
|
+
const rewriteDashManifest = (type, response) => {
|
|
1382
|
+
const { MANIFEST } = window.shaka.net.NetworkingEngine.RequestType;
|
|
1383
|
+
if (type !== MANIFEST) return response;
|
|
1384
|
+
const data = new TextDecoder().decode(response.data);
|
|
1385
|
+
if (/<MPD/i.test(data)) response.data = new TextEncoder().encode(fixDashManifest_default(data));
|
|
1386
|
+
return response;
|
|
1387
|
+
};
|
|
1388
|
+
const sleep = (ms) => new Promise((r) => {
|
|
1389
|
+
setTimeout(r, ms);
|
|
1390
|
+
});
|
|
1391
|
+
const MAX_ERROR_RETRY = 3;
|
|
1392
|
+
const RETRY_DELAY = 5e3;
|
|
1393
|
+
const isSegmentNotFound = (detail) => detail?.code === window.shaka.util.Error.Code.BAD_HTTP_STATUS && detail.severity === window.shaka.util.Error.Severity.RECOVERABLE && detail?.data[1] === 404;
|
|
1394
|
+
const syncStartOver = (media, player) => {
|
|
1395
|
+
media.addEventListener("timeupdate", () => {
|
|
1396
|
+
const { start, end } = player.seekRange();
|
|
1397
|
+
const defaultLiveOffset = end - Date.now() / 1e3;
|
|
1398
|
+
const seekDurationDiff = end - start - Date.now() / 1e3;
|
|
1399
|
+
if (media.defaultLiveOffset && Math.abs(defaultLiveOffset - media.defaultLiveOffset) + Math.abs(seekDurationDiff - media.seekDurationDiff) > 7) {
|
|
1400
|
+
media.defaultLiveOffset = defaultLiveOffset;
|
|
1401
|
+
media.seekDurationDiff = seekDurationDiff;
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
};
|
|
1405
|
+
const proxyTextTrackEvents = (media, { player }) => {
|
|
1406
|
+
const handleTextTrackChange = () => media.dispatchEvent(new CustomEvent("textTrackChange", { detail: { getTextTracks: () => player.isTextTrackVisible() ? player.getTextTracks() : [] } }));
|
|
1407
|
+
player.addEventListener("texttrackvisibility", handleTextTrackChange);
|
|
1408
|
+
player.addEventListener("textchange", handleTextTrackChange);
|
|
1409
|
+
};
|
|
1410
|
+
const loadShaka = async (videoElement, config = {}, options = {}) => {
|
|
1411
|
+
window.shakaMediaKeysPolyfill = true;
|
|
1412
|
+
const shakaModule = await import("shaka-player");
|
|
1413
|
+
const shaka$2 = shakaModule.default || shakaModule;
|
|
1414
|
+
window.shaka = shaka$2;
|
|
1415
|
+
shaka$2.polyfill.installAll();
|
|
1416
|
+
const player = new shaka$2.Player();
|
|
1417
|
+
getUrlObject_default((mediaSource) => {
|
|
1418
|
+
if (player) player.mediaSource = mediaSource;
|
|
1419
|
+
});
|
|
1420
|
+
player.attach(videoElement);
|
|
1421
|
+
player.configure({
|
|
1422
|
+
...config,
|
|
1423
|
+
manifest: {
|
|
1424
|
+
dash: { ignoreSuggestedPresentationDelay: true },
|
|
1425
|
+
retryParameters: { maxAttempts: 6 },
|
|
1426
|
+
...config.manifest
|
|
1427
|
+
},
|
|
1428
|
+
streaming: {
|
|
1429
|
+
autoLowLatencyMode: true,
|
|
1430
|
+
safeSeekOffset: 0,
|
|
1431
|
+
rebufferingGoal: 0,
|
|
1432
|
+
...isSafari() && { preferNativeHls: true },
|
|
1433
|
+
...config.streaming
|
|
1434
|
+
},
|
|
1435
|
+
...options.container && document.fullscreenEnabled && { textDisplayFactory: () => new UITextDisplayer_default(videoElement, options.container) }
|
|
1436
|
+
});
|
|
1437
|
+
syncStartOver(videoElement, player);
|
|
1438
|
+
player.addEventListener("error", (event) => {
|
|
1439
|
+
console.log(event);
|
|
1440
|
+
const { detail = {} } = event;
|
|
1441
|
+
const error = /* @__PURE__ */ new Error(`Player: ${detail.code}/${detail.name}`);
|
|
1442
|
+
if (!detail || /The video element has thrown a media error|Video element triggered an Error/.test(detail.message) || detail.data?.some?.((message) => /Unsupported source type/i.test(message))) return;
|
|
1443
|
+
if (detail.code == 3016) return;
|
|
1444
|
+
if (isSegmentNotFound(detail)) {
|
|
1445
|
+
setTimeout(() => {
|
|
1446
|
+
if (player.errorRetry < MAX_ERROR_RETRY) {
|
|
1447
|
+
player.errorRetry += 1;
|
|
1448
|
+
player.reload();
|
|
1449
|
+
}
|
|
1450
|
+
}, RETRY_DELAY);
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
videoElement.dispatchEvent(Object.assign(new CustomEvent("error"), {
|
|
1454
|
+
error: detail,
|
|
1455
|
+
message: `Player Error: ${detail.code}/${detail.message?.split(" ", 3)[2]}`
|
|
1456
|
+
}));
|
|
1457
|
+
if (detail.code === 1001 || detail.severity === 2) {
|
|
1458
|
+
console.info("Stream unavailable, unload source");
|
|
1459
|
+
player.unload();
|
|
1460
|
+
}
|
|
1461
|
+
window.Sentry?.captureException(error);
|
|
1462
|
+
});
|
|
1463
|
+
player.addEventListener("loaded", () => {
|
|
1464
|
+
videoElement.dispatchEvent(new CustomEvent("canplay", {}));
|
|
1465
|
+
});
|
|
1466
|
+
player.addEventListener("adaptation", (event) => {
|
|
1467
|
+
const { videoBandwidth, width, height } = event.newTrack;
|
|
1468
|
+
if (event.oldTrack.height !== height) videoElement.dispatchEvent(new CustomEvent("downloadQualityChange", { detail: {
|
|
1469
|
+
bitrate: parseInt(videoBandwidth / 1e3, 10),
|
|
1470
|
+
height,
|
|
1471
|
+
width
|
|
1472
|
+
} }));
|
|
1473
|
+
});
|
|
1474
|
+
player.addEventListener("variantchanged", (event) => {
|
|
1475
|
+
const { language } = event.newTrack;
|
|
1476
|
+
if (event.oldTrack.language !== language) videoElement.dispatchEvent(new CustomEvent("audioTrackChange", { detail: { lang: language } }));
|
|
1477
|
+
});
|
|
1478
|
+
proxyTextTrackEvents(videoElement, { player });
|
|
1479
|
+
const extensionOptions = {
|
|
1480
|
+
requestHandlers: [],
|
|
1481
|
+
responseHandlers: [rewriteDashManifest]
|
|
1482
|
+
};
|
|
1483
|
+
if (isSafari()) setupKKFariplay_default(player, extensionOptions);
|
|
1484
|
+
const getAvailableVideoQualities = () => player.getVariantTracks().reduce((trackList, currentTrack) => {
|
|
1485
|
+
if (!trackList.find((track) => track.height === currentTrack.height)) trackList.push(getQualityItem(currentTrack));
|
|
1486
|
+
return trackList;
|
|
1487
|
+
}, []);
|
|
1488
|
+
const getVideoQuality = () => {
|
|
1489
|
+
const activeTrack = player.getVariantTracks().find((track) => track.active);
|
|
1490
|
+
if (!activeTrack) return {};
|
|
1491
|
+
return getQualityItem(activeTrack);
|
|
1492
|
+
};
|
|
1493
|
+
VttTextParser_default.register(shaka$2);
|
|
1494
|
+
HttpFetchPlugin_default.register(shaka$2);
|
|
1495
|
+
const networkEngine = player.getNetworkingEngine();
|
|
1496
|
+
networkEngine.registerRequestFilter((type, request) => extensionOptions.requestHandlers.reduce((merged, handler) => handler(type, merged, player, extensionOptions), request));
|
|
1497
|
+
networkEngine.registerResponseFilter((type, response) => extensionOptions.responseHandlers.reduce((merged, handler) => handler(type, merged, player, extensionOptions), response));
|
|
1498
|
+
const load = player.load.bind(player);
|
|
1499
|
+
const extensions = {
|
|
1500
|
+
shaka: shaka$2,
|
|
1501
|
+
errorRetry: 0,
|
|
1502
|
+
lastLoad: {},
|
|
1503
|
+
get mediaSource() {
|
|
1504
|
+
return player.mediaSource;
|
|
1505
|
+
},
|
|
1506
|
+
configureExtensions: ({ drm } = {}) => {
|
|
1507
|
+
extensionOptions.drm = drm;
|
|
1508
|
+
},
|
|
1509
|
+
load: async (assetUri, startTime, mimeType) => {
|
|
1510
|
+
await sleep(0);
|
|
1511
|
+
player.lastLoad = {
|
|
1512
|
+
assetUri,
|
|
1513
|
+
startTime,
|
|
1514
|
+
mimeType
|
|
1515
|
+
};
|
|
1516
|
+
return load(assetUri, startTime, mimeType);
|
|
1517
|
+
},
|
|
1518
|
+
reload: async () => {
|
|
1519
|
+
await player.unload();
|
|
1520
|
+
const { assetUri, startTime, mimeType } = player.lastLoad;
|
|
1521
|
+
return player.load(assetUri, startTime, mimeType);
|
|
1522
|
+
},
|
|
1523
|
+
getPlaybackSpeed: () => videoElement.playbackRate,
|
|
1524
|
+
getVideoElement: () => videoElement,
|
|
1525
|
+
getVideoQuality,
|
|
1526
|
+
getAvailableVideoQualities,
|
|
1527
|
+
isAlive: () => player.getLoadMode() !== shaka$2.Player.LoadMode.DESTROYED,
|
|
1528
|
+
on: player.addEventListener.bind(player)
|
|
1529
|
+
};
|
|
1530
|
+
Object.assign(player, extensions);
|
|
1531
|
+
return player;
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
//#endregion
|
|
1535
|
+
//#region src/playerCore/loadPlayer.js
|
|
1536
|
+
const loadPlayer = async (videoElement, { container, source, shaka: shaka$2, bitmovin }) => {
|
|
1537
|
+
if (isIOS()) on(document, "visibilitychange", () => setTimeout(() => videoElement.pause(), 50));
|
|
1538
|
+
printVersion_default();
|
|
1539
|
+
let player;
|
|
1540
|
+
if (source?.native) player = await loadNative({ videoElement });
|
|
1541
|
+
else if (shaka$2 || !bitmovin) {
|
|
1542
|
+
player = await loadShaka(videoElement, shaka$2, { container });
|
|
1543
|
+
videoElement.dispatchEvent(new CustomEvent("playerStarted"));
|
|
1544
|
+
} else if (bitmovin?.load) {
|
|
1545
|
+
player = await bitmovin.load({
|
|
1546
|
+
container,
|
|
1547
|
+
videoElement,
|
|
1548
|
+
config: bitmovin
|
|
1549
|
+
});
|
|
1550
|
+
videoElement.dispatchEvent(new CustomEvent("playerStarted"));
|
|
1551
|
+
}
|
|
1552
|
+
reloadOnLiveStall(videoElement, { reload: () => player.reload() });
|
|
1553
|
+
player.preferredSettings = {};
|
|
1554
|
+
player.modules = {};
|
|
1555
|
+
return player;
|
|
1556
|
+
};
|
|
1557
|
+
var loadPlayer_default = loadPlayer;
|
|
1558
|
+
|
|
1559
|
+
//#endregion
|
|
1560
|
+
export { handleError as n, loadPlayer_default as t };
|