jassub 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1,457 @@
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 || [],
111
+ fallbackFont: options.fallbackFont || "./default.woff2",
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
+ });
119
+ if (_offscreenRender === true)
120
+ this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
121
+ this.setVideo(options.video);
122
+ if (this._onDemandRender) {
123
+ this.busy = false;
124
+ this._video.requestVideoFrameCallback(this._demandRender.bind(this));
125
+ }
126
+ }
127
+ static _test() {
128
+ if (_JASSUB._supportsWebAssembly !== null)
129
+ return null;
130
+ const canvas1 = document.createElement("canvas");
131
+ const ctx1 = canvas1.getContext("2d");
132
+ if (typeof ImageData.prototype.constructor === "function") {
133
+ try {
134
+ new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1);
135
+ } catch (e) {
136
+ console.log("detected that ImageData is not constructable despite browser saying so");
137
+ window.ImageData = function(data, width, height) {
138
+ const imageData = ctx1.createImageData(width, height);
139
+ if (data)
140
+ imageData.data.set(data);
141
+ return imageData;
142
+ };
143
+ }
144
+ }
145
+ try {
146
+ if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
147
+ const module = new WebAssembly.Module(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0));
148
+ if (module instanceof WebAssembly.Module) {
149
+ _JASSUB._supportsWebAssembly = new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
150
+ }
151
+ }
152
+ } catch (e) {
153
+ _JASSUB._supportsWebAssembly = false;
154
+ }
155
+ const canvas2 = document.createElement("canvas");
156
+ const ctx2 = canvas2.getContext("2d");
157
+ canvas1.width = canvas2.width = 1;
158
+ canvas1.height = canvas2.height = 1;
159
+ ctx1.clearRect(0, 0, 1, 1);
160
+ ctx2.clearRect(0, 0, 1, 1);
161
+ const prePut = ctx2.getImageData(0, 0, 1, 1).data;
162
+ ctx1.putImageData(new ImageData(new Uint8ClampedArray([0, 255, 0, 0]), 1, 1), 0, 0);
163
+ ctx2.drawImage(canvas1, 0, 0);
164
+ const postPut = ctx2.getImageData(0, 0, 1, 1).data;
165
+ _JASSUB._hasAlphaBug = prePut[1] !== postPut[1];
166
+ if (_JASSUB._hasAlphaBug)
167
+ console.log("Detected a browser having issue with transparent pixels, applying workaround");
168
+ canvas2.remove();
169
+ }
170
+ resize(width = 0, height = 0, top = 0, left = 0) {
171
+ let videoSize = null;
172
+ if ((!width || !height) && this._video) {
173
+ videoSize = this._getVideoPosition();
174
+ const newsize = this._computeCanvasSize(videoSize.width || 0 * (window.devicePixelRatio || 1), videoSize.height || 0 * (window.devicePixelRatio || 1));
175
+ width = newsize.width;
176
+ height = newsize.height;
177
+ top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
178
+ left = videoSize.x;
179
+ }
180
+ if (this._canvas.style.top !== top + "px" || this._canvas.style.left !== left + "px") {
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
+ }
207
+ _getVideoPosition() {
208
+ const videoRatio = this._video.videoWidth / this._video.videoHeight;
209
+ const { offsetWidth, offsetHeight } = this._video;
210
+ const elementRatio = offsetWidth / offsetHeight;
211
+ let width = offsetWidth;
212
+ let height = offsetHeight;
213
+ if (elementRatio > videoRatio) {
214
+ width = Math.floor(offsetHeight * videoRatio);
215
+ } else {
216
+ height = Math.floor(offsetWidth / videoRatio);
217
+ }
218
+ const x = (offsetWidth - width) / 2;
219
+ const y = (offsetHeight - height) / 2;
220
+ return { width, height, x, y };
221
+ }
222
+ _computeCanvasSize(width = 0, height = 0) {
223
+ const scalefactor = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor;
224
+ if (height <= 0 || width <= 0) {
225
+ width = 0;
226
+ height = 0;
227
+ } else {
228
+ const sgn = scalefactor < 1 ? -1 : 1;
229
+ let newH = height;
230
+ if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
231
+ newH *= scalefactor;
232
+ } else if (sgn * newH < sgn * this.prescaleHeightLimit) {
233
+ newH = this.prescaleHeightLimit;
234
+ }
235
+ if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight)
236
+ newH = this.maxRenderHeight;
237
+ width *= newH / height;
238
+ height = newH;
239
+ }
240
+ return { width, height };
241
+ }
242
+ _timeupdate({ type }) {
243
+ const eventmap = {
244
+ seeking: true,
245
+ waiting: true,
246
+ playing: false
247
+ };
248
+ const playing = eventmap[type];
249
+ if (playing != null)
250
+ this._playstate = playing;
251
+ this.setCurrentTime(this._video.paused || this._playstate, this._video.currentTime + this.timeOffset);
252
+ }
253
+ setVideo(video) {
254
+ if (video instanceof HTMLVideoElement) {
255
+ this._removeListeners();
256
+ this._video = video;
257
+ if (this._onDemandRender !== true) {
258
+ this._playstate = video.paused;
259
+ video.addEventListener("timeupdate", (e) => this._timeupdate(e), false);
260
+ video.addEventListener("progress", (e) => this._timeupdate(e), false);
261
+ video.addEventListener("waiting", (e) => this._timeupdate(e), false);
262
+ video.addEventListener("seeking", (e) => this._timeupdate(e), false);
263
+ video.addEventListener("playing", (e) => this._timeupdate(e), false);
264
+ video.addEventListener("ratechange", (e) => this.setRate(e), false);
265
+ }
266
+ if (video.videoWidth > 0) {
267
+ this.resize();
268
+ } else {
269
+ video.addEventListener("loadedmetadata", () => this.resize(0, 0, 0, 0), false);
270
+ }
271
+ if (typeof ResizeObserver !== "undefined") {
272
+ if (!this._ro)
273
+ this._ro = new ResizeObserver(() => this.resize(0, 0, 0, 0));
274
+ this._ro.observe(video);
275
+ }
276
+ } else {
277
+ this._error("Video element invalid!");
278
+ }
279
+ }
280
+ runBenchmark() {
281
+ this.sendMessage("runBenchmark");
282
+ }
283
+ setTrackByUrl(url) {
284
+ this.sendMessage("setTrackByUrl", { url });
285
+ }
286
+ setTrack(content) {
287
+ this.sendMessage("setTrack", { content });
288
+ }
289
+ freeTrack() {
290
+ this.sendMessage("freeTrack");
291
+ }
292
+ setIsPaused(isPaused) {
293
+ this.sendMessage("video", { isPaused });
294
+ }
295
+ setRate(rate) {
296
+ this.sendMessage("video", { rate });
297
+ }
298
+ setCurrentTime(isPaused, currentTime, rate) {
299
+ this.sendMessage("video", { isPaused, currentTime, rate });
300
+ }
301
+ createEvent(event) {
302
+ this.sendMessage("createEvent", { event });
303
+ }
304
+ setEvent(event, index) {
305
+ this.sendMessage("setEvent", { event, index });
306
+ }
307
+ removeEvent(index) {
308
+ this.sendMessage("removeEvent", { index });
309
+ }
310
+ getEvents(callback) {
311
+ this._fetchFromWorker({
312
+ target: "getEvents"
313
+ }, (err, { events }) => {
314
+ callback(err, events);
315
+ });
316
+ }
317
+ createStyle(style) {
318
+ this.sendMessage("createStyle", { style });
319
+ }
320
+ setStyle(event, index) {
321
+ this.sendMessage("setStyle", { event, index });
322
+ }
323
+ removeStyle(index) {
324
+ this.sendMessage("removeStyle", { index });
325
+ }
326
+ getStyles(callback) {
327
+ this._fetchFromWorker({
328
+ target: "getStyles"
329
+ }, (err, { styles }) => {
330
+ callback(err, styles);
331
+ });
332
+ }
333
+ _unbusy() {
334
+ this.busy = false;
335
+ }
336
+ _demandRender(now, metadata) {
337
+ if (this._destroyed)
338
+ return null;
339
+ if (!this.busy) {
340
+ this.busy = true;
341
+ this.sendMessage("demand", { time: metadata.mediaTime + this.timeOffset });
342
+ }
343
+ this._video.requestVideoFrameCallback(this._demandRender.bind(this));
344
+ }
345
+ _render({ images, async, times }) {
346
+ const drawStartTime = Date.now();
347
+ this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height);
348
+ for (const image of images) {
349
+ if (image.image) {
350
+ if (async) {
351
+ this._ctx.drawImage(image.image, image.x, image.y);
352
+ image.image.close();
353
+ } else {
354
+ this._bufferCanvas.width = image.w;
355
+ this._bufferCanvas.height = image.h;
356
+ this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(image.image)), image.w, image.h), 0, 0);
357
+ this._ctx.drawImage(this._bufferCanvas, image.x, image.y);
358
+ }
359
+ }
360
+ }
361
+ if (this.debug) {
362
+ times.drawTime = Date.now() - drawStartTime;
363
+ let total = 0;
364
+ for (const key in times)
365
+ total += times[key];
366
+ console.log("Bitmaps: " + images.length + " Total: " + Math.round(total) + "ms", times);
367
+ }
368
+ }
369
+ _fixAlpha(uint8) {
370
+ if (_JASSUB._hasAlphaBug) {
371
+ for (let j = 3; j < uint8.length; j += 4) {
372
+ uint8[j] = uint8[j] > 1 ? uint8[j] : 1;
373
+ }
374
+ }
375
+ return uint8;
376
+ }
377
+ _ready() {
378
+ this.dispatchEvent(new CustomEvent("ready"));
379
+ }
380
+ sendMessage(target, data = {}, transferable) {
381
+ if (transferable) {
382
+ this._worker.postMessage(__spreadValues({
383
+ target,
384
+ transferable
385
+ }, data), [...transferable]);
386
+ } else {
387
+ this._worker.postMessage(__spreadValues({
388
+ target
389
+ }, data));
390
+ }
391
+ }
392
+ _fetchFromWorker(workerOptions, callback) {
393
+ try {
394
+ const target = workerOptions.target;
395
+ const timeout = setTimeout(() => {
396
+ reject(new Error("Error: Timeout while try to fetch " + target));
397
+ }, 5e3);
398
+ const resolve = ({ data }) => {
399
+ if (data.target === target) {
400
+ callback(null, data);
401
+ this._worker.removeEventListener("message", resolve);
402
+ this._worker.removeEventListener("error", reject);
403
+ clearTimeout(timeout);
404
+ }
405
+ };
406
+ const reject = (event) => {
407
+ callback(event);
408
+ this._worker.removeEventListener("message", resolve);
409
+ this._worker.removeEventListener("error", reject);
410
+ clearTimeout(timeout);
411
+ };
412
+ this._worker.addEventListener("message", resolve);
413
+ this._worker.addEventListener("error", reject);
414
+ this._worker.postMessage(workerOptions);
415
+ } catch (error) {
416
+ this._error(error);
417
+ }
418
+ }
419
+ _console({ content, command }) {
420
+ console[command].apply(console, JSON.parse(content));
421
+ }
422
+ _onmessage({ data }) {
423
+ if (this["_" + data.target])
424
+ this["_" + data.target](data);
425
+ }
426
+ _error(err) {
427
+ if (!(err instanceof ErrorEvent))
428
+ this.dispatchEvent(new ErrorEvent("error", { message: err instanceof Error ? err.cause : err }));
429
+ throw err instanceof Error ? err : new Error(err instanceof ErrorEvent ? err.message : "error", { cause: err });
430
+ }
431
+ _removeListeners() {
432
+ if (this._video) {
433
+ if (this._ro)
434
+ this._ro.unobserve(this._video);
435
+ this._video.removeEventListener("timeupdate", this._timeupdate);
436
+ this._video.removeEventListener("progress", this._timeupdate);
437
+ this._video.removeEventListener("waiting", this._timeupdate);
438
+ this._video.removeEventListener("seeking", this._timeupdate);
439
+ this._video.removeEventListener("playing", this._timeupdate);
440
+ this._video.removeEventListener("ratechange", this.setRate);
441
+ }
442
+ }
443
+ destroy(err) {
444
+ if (err)
445
+ this._error(err);
446
+ if (this._video)
447
+ this._video.parentNode.removeChild(this._canvasParent);
448
+ this._destroyed = true;
449
+ this._removeListeners();
450
+ this.sendMessage("destroy");
451
+ this._worker.terminate();
452
+ }
453
+ };
454
+ let JASSUB = _JASSUB;
455
+ __publicField(JASSUB, "_supportsWebAssembly", null);
456
+ __publicField(JASSUB, "_hasAlphaBug", null);
457
+ export { JASSUB as default };
@@ -0,0 +1 @@
1
+ (function(h,a){typeof exports=="object"&&typeof module!="undefined"?module.exports=a():typeof define=="function"&&define.amd?define(a):(h=typeof globalThis!="undefined"?globalThis:h||self,h.JASSUB=a())})(this,function(){"use strict";var p=Object.defineProperty;var g=Object.getOwnPropertySymbols;var y=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable;var u=(h,a,l)=>a in h?p(h,a,{enumerable:!0,configurable:!0,writable:!0,value:l}):h[a]=l,v=(h,a)=>{for(var l in a||(a={}))y.call(a,l)&&u(h,l,a[l]);if(g)for(var l of g(a))b.call(a,l)&&u(h,l,a[l]);return h};var _=(h,a,l)=>(u(h,typeof a!="symbol"?a+"":a,l),l);!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(l){const e=this.getVideoPlaybackQuality(),t=this.mozPresentedFrames||e.totalVideoFrames-e.droppedVideoFrames,i=(r,o)=>{const d=this.getVideoPlaybackQuality(),f=this.mozPresentedFrames||d.totalVideoFrames-d.droppedVideoFrames;if(f>t){const c=this.mozFrameDelay||d.totalFrameDelay-e.totalFrameDelay||0,m=o-r;l(o,{presentationTime:o+c*1e3,expectedDisplayTime:o+m,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+m/1e3,presentedFrames:f,processingDuration:c}),delete this._rvfcpolyfillmap[n]}else this._rvfcpolyfillmap[n]=requestAnimationFrame(c=>i(o,c))},n=Date.now(),s=performance.now();return this._rvfcpolyfillmap[n]=requestAnimationFrame(r=>i(s,r)),n},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(l){cancelAnimationFrame(this._rvfcpolyfillmap[l]),delete this._rvfcpolyfillmap[l]});const a=class extends EventTarget{constructor(e={}){var s,r,o;super(),globalThis.Worker||this.destroy("Worker not supported"),a._test();const t=e.blendMode||"js",i=typeof createImageBitmap!="undefined"&&((s=e.asyncRender)!=null?s:!0),n=typeof OffscreenCanvas!="undefined"&&((r=e.offscreenRender)!=null?r:!0);this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&((o=e.onDemandRender)!=null?o:!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._canvasParent=null,this._video?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._video.nextSibling?this._video.parentNode.insertBefore(this._canvasParent,this._video.nextSibling):this._video.parentNode.appendChild(this._canvasParent)):this._canvas||this.destroy("Don't know where to render: you should give video or canvas in options."),this._canvas=e.canvas||document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),this._canvasctrl=n?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!n&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._worker=new Worker(a._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=d=>this._onmessage(d),this._worker.onerror=d=>this._error(d),this._worker.postMessage({target:"init",asyncRender:i,width:this._canvas.width,height:this._canvas.height,preMain:!0,blendMode:t,subUrl:e.subUrl,subContent:e.subContent||null,fonts:e.fonts||[],availableFonts:e.availableFonts||[],fallbackFont:e.fallbackFont||"./default.woff2",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:a._hasAlphaBug}),n===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._video.requestVideoFrameCallback(this._demandRender.bind(this)))}static _test(){if(a._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d");if(typeof ImageData.prototype.constructor=="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("detected that ImageData is not constructable despite browser saying so"),window.ImageData=function(d,f,c){const m=t.createImageData(f,c);return d&&m.data.set(d),m}}try{if(typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"){const o=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));o instanceof WebAssembly.Module&&(a._supportsWebAssembly=new WebAssembly.Instance(o)instanceof WebAssembly.Instance)}}catch{a._supportsWebAssembly=!1}const i=document.createElement("canvas"),n=i.getContext("2d");e.width=i.width=1,e.height=i.height=1,t.clearRect(0,0,1,1),n.clearRect(0,0,1,1);const s=n.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),n.drawImage(e,0,0);const r=n.getImageData(0,0,1,1).data;a._hasAlphaBug=s[1]!==r[1],a._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),i.remove()}resize(e=0,t=0,i=0,n=0){let s=null;if((!e||!t)&&this._video){s=this._getVideoPosition();const r=this._computeCanvasSize(s.width||0*(window.devicePixelRatio||1),s.height||0*(window.devicePixelRatio||1));e=r.width,t=r.height,i=s.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),n=s.x}(this._canvas.style.top!==i+"px"||this._canvas.style.left!==n+"px")&&(s!=null&&(this._canvas.style.top=i+"px",this._canvas.style.left=n+"px",this._canvas.style.width=s.width+"px",this._canvas.style.height=s.height+"px"),this._canvasctrl.width===e&&this._canvasctrl.height===t||(this._resizeTimeoutBuffer?(clearTimeout(this._resizeTimeoutBuffer),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0,this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t})},100)):(this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t}),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0},100))))}_getVideoPosition(){const e=this._video.videoWidth/this._video.videoHeight,{offsetWidth:t,offsetHeight:i}=this._video,n=t/i;let s=t,r=i;n>e?s=Math.floor(i*e):r=Math.floor(t/e);const o=(t-s)/2,d=(i-r)/2;return{width:s,height:r,x:o,y:d}}_computeCanvasSize(e=0,t=0){const i=this.prescaleFactor<=0?1:this.prescaleFactor;if(t<=0||e<=0)e=0,t=0;else{const n=i<1?-1:1;let s=t;n*s*i<=n*this.prescaleHeightLimit?s*=i:n*s<n*this.prescaleHeightLimit&&(s=this.prescaleHeightLimit),this.maxRenderHeight>0&&s>this.maxRenderHeight&&(s=this.maxRenderHeight),e*=s/t,t=s}return{width:e,height:t}}_timeupdate({type:e}){const i={seeking:!0,waiting:!0,playing:!1}[e];i!=null&&(this._playstate=i),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender!==!0&&(this._playstate=e.paused,e.addEventListener("timeupdate",t=>this._timeupdate(t),!1),e.addEventListener("progress",t=>this._timeupdate(t),!1),e.addEventListener("waiting",t=>this._timeupdate(t),!1),e.addEventListener("seeking",t=>this._timeupdate(t),!1),e.addEventListener("playing",t=>this._timeupdate(t),!1),e.addEventListener("ratechange",t=>this.setRate(t),!1)),e.videoWidth>0?this.resize():e.addEventListener("loadedmetadata",()=>this.resize(0,0,0,0),!1),typeof ResizeObserver!="undefined"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize(0,0,0,0))),this._ro.observe(e))):this._error("Video element invalid!")}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(e){this.sendMessage("setTrackByUrl",{url:e})}setTrack(e){this.sendMessage("setTrack",{content:e})}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,i){this.sendMessage("video",{isPaused:e,currentTime:t,rate:i})}createEvent(e){this.sendMessage("createEvent",{event:e})}setEvent(e,t){this.sendMessage("setEvent",{event:e,index:t})}removeEvent(e){this.sendMessage("removeEvent",{index:e})}getEvents(e){this._fetchFromWorker({target:"getEvents"},(t,{events:i})=>{e(t,i)})}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{event:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:i})=>{e(t,i)})}_unbusy(){this.busy=!1}_demandRender(e,t){if(this._destroyed)return null;this.busy||(this.busy=!0,this.sendMessage("demand",{time:t.mediaTime+this.timeOffset})),this._video.requestVideoFrameCallback(this._demandRender.bind(this))}_render({images:e,async:t,times:i}){const n=Date.now();this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const s of e)s.image&&(t?(this._ctx.drawImage(s.image,s.x,s.y),s.image.close()):(this._bufferCanvas.width=s.w,this._bufferCanvas.height=s.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(s.image)),s.w,s.h),0,0),this._ctx.drawImage(this._bufferCanvas,s.x,s.y)));if(this.debug){i.drawTime=Date.now()-n;let s=0;for(const r in i)s+=i[r];console.log("Bitmaps: "+e.length+" Total: "+Math.round(s)+"ms",i)}}_fixAlpha(e){if(a._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e,t={},i){i?this._worker.postMessage(v({target:e,transferable:i},t),[...i]):this._worker.postMessage(v({target:e},t))}_fetchFromWorker(e,t){try{const i=e.target,n=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+i))},5e3),s=({data:o})=>{o.target===i&&(t(null,o),this._worker.removeEventListener("message",s),this._worker.removeEventListener("error",r),clearTimeout(n))},r=o=>{t(o),this._worker.removeEventListener("message",s),this._worker.removeEventListener("error",r),clearTimeout(n)};this._worker.addEventListener("message",s),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(i){this._error(i)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){throw e instanceof ErrorEvent||this.dispatchEvent(new ErrorEvent("error",{message:e instanceof Error?e.cause:e})),e instanceof Error?e:new Error(e instanceof ErrorEvent?e.message:"error",{cause:e})}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._video.removeEventListener("timeupdate",this._timeupdate),this._video.removeEventListener("progress",this._timeupdate),this._video.removeEventListener("waiting",this._timeupdate),this._video.removeEventListener("seeking",this._timeupdate),this._video.removeEventListener("playing",this._timeupdate),this._video.removeEventListener("ratechange",this.setRate))}destroy(e){e&&this._error(e),this._video&&this._video.parentNode.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker.terminate()}};let h=a;return _(h,"_supportsWebAssembly",null),_(h,"_hasAlphaBug",null),h});
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "jassub",
3
+ "version": "1.0.0",
4
+ "description": "libass Subtitle Renderer and Parser library for browsers",
5
+ "main": "src/jassub.js",
6
+ "files": [
7
+ "dist/*",
8
+ "src/jassub.js"
9
+ ],
10
+ "scripts": {
11
+ "bundle": "vite build"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/ThaUnknown/jassub.git"
16
+ },
17
+ "keywords": [
18
+ "libass",
19
+ "subtitle",
20
+ "wasm",
21
+ "emscripten"
22
+ ],
23
+ "author": "ThaUnknown",
24
+ "contributors": [
25
+ "SubtitlesOctopus Contributors"
26
+ ],
27
+ "license": "LGPL-2.1-or-later AND (FTL OR GPL-2.0-or-later) AND MIT AND MIT-Modern-Variant AND ISC AND NTP AND Zlib AND BSL-1.0",
28
+ "bugs": {
29
+ "url": "https://github.com/ThaUnknown/jassub/issues"
30
+ },
31
+ "homepage": "https://github.com/ThaUnknown/jassub",
32
+ "dependencies": {
33
+ "rvfc-polyfill": "^1.0.3"
34
+ },
35
+ "devDependencies": {
36
+ "vite": "^2.9.12"
37
+ }
38
+ }