jassub 1.1.8 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/jassub.es.js CHANGED
@@ -1,491 +1,491 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
- var __hasOwnProp = Object.prototype.hasOwnProperty;
4
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
- var __spreadValues = (a, b) => {
7
- for (var prop in b || (b = {}))
8
- if (__hasOwnProp.call(b, prop))
9
- __defNormalProp(a, prop, b[prop]);
10
- if (__getOwnPropSymbols)
11
- for (var prop of __getOwnPropSymbols(b)) {
12
- if (__propIsEnum.call(b, prop))
13
- __defNormalProp(a, prop, b[prop]);
14
- }
15
- return a;
16
- };
17
- var __publicField = (obj, key, value) => {
18
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
- return value;
20
- };
21
- if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype) && "getVideoPlaybackQuality" in HTMLVideoElement.prototype) {
22
- HTMLVideoElement.prototype._rvfcpolyfillmap = {};
23
- HTMLVideoElement.prototype.requestVideoFrameCallback = function(callback) {
24
- const quality = this.getVideoPlaybackQuality();
25
- const baseline = this.mozPresentedFrames || quality.totalVideoFrames - quality.droppedVideoFrames;
26
- const check = (old, now2) => {
27
- const newquality = this.getVideoPlaybackQuality();
28
- const presentedFrames = this.mozPresentedFrames || newquality.totalVideoFrames - newquality.droppedVideoFrames;
29
- if (presentedFrames > baseline) {
30
- const processingDuration = this.mozFrameDelay || newquality.totalFrameDelay - quality.totalFrameDelay || 0;
31
- const timediff = now2 - old;
32
- callback(now2, {
33
- presentationTime: now2 + processingDuration * 1e3,
34
- expectedDisplayTime: now2 + timediff,
35
- width: this.videoWidth,
36
- height: this.videoHeight,
37
- mediaTime: Math.max(0, this.currentTime || 0) + timediff / 1e3,
38
- presentedFrames,
39
- processingDuration
40
- });
41
- delete this._rvfcpolyfillmap[handle];
42
- } else {
43
- this._rvfcpolyfillmap[handle] = requestAnimationFrame((newer) => check(now2, newer));
44
- }
45
- };
46
- const handle = Date.now();
47
- const now = performance.now();
48
- this._rvfcpolyfillmap[handle] = requestAnimationFrame((newer) => check(now, newer));
49
- return handle;
50
- };
51
- HTMLVideoElement.prototype.cancelVideoFrameCallback = function(handle) {
52
- cancelAnimationFrame(this._rvfcpolyfillmap[handle]);
53
- delete this._rvfcpolyfillmap[handle];
54
- };
55
- }
56
- const _JASSUB = class extends EventTarget {
57
- constructor(options = {}) {
58
- var _a, _b, _c;
59
- super();
60
- if (!globalThis.Worker) {
61
- this.destroy("Worker not supported");
62
- }
63
- _JASSUB._test();
64
- const _blendMode = options.blendMode || "js";
65
- const _asyncRender = typeof createImageBitmap !== "undefined" && ((_a = options.asyncRender) != null ? _a : true);
66
- const _offscreenRender = typeof OffscreenCanvas !== "undefined" && ((_b = options.offscreenRender) != null ? _b : true);
67
- this._onDemandRender = "requestVideoFrameCallback" in HTMLVideoElement.prototype && ((_c = options.onDemandRender) != null ? _c : true);
68
- this.timeOffset = options.timeOffset || 0;
69
- this._video = options.video;
70
- this._canvasParent = null;
71
- if (this._video) {
72
- this._canvasParent = document.createElement("div");
73
- this._canvasParent.className = "JASSUB";
74
- this._canvasParent.style.position = "relative";
75
- if (this._video.nextSibling) {
76
- this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling);
77
- } else {
78
- this._video.parentNode.appendChild(this._canvasParent);
79
- }
80
- } else if (!this._canvas) {
81
- this.destroy("Don't know where to render: you should give video or canvas in options.");
82
- }
83
- this._canvas = options.canvas || document.createElement("canvas");
84
- this._canvas.style.display = "block";
85
- this._canvas.style.position = "absolute";
86
- this._canvas.style.pointerEvents = "none";
87
- this._canvasParent.appendChild(this._canvas);
88
- this._bufferCanvas = document.createElement("canvas");
89
- this._bufferCtx = this._bufferCanvas.getContext("2d");
90
- this._canvasctrl = _offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas;
91
- this._ctx = !_offscreenRender && this._canvasctrl.getContext("2d");
92
- this._lastRenderTime = 0;
93
- this.debug = !!options.debug;
94
- this.prescaleFactor = options.prescaleFactor || 1;
95
- this.prescaleHeightLimit = options.prescaleHeightLimit || 1080;
96
- this.maxRenderHeight = options.maxRenderHeight || 0;
97
- this._worker = new Worker(_JASSUB._supportsWebAssembly ? options.workerUrl || "jassub-worker.js" : options.legacyWorkerUrl || "jassub-worker-legacy.js");
98
- this._worker.onmessage = (e) => this._onmessage(e);
99
- this._worker.onerror = (e) => this._error(e);
100
- this._worker.postMessage({
101
- target: "init",
102
- asyncRender: _asyncRender,
103
- width: this._canvas.width,
104
- height: this._canvas.height,
105
- preMain: true,
106
- blendMode: _blendMode,
107
- subUrl: options.subUrl,
108
- subContent: options.subContent || null,
109
- fonts: options.fonts || [],
110
- availableFonts: options.availableFonts || { "liberation sans": "./default.woff2" },
111
- fallbackFont: options.fallbackFont || "liberation sans",
112
- debug: this.debug,
113
- targetFps: options.targetFps || 24,
114
- dropAllAnimations: options.dropAllAnimations,
115
- libassMemoryLimit: options.libassMemoryLimit || 0,
116
- libassGlyphLimit: options.libassGlyphLimit || 0,
117
- hasAlphaBug: _JASSUB._hasAlphaBug,
118
- useLocalFonts: "queryLocalFonts" in self && !!options.useLocalFonts
119
- });
120
- if (_offscreenRender === true)
121
- this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
122
- this.setVideo(options.video);
123
- if (this._onDemandRender) {
124
- this.busy = false;
125
- this._video.requestVideoFrameCallback(this._demandRender.bind(this));
126
- }
127
- }
128
- static _test() {
129
- if (_JASSUB._supportsWebAssembly !== null)
130
- return null;
131
- const canvas1 = document.createElement("canvas");
132
- const ctx1 = canvas1.getContext("2d");
133
- if (typeof ImageData.prototype.constructor === "function") {
134
- try {
135
- new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1);
136
- } catch (e) {
137
- console.log("detected that ImageData is not constructable despite browser saying so");
138
- window.ImageData = function(data, width, height) {
139
- const imageData = ctx1.createImageData(width, height);
140
- if (data)
141
- imageData.data.set(data);
142
- return imageData;
143
- };
144
- }
145
- }
146
- try {
147
- if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
148
- const module = new WebAssembly.Module(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0));
149
- if (module instanceof WebAssembly.Module) {
150
- _JASSUB._supportsWebAssembly = new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
151
- }
152
- }
153
- } catch (e) {
154
- _JASSUB._supportsWebAssembly = false;
155
- }
156
- const canvas2 = document.createElement("canvas");
157
- const ctx2 = canvas2.getContext("2d");
158
- canvas1.width = canvas2.width = 1;
159
- canvas1.height = canvas2.height = 1;
160
- ctx1.clearRect(0, 0, 1, 1);
161
- ctx2.clearRect(0, 0, 1, 1);
162
- const prePut = ctx2.getImageData(0, 0, 1, 1).data;
163
- ctx1.putImageData(new ImageData(new Uint8ClampedArray([0, 255, 0, 0]), 1, 1), 0, 0);
164
- ctx2.drawImage(canvas1, 0, 0);
165
- const postPut = ctx2.getImageData(0, 0, 1, 1).data;
166
- _JASSUB._hasAlphaBug = prePut[1] !== postPut[1];
167
- if (_JASSUB._hasAlphaBug)
168
- console.log("Detected a browser having issue with transparent pixels, applying workaround");
169
- canvas2.remove();
170
- }
171
- resize(width = 0, height = 0, top = 0, left = 0) {
172
- let videoSize = null;
173
- if ((!width || !height) && this._video) {
174
- videoSize = this._getVideoPosition();
175
- const newsize = this._computeCanvasSize(videoSize.width || 0 * (window.devicePixelRatio || 1), videoSize.height || 0 * (window.devicePixelRatio || 1));
176
- width = newsize.width;
177
- height = newsize.height;
178
- top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
179
- left = videoSize.x;
180
- }
181
- if (this._canvas.style.top !== top + "px" || this._canvas.style.left !== left + "px") {
182
- if (videoSize != null) {
183
- this._canvas.style.top = top + "px";
184
- this._canvas.style.left = left + "px";
185
- this._canvas.style.width = videoSize.width + "px";
186
- this._canvas.style.height = videoSize.height + "px";
187
- }
188
- if (!(this._canvasctrl.width === width && this._canvasctrl.height === height)) {
189
- if (this._resizeTimeoutBuffer) {
190
- clearTimeout(this._resizeTimeoutBuffer);
191
- this._resizeTimeoutBuffer = setTimeout(() => {
192
- this._resizeTimeoutBuffer = void 0;
193
- this._canvasctrl.width = width;
194
- this._canvasctrl.height = height;
195
- this.sendMessage("canvas", { width, height });
196
- }, 100);
197
- } else {
198
- this._canvasctrl.width = width;
199
- this._canvasctrl.height = height;
200
- this.sendMessage("canvas", { width, height });
201
- this._resizeTimeoutBuffer = setTimeout(() => {
202
- this._resizeTimeoutBuffer = void 0;
203
- }, 100);
204
- }
205
- }
206
- }
207
- }
208
- _getVideoPosition() {
209
- const videoRatio = this._video.videoWidth / this._video.videoHeight;
210
- const { offsetWidth, offsetHeight } = this._video;
211
- const elementRatio = offsetWidth / offsetHeight;
212
- let width = offsetWidth;
213
- let height = offsetHeight;
214
- if (elementRatio > videoRatio) {
215
- width = Math.floor(offsetHeight * videoRatio);
216
- } else {
217
- height = Math.floor(offsetWidth / videoRatio);
218
- }
219
- const x = (offsetWidth - width) / 2;
220
- const y = (offsetHeight - height) / 2;
221
- return { width, height, x, y };
222
- }
223
- _computeCanvasSize(width = 0, height = 0) {
224
- const scalefactor = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor;
225
- if (height <= 0 || width <= 0) {
226
- width = 0;
227
- height = 0;
228
- } else {
229
- const sgn = scalefactor < 1 ? -1 : 1;
230
- let newH = height;
231
- if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
232
- newH *= scalefactor;
233
- } else if (sgn * newH < sgn * this.prescaleHeightLimit) {
234
- newH = this.prescaleHeightLimit;
235
- }
236
- if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight)
237
- newH = this.maxRenderHeight;
238
- width *= newH / height;
239
- height = newH;
240
- }
241
- return { width, height };
242
- }
243
- _timeupdate({ type }) {
244
- const eventmap = {
245
- seeking: true,
246
- waiting: true,
247
- playing: false
248
- };
249
- const playing = eventmap[type];
250
- if (playing != null)
251
- this._playstate = playing;
252
- this.setCurrentTime(this._video.paused || this._playstate, this._video.currentTime + this.timeOffset);
253
- }
254
- setVideo(video) {
255
- if (video instanceof HTMLVideoElement) {
256
- this._removeListeners();
257
- this._video = video;
258
- if (this._onDemandRender !== true) {
259
- this._playstate = video.paused;
260
- video.addEventListener("timeupdate", this._timeupdate.bind(this), false);
261
- video.addEventListener("progress", this._timeupdate.bind(this), false);
262
- video.addEventListener("waiting", this._timeupdate.bind(this), false);
263
- video.addEventListener("seeking", this._timeupdate.bind(this), false);
264
- video.addEventListener("playing", this._timeupdate.bind(this), false);
265
- video.addEventListener("ratechange", this.setRate.bind(this), false);
266
- }
267
- if (video.videoWidth > 0)
268
- this.resize();
269
- video.addEventListener("resize", this.resize.bind(this));
270
- if (typeof ResizeObserver !== "undefined") {
271
- if (!this._ro)
272
- this._ro = new ResizeObserver(() => this.resize());
273
- this._ro.observe(video);
274
- }
275
- } else {
276
- this._error("Video element invalid!");
277
- }
278
- }
279
- runBenchmark() {
280
- this.sendMessage("runBenchmark");
281
- }
282
- setTrackByUrl(url) {
283
- this.sendMessage("setTrackByUrl", { url });
284
- }
285
- setTrack(content) {
286
- this.sendMessage("setTrack", { content });
287
- }
288
- freeTrack() {
289
- this.sendMessage("freeTrack");
290
- }
291
- setIsPaused(isPaused) {
292
- this.sendMessage("video", { isPaused });
293
- }
294
- setRate(rate) {
295
- this.sendMessage("video", { rate });
296
- }
297
- setCurrentTime(isPaused, currentTime, rate) {
298
- this.sendMessage("video", { isPaused, currentTime, rate });
299
- }
300
- createEvent(event) {
301
- this.sendMessage("createEvent", { event });
302
- }
303
- setEvent(event, index) {
304
- this.sendMessage("setEvent", { event, index });
305
- }
306
- removeEvent(index) {
307
- this.sendMessage("removeEvent", { index });
308
- }
309
- getEvents(callback) {
310
- this._fetchFromWorker({
311
- target: "getEvents"
312
- }, (err, { events }) => {
313
- callback(err, events);
314
- });
315
- }
316
- createStyle(style) {
317
- this.sendMessage("createStyle", { style });
318
- }
319
- setStyle(event, index) {
320
- this.sendMessage("setStyle", { event, index });
321
- }
322
- removeStyle(index) {
323
- this.sendMessage("removeStyle", { index });
324
- }
325
- getStyles(callback) {
326
- this._fetchFromWorker({
327
- target: "getStyles"
328
- }, (err, { styles }) => {
329
- callback(err, styles);
330
- });
331
- }
332
- addFont(font) {
333
- this.sendMessage("addFont", { font });
334
- }
335
- _sendLocalFont(font) {
336
- try {
337
- queryLocalFonts().then((fontData) => {
338
- const filtered = fontData && fontData.filter((obj) => obj.fullName.toLowerCase() === font);
339
- if (filtered && filtered.length) {
340
- filtered[0].blob().then((blob) => {
341
- blob.arrayBuffer().then((buffer) => {
342
- this.addFont(new Uint8Array(buffer));
343
- });
344
- });
345
- }
346
- });
347
- } catch (e) {
348
- console.warn("Local fonts API:", e);
349
- }
350
- }
351
- _getLocalFont({ font }) {
352
- try {
353
- if ("request" in navigator.permissions) {
354
- navigator.permissions.request({ name: "local-fonts" }).then((permission) => {
355
- if (permission.state === "granted") {
356
- this._sendLocalFont(font);
357
- }
358
- });
359
- } else if ("queryLocalFonts" in self) {
360
- this._sendLocalFont(font);
361
- }
362
- } catch (e) {
363
- console.warn("Local fonts API:", e);
364
- }
365
- }
366
- _unbusy() {
367
- this.busy = false;
368
- }
369
- _demandRender(now, metadata) {
370
- if (this._destroyed)
371
- return null;
372
- if (!this.busy) {
373
- this.busy = true;
374
- this.sendMessage("demand", { time: metadata.mediaTime + this.timeOffset });
375
- }
376
- this._video.requestVideoFrameCallback(this._demandRender.bind(this));
377
- }
378
- _render({ images, async, times }) {
379
- const drawStartTime = Date.now();
380
- this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height);
381
- for (const image of images) {
382
- if (image.image) {
383
- if (async) {
384
- this._ctx.drawImage(image.image, image.x, image.y);
385
- image.image.close();
386
- } else {
387
- this._bufferCanvas.width = image.w;
388
- this._bufferCanvas.height = image.h;
389
- this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(image.image)), image.w, image.h), 0, 0);
390
- this._ctx.drawImage(this._bufferCanvas, image.x, image.y);
391
- }
392
- }
393
- }
394
- if (this.debug) {
395
- times.drawTime = Date.now() - drawStartTime;
396
- let total = 0;
397
- for (const key in times)
398
- total += times[key];
399
- console.log("Bitmaps: " + images.length + " Total: " + Math.round(total) + "ms", times);
400
- }
401
- }
402
- _fixAlpha(uint8) {
403
- if (_JASSUB._hasAlphaBug) {
404
- for (let j = 3; j < uint8.length; j += 4) {
405
- uint8[j] = uint8[j] > 1 ? uint8[j] : 1;
406
- }
407
- }
408
- return uint8;
409
- }
410
- _ready() {
411
- this.dispatchEvent(new CustomEvent("ready"));
412
- }
413
- sendMessage(target, data = {}, transferable) {
414
- if (transferable) {
415
- this._worker.postMessage(__spreadValues({
416
- target,
417
- transferable
418
- }, data), [...transferable]);
419
- } else {
420
- this._worker.postMessage(__spreadValues({
421
- target
422
- }, data));
423
- }
424
- }
425
- _fetchFromWorker(workerOptions, callback) {
426
- try {
427
- const target = workerOptions.target;
428
- const timeout = setTimeout(() => {
429
- reject(new Error("Error: Timeout while try to fetch " + target));
430
- }, 5e3);
431
- const resolve = ({ data }) => {
432
- if (data.target === target) {
433
- callback(null, data);
434
- this._worker.removeEventListener("message", resolve);
435
- this._worker.removeEventListener("error", reject);
436
- clearTimeout(timeout);
437
- }
438
- };
439
- const reject = (event) => {
440
- callback(event);
441
- this._worker.removeEventListener("message", resolve);
442
- this._worker.removeEventListener("error", reject);
443
- clearTimeout(timeout);
444
- };
445
- this._worker.addEventListener("message", resolve);
446
- this._worker.addEventListener("error", reject);
447
- this._worker.postMessage(workerOptions);
448
- } catch (error) {
449
- this._error(error);
450
- }
451
- }
452
- _console({ content, command }) {
453
- console[command].apply(console, JSON.parse(content));
454
- }
455
- _onmessage({ data }) {
456
- if (this["_" + data.target])
457
- this["_" + data.target](data);
458
- }
459
- _error(err) {
460
- if (!(err instanceof ErrorEvent))
461
- this.dispatchEvent(new ErrorEvent("error", { message: err instanceof Error ? err.cause : err }));
462
- throw err instanceof Error ? err : new Error(err instanceof ErrorEvent ? err.message : "error", { cause: err });
463
- }
464
- _removeListeners() {
465
- if (this._video) {
466
- if (this._ro)
467
- this._ro.unobserve(this._video);
468
- this._video.removeEventListener("timeupdate", this._timeupdate);
469
- this._video.removeEventListener("progress", this._timeupdate);
470
- this._video.removeEventListener("waiting", this._timeupdate);
471
- this._video.removeEventListener("seeking", this._timeupdate);
472
- this._video.removeEventListener("playing", this._timeupdate);
473
- this._video.removeEventListener("ratechange", this.setRate);
474
- this._video.removeEventListener("resize", this.resize);
475
- }
476
- }
477
- destroy(err) {
478
- if (err)
479
- this._error(err);
480
- if (this._video)
481
- this._video.parentNode.removeChild(this._canvasParent);
482
- this._destroyed = true;
483
- this._removeListeners();
484
- this.sendMessage("destroy");
485
- this._worker.terminate();
486
- }
487
- };
488
- let JASSUB = _JASSUB;
489
- __publicField(JASSUB, "_supportsWebAssembly", null);
490
- __publicField(JASSUB, "_hasAlphaBug", null);
491
- export { JASSUB as default };
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ var __publicField = (obj, key, value) => {
18
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
+ return value;
20
+ };
21
+ if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype) && "getVideoPlaybackQuality" in HTMLVideoElement.prototype) {
22
+ HTMLVideoElement.prototype._rvfcpolyfillmap = {};
23
+ HTMLVideoElement.prototype.requestVideoFrameCallback = function(callback) {
24
+ const quality = this.getVideoPlaybackQuality();
25
+ const baseline = this.mozPresentedFrames || quality.totalVideoFrames - quality.droppedVideoFrames;
26
+ const check = (old, now2) => {
27
+ const newquality = this.getVideoPlaybackQuality();
28
+ const presentedFrames = this.mozPresentedFrames || newquality.totalVideoFrames - newquality.droppedVideoFrames;
29
+ if (presentedFrames > baseline) {
30
+ const processingDuration = this.mozFrameDelay || newquality.totalFrameDelay - quality.totalFrameDelay || 0;
31
+ const timediff = now2 - old;
32
+ callback(now2, {
33
+ presentationTime: now2 + processingDuration * 1e3,
34
+ expectedDisplayTime: now2 + timediff,
35
+ width: this.videoWidth,
36
+ height: this.videoHeight,
37
+ mediaTime: Math.max(0, this.currentTime || 0) + timediff / 1e3,
38
+ presentedFrames,
39
+ processingDuration
40
+ });
41
+ delete this._rvfcpolyfillmap[handle];
42
+ } else {
43
+ this._rvfcpolyfillmap[handle] = requestAnimationFrame((newer) => check(now2, newer));
44
+ }
45
+ };
46
+ const handle = Date.now();
47
+ const now = performance.now();
48
+ this._rvfcpolyfillmap[handle] = requestAnimationFrame((newer) => check(now, newer));
49
+ return handle;
50
+ };
51
+ HTMLVideoElement.prototype.cancelVideoFrameCallback = function(handle) {
52
+ cancelAnimationFrame(this._rvfcpolyfillmap[handle]);
53
+ delete this._rvfcpolyfillmap[handle];
54
+ };
55
+ }
56
+ const _JASSUB = class extends EventTarget {
57
+ constructor(options = {}) {
58
+ var _a, _b, _c;
59
+ super();
60
+ if (!globalThis.Worker) {
61
+ this.destroy("Worker not supported");
62
+ }
63
+ _JASSUB._test();
64
+ const _blendMode = options.blendMode || "js";
65
+ const _asyncRender = typeof createImageBitmap !== "undefined" && ((_a = options.asyncRender) != null ? _a : true);
66
+ const _offscreenRender = typeof OffscreenCanvas !== "undefined" && ((_b = options.offscreenRender) != null ? _b : true);
67
+ this._onDemandRender = "requestVideoFrameCallback" in HTMLVideoElement.prototype && ((_c = options.onDemandRender) != null ? _c : true);
68
+ this.timeOffset = options.timeOffset || 0;
69
+ this._video = options.video;
70
+ this._canvasParent = null;
71
+ if (this._video) {
72
+ this._canvasParent = document.createElement("div");
73
+ this._canvasParent.className = "JASSUB";
74
+ this._canvasParent.style.position = "relative";
75
+ if (this._video.nextSibling) {
76
+ this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling);
77
+ } else {
78
+ this._video.parentNode.appendChild(this._canvasParent);
79
+ }
80
+ } else if (!this._canvas) {
81
+ this.destroy("Don't know where to render: you should give video or canvas in options.");
82
+ }
83
+ this._canvas = options.canvas || document.createElement("canvas");
84
+ this._canvas.style.display = "block";
85
+ this._canvas.style.position = "absolute";
86
+ this._canvas.style.pointerEvents = "none";
87
+ this._canvasParent.appendChild(this._canvas);
88
+ this._bufferCanvas = document.createElement("canvas");
89
+ this._bufferCtx = this._bufferCanvas.getContext("2d");
90
+ this._canvasctrl = _offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas;
91
+ this._ctx = !_offscreenRender && this._canvasctrl.getContext("2d");
92
+ this._lastRenderTime = 0;
93
+ this.debug = !!options.debug;
94
+ this.prescaleFactor = options.prescaleFactor || 1;
95
+ this.prescaleHeightLimit = options.prescaleHeightLimit || 1080;
96
+ this.maxRenderHeight = options.maxRenderHeight || 0;
97
+ this._worker = new Worker(_JASSUB._supportsWebAssembly ? options.workerUrl || "jassub-worker.js" : options.legacyWorkerUrl || "jassub-worker-legacy.js");
98
+ this._worker.onmessage = (e) => this._onmessage(e);
99
+ this._worker.onerror = (e) => this._error(e);
100
+ this._worker.postMessage({
101
+ target: "init",
102
+ asyncRender: _asyncRender,
103
+ width: this._canvas.width,
104
+ height: this._canvas.height,
105
+ preMain: true,
106
+ blendMode: _blendMode,
107
+ subUrl: options.subUrl,
108
+ subContent: options.subContent || null,
109
+ fonts: options.fonts || [],
110
+ availableFonts: options.availableFonts || { "liberation sans": "./default.woff2" },
111
+ fallbackFont: options.fallbackFont || "liberation sans",
112
+ debug: this.debug,
113
+ targetFps: options.targetFps || 24,
114
+ dropAllAnimations: options.dropAllAnimations,
115
+ libassMemoryLimit: options.libassMemoryLimit || 0,
116
+ libassGlyphLimit: options.libassGlyphLimit || 0,
117
+ hasAlphaBug: _JASSUB._hasAlphaBug,
118
+ useLocalFonts: "queryLocalFonts" in self && !!options.useLocalFonts
119
+ });
120
+ if (_offscreenRender === true)
121
+ this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
122
+ this.setVideo(options.video);
123
+ if (this._onDemandRender) {
124
+ this.busy = false;
125
+ this._video.requestVideoFrameCallback(this._demandRender.bind(this));
126
+ }
127
+ }
128
+ static _test() {
129
+ if (_JASSUB._supportsWebAssembly !== null)
130
+ return null;
131
+ const canvas1 = document.createElement("canvas");
132
+ const ctx1 = canvas1.getContext("2d");
133
+ if (typeof ImageData.prototype.constructor === "function") {
134
+ try {
135
+ new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1);
136
+ } catch (e) {
137
+ console.log("detected that ImageData is not constructable despite browser saying so");
138
+ window.ImageData = function(data, width, height) {
139
+ const imageData = ctx1.createImageData(width, height);
140
+ if (data)
141
+ imageData.data.set(data);
142
+ return imageData;
143
+ };
144
+ }
145
+ }
146
+ try {
147
+ if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
148
+ const module = new WebAssembly.Module(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0));
149
+ if (module instanceof WebAssembly.Module) {
150
+ _JASSUB._supportsWebAssembly = new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
151
+ }
152
+ }
153
+ } catch (e) {
154
+ _JASSUB._supportsWebAssembly = false;
155
+ }
156
+ const canvas2 = document.createElement("canvas");
157
+ const ctx2 = canvas2.getContext("2d");
158
+ canvas1.width = canvas2.width = 1;
159
+ canvas1.height = canvas2.height = 1;
160
+ ctx1.clearRect(0, 0, 1, 1);
161
+ ctx2.clearRect(0, 0, 1, 1);
162
+ const prePut = ctx2.getImageData(0, 0, 1, 1).data;
163
+ ctx1.putImageData(new ImageData(new Uint8ClampedArray([0, 255, 0, 0]), 1, 1), 0, 0);
164
+ ctx2.drawImage(canvas1, 0, 0);
165
+ const postPut = ctx2.getImageData(0, 0, 1, 1).data;
166
+ _JASSUB._hasAlphaBug = prePut[1] !== postPut[1];
167
+ if (_JASSUB._hasAlphaBug)
168
+ console.log("Detected a browser having issue with transparent pixels, applying workaround");
169
+ canvas2.remove();
170
+ }
171
+ resize(width = 0, height = 0, top = 0, left = 0) {
172
+ let videoSize = null;
173
+ if ((!width || !height) && this._video) {
174
+ videoSize = this._getVideoPosition();
175
+ const newsize = this._computeCanvasSize((videoSize.width || 0) * (window.devicePixelRatio || 1), (videoSize.height || 0) * (window.devicePixelRatio || 1));
176
+ width = newsize.width;
177
+ height = newsize.height;
178
+ top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
179
+ left = videoSize.x;
180
+ }
181
+ if (videoSize != null) {
182
+ this._canvas.style.top = top + "px";
183
+ this._canvas.style.left = left + "px";
184
+ this._canvas.style.width = videoSize.width + "px";
185
+ this._canvas.style.height = videoSize.height + "px";
186
+ }
187
+ if (!(this._canvasctrl.width === width && this._canvasctrl.height === height)) {
188
+ if (this._resizeTimeoutBuffer) {
189
+ clearTimeout(this._resizeTimeoutBuffer);
190
+ this._resizeTimeoutBuffer = setTimeout(() => {
191
+ this._resizeTimeoutBuffer = void 0;
192
+ this._canvasctrl.width = width;
193
+ this._canvasctrl.height = height;
194
+ this.sendMessage("canvas", { width, height });
195
+ }, 100);
196
+ } else {
197
+ this._canvasctrl.width = width;
198
+ this._canvasctrl.height = height;
199
+ this.sendMessage("canvas", { width, height });
200
+ this._resizeTimeoutBuffer = setTimeout(() => {
201
+ this._resizeTimeoutBuffer = void 0;
202
+ }, 100);
203
+ }
204
+ }
205
+ }
206
+ _getVideoPosition() {
207
+ const videoRatio = this._video.videoWidth / this._video.videoHeight;
208
+ const { offsetWidth, offsetHeight } = this._video;
209
+ const elementRatio = offsetWidth / offsetHeight;
210
+ let width = offsetWidth;
211
+ let height = offsetHeight;
212
+ if (elementRatio > videoRatio) {
213
+ width = Math.floor(offsetHeight * videoRatio);
214
+ } else {
215
+ height = Math.floor(offsetWidth / videoRatio);
216
+ }
217
+ const x = (offsetWidth - width) / 2;
218
+ const y = (offsetHeight - height) / 2;
219
+ return { width, height, x, y };
220
+ }
221
+ _computeCanvasSize(width = 0, height = 0) {
222
+ const scalefactor = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor;
223
+ if (height <= 0 || width <= 0) {
224
+ width = 0;
225
+ height = 0;
226
+ } else {
227
+ const sgn = scalefactor < 1 ? -1 : 1;
228
+ let newH = height;
229
+ if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
230
+ newH *= scalefactor;
231
+ } else if (sgn * newH < sgn * this.prescaleHeightLimit) {
232
+ newH = this.prescaleHeightLimit;
233
+ }
234
+ if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight)
235
+ newH = this.maxRenderHeight;
236
+ width *= newH / height;
237
+ height = newH;
238
+ }
239
+ return { width, height };
240
+ }
241
+ _timeupdate({ type }) {
242
+ const eventmap = {
243
+ seeking: true,
244
+ waiting: true,
245
+ playing: false
246
+ };
247
+ const playing = eventmap[type];
248
+ if (playing != null)
249
+ this._playstate = playing;
250
+ this.setCurrentTime(this._video.paused || this._playstate, this._video.currentTime + this.timeOffset);
251
+ }
252
+ setVideo(video) {
253
+ if (video instanceof HTMLVideoElement) {
254
+ this._removeListeners();
255
+ this._video = video;
256
+ if (this._onDemandRender !== true) {
257
+ this._playstate = video.paused;
258
+ video.addEventListener("timeupdate", this._timeupdate.bind(this), false);
259
+ video.addEventListener("progress", this._timeupdate.bind(this), false);
260
+ video.addEventListener("waiting", this._timeupdate.bind(this), false);
261
+ video.addEventListener("seeking", this._timeupdate.bind(this), false);
262
+ video.addEventListener("playing", this._timeupdate.bind(this), false);
263
+ video.addEventListener("ratechange", this.setRate.bind(this), false);
264
+ }
265
+ if (video.videoWidth > 0)
266
+ this.resize();
267
+ video.addEventListener("resize", this.resize.bind(this));
268
+ if (typeof ResizeObserver !== "undefined") {
269
+ if (!this._ro)
270
+ this._ro = new ResizeObserver(() => this.resize());
271
+ this._ro.observe(video);
272
+ }
273
+ } else {
274
+ this._error("Video element invalid!");
275
+ }
276
+ }
277
+ runBenchmark() {
278
+ this.sendMessage("runBenchmark");
279
+ }
280
+ setTrackByUrl(url) {
281
+ this.sendMessage("setTrackByUrl", { url });
282
+ }
283
+ setTrack(content) {
284
+ this.sendMessage("setTrack", { content });
285
+ }
286
+ freeTrack() {
287
+ this.sendMessage("freeTrack");
288
+ }
289
+ setIsPaused(isPaused) {
290
+ this.sendMessage("video", { isPaused });
291
+ }
292
+ setRate(rate) {
293
+ this.sendMessage("video", { rate });
294
+ }
295
+ setCurrentTime(isPaused, currentTime, rate) {
296
+ this.sendMessage("video", { isPaused, currentTime, rate });
297
+ }
298
+ createEvent(event) {
299
+ this.sendMessage("createEvent", { event });
300
+ }
301
+ setEvent(event, index) {
302
+ this.sendMessage("setEvent", { event, index });
303
+ }
304
+ removeEvent(index) {
305
+ this.sendMessage("removeEvent", { index });
306
+ }
307
+ getEvents(callback) {
308
+ this._fetchFromWorker({
309
+ target: "getEvents"
310
+ }, (err, { events }) => {
311
+ callback(err, events);
312
+ });
313
+ }
314
+ createStyle(style) {
315
+ this.sendMessage("createStyle", { style });
316
+ }
317
+ setStyle(event, index) {
318
+ this.sendMessage("setStyle", { event, index });
319
+ }
320
+ removeStyle(index) {
321
+ this.sendMessage("removeStyle", { index });
322
+ }
323
+ getStyles(callback) {
324
+ this._fetchFromWorker({
325
+ target: "getStyles"
326
+ }, (err, { styles }) => {
327
+ callback(err, styles);
328
+ });
329
+ }
330
+ addFont(font) {
331
+ this.sendMessage("addFont", { font });
332
+ }
333
+ _sendLocalFont(font) {
334
+ try {
335
+ queryLocalFonts().then((fontData) => {
336
+ const filtered = fontData && fontData.filter((obj) => obj.fullName.toLowerCase() === font);
337
+ if (filtered && filtered.length) {
338
+ filtered[0].blob().then((blob) => {
339
+ blob.arrayBuffer().then((buffer) => {
340
+ this.addFont(new Uint8Array(buffer));
341
+ });
342
+ });
343
+ }
344
+ });
345
+ } catch (e) {
346
+ console.warn("Local fonts API:", e);
347
+ }
348
+ }
349
+ _getLocalFont({ font }) {
350
+ var _a, _b;
351
+ try {
352
+ const query = ((_a = navigator == null ? void 0 : navigator.permissions) == null ? void 0 : _a.request) || ((_b = navigator == null ? void 0 : navigator.permissions) == null ? void 0 : _b.query);
353
+ if (query) {
354
+ query({ name: "local-fonts" }).then((permission) => {
355
+ if (permission.state === "granted") {
356
+ this._sendLocalFont(font);
357
+ }
358
+ });
359
+ } else if ("queryLocalFonts" in self) {
360
+ this._sendLocalFont(font);
361
+ }
362
+ } catch (e) {
363
+ console.warn("Local fonts API:", e);
364
+ }
365
+ }
366
+ _unbusy() {
367
+ this.busy = false;
368
+ }
369
+ _demandRender(now, metadata) {
370
+ if (this._destroyed)
371
+ return null;
372
+ if (!this.busy) {
373
+ this.busy = true;
374
+ this.sendMessage("demand", { time: metadata.mediaTime + this.timeOffset });
375
+ }
376
+ this._video.requestVideoFrameCallback(this._demandRender.bind(this));
377
+ }
378
+ _render({ images, async, times }) {
379
+ const drawStartTime = Date.now();
380
+ this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height);
381
+ for (const image of images) {
382
+ if (image.image) {
383
+ if (async) {
384
+ this._ctx.drawImage(image.image, image.x, image.y);
385
+ image.image.close();
386
+ } else {
387
+ this._bufferCanvas.width = image.w;
388
+ this._bufferCanvas.height = image.h;
389
+ this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(image.image)), image.w, image.h), 0, 0);
390
+ this._ctx.drawImage(this._bufferCanvas, image.x, image.y);
391
+ }
392
+ }
393
+ }
394
+ if (this.debug) {
395
+ times.drawTime = Date.now() - drawStartTime;
396
+ let total = 0;
397
+ for (const key in times)
398
+ total += times[key];
399
+ console.log("Bitmaps: " + images.length + " Total: " + Math.round(total) + "ms", times);
400
+ }
401
+ }
402
+ _fixAlpha(uint8) {
403
+ if (_JASSUB._hasAlphaBug) {
404
+ for (let j = 3; j < uint8.length; j += 4) {
405
+ uint8[j] = uint8[j] > 1 ? uint8[j] : 1;
406
+ }
407
+ }
408
+ return uint8;
409
+ }
410
+ _ready() {
411
+ this.dispatchEvent(new CustomEvent("ready"));
412
+ }
413
+ sendMessage(target, data = {}, transferable) {
414
+ if (transferable) {
415
+ this._worker.postMessage(__spreadValues({
416
+ target,
417
+ transferable
418
+ }, data), [...transferable]);
419
+ } else {
420
+ this._worker.postMessage(__spreadValues({
421
+ target
422
+ }, data));
423
+ }
424
+ }
425
+ _fetchFromWorker(workerOptions, callback) {
426
+ try {
427
+ const target = workerOptions.target;
428
+ const timeout = setTimeout(() => {
429
+ reject(new Error("Error: Timeout while try to fetch " + target));
430
+ }, 5e3);
431
+ const resolve = ({ data }) => {
432
+ if (data.target === target) {
433
+ callback(null, data);
434
+ this._worker.removeEventListener("message", resolve);
435
+ this._worker.removeEventListener("error", reject);
436
+ clearTimeout(timeout);
437
+ }
438
+ };
439
+ const reject = (event) => {
440
+ callback(event);
441
+ this._worker.removeEventListener("message", resolve);
442
+ this._worker.removeEventListener("error", reject);
443
+ clearTimeout(timeout);
444
+ };
445
+ this._worker.addEventListener("message", resolve);
446
+ this._worker.addEventListener("error", reject);
447
+ this._worker.postMessage(workerOptions);
448
+ } catch (error) {
449
+ this._error(error);
450
+ }
451
+ }
452
+ _console({ content, command }) {
453
+ console[command].apply(console, JSON.parse(content));
454
+ }
455
+ _onmessage({ data }) {
456
+ if (this["_" + data.target])
457
+ this["_" + data.target](data);
458
+ }
459
+ _error(err) {
460
+ if (!(err instanceof ErrorEvent))
461
+ this.dispatchEvent(new ErrorEvent("error", { message: err instanceof Error ? err.cause : err }));
462
+ throw err instanceof Error ? err : new Error(err instanceof ErrorEvent ? err.message : "error", { cause: err });
463
+ }
464
+ _removeListeners() {
465
+ if (this._video) {
466
+ if (this._ro)
467
+ this._ro.unobserve(this._video);
468
+ this._video.removeEventListener("timeupdate", this._timeupdate);
469
+ this._video.removeEventListener("progress", this._timeupdate);
470
+ this._video.removeEventListener("waiting", this._timeupdate);
471
+ this._video.removeEventListener("seeking", this._timeupdate);
472
+ this._video.removeEventListener("playing", this._timeupdate);
473
+ this._video.removeEventListener("ratechange", this.setRate);
474
+ this._video.removeEventListener("resize", this.resize);
475
+ }
476
+ }
477
+ destroy(err) {
478
+ if (err)
479
+ this._error(err);
480
+ if (this._video)
481
+ this._video.parentNode.removeChild(this._canvasParent);
482
+ this._destroyed = true;
483
+ this._removeListeners();
484
+ this.sendMessage("destroy");
485
+ this._worker.terminate();
486
+ }
487
+ };
488
+ let JASSUB = _JASSUB;
489
+ __publicField(JASSUB, "_supportsWebAssembly", null);
490
+ __publicField(JASSUB, "_hasAlphaBug", null);
491
+ export { JASSUB as default };