jassub 1.0.1 → 1.1.2
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/README.md +5 -6
- package/dist/jassub-worker.js +1 -1
- package/dist/jassub-worker.wasm +0 -0
- package/dist/jassub.es.js +37 -14
- package/dist/jassub.umd.js +1 -1
- package/package.json +1 -1
- package/src/jassub.js +46 -17
- package/dist/jassub-worker.data +0 -35
package/dist/jassub-worker.wasm
CHANGED
|
Binary file
|
package/dist/jassub.es.js
CHANGED
|
@@ -107,14 +107,15 @@ const _JASSUB = class extends EventTarget {
|
|
|
107
107
|
subUrl: options.subUrl,
|
|
108
108
|
subContent: options.subContent || null,
|
|
109
109
|
fonts: options.fonts || [],
|
|
110
|
-
availableFonts: options.availableFonts ||
|
|
111
|
-
fallbackFont: options.fallbackFont || "
|
|
110
|
+
availableFonts: options.availableFonts || { "liberation sans": "./default.woff2" },
|
|
111
|
+
fallbackFont: options.fallbackFont || "liberation sans",
|
|
112
112
|
debug: this.debug,
|
|
113
113
|
targetFps: options.targetFps || 24,
|
|
114
114
|
dropAllAnimations: options.dropAllAnimations,
|
|
115
115
|
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
116
116
|
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
117
|
-
hasAlphaBug: _JASSUB._hasAlphaBug
|
|
117
|
+
hasAlphaBug: _JASSUB._hasAlphaBug,
|
|
118
|
+
useLocalFonts: "queryLocalFonts" in self && !!options.useLocalFonts
|
|
118
119
|
});
|
|
119
120
|
if (_offscreenRender === true)
|
|
120
121
|
this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
|
|
@@ -256,21 +257,19 @@ const _JASSUB = class extends EventTarget {
|
|
|
256
257
|
this._video = video;
|
|
257
258
|
if (this._onDemandRender !== true) {
|
|
258
259
|
this._playstate = video.paused;
|
|
259
|
-
video.addEventListener("timeupdate",
|
|
260
|
-
video.addEventListener("progress",
|
|
261
|
-
video.addEventListener("waiting",
|
|
262
|
-
video.addEventListener("seeking",
|
|
263
|
-
video.addEventListener("playing",
|
|
264
|
-
video.addEventListener("ratechange",
|
|
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);
|
|
265
266
|
}
|
|
266
|
-
if (video.videoWidth > 0)
|
|
267
|
+
if (video.videoWidth > 0)
|
|
267
268
|
this.resize();
|
|
268
|
-
|
|
269
|
-
video.addEventListener("loadedmetadata", () => this.resize(0, 0, 0, 0), false);
|
|
270
|
-
}
|
|
269
|
+
video.addEventListener("resize", this.resize.bind(this));
|
|
271
270
|
if (typeof ResizeObserver !== "undefined") {
|
|
272
271
|
if (!this._ro)
|
|
273
|
-
this._ro = new ResizeObserver(() => this.resize(
|
|
272
|
+
this._ro = new ResizeObserver(() => this.resize());
|
|
274
273
|
this._ro.observe(video);
|
|
275
274
|
}
|
|
276
275
|
} else {
|
|
@@ -330,6 +329,29 @@ const _JASSUB = class extends EventTarget {
|
|
|
330
329
|
callback(err, styles);
|
|
331
330
|
});
|
|
332
331
|
}
|
|
332
|
+
addFont(font) {
|
|
333
|
+
this.sendMessage("addFont", { font });
|
|
334
|
+
}
|
|
335
|
+
_getLocalFont({ font }) {
|
|
336
|
+
try {
|
|
337
|
+
navigator.permissions.request({ name: "local-fonts" }).then((permission) => {
|
|
338
|
+
if (permission.state === "granted") {
|
|
339
|
+
queryLocalFonts().then((fontData) => {
|
|
340
|
+
const filtered = fontData && fontData.filter((obj) => obj.fullName.toLowerCase() === font);
|
|
341
|
+
if (filtered && filtered.length) {
|
|
342
|
+
filtered[0].blob().then((blob) => {
|
|
343
|
+
blob.arrayBuffer().then((buffer) => {
|
|
344
|
+
this.addFont(new Uint8Array(buffer));
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
} catch (e) {
|
|
352
|
+
console.warn("Local fonts API:", e);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
333
355
|
_unbusy() {
|
|
334
356
|
this.busy = false;
|
|
335
357
|
}
|
|
@@ -438,6 +460,7 @@ const _JASSUB = class extends EventTarget {
|
|
|
438
460
|
this._video.removeEventListener("seeking", this._timeupdate);
|
|
439
461
|
this._video.removeEventListener("playing", this._timeupdate);
|
|
440
462
|
this._video.removeEventListener("ratechange", this.setRate);
|
|
463
|
+
this._video.removeEventListener("resize", this.resize);
|
|
441
464
|
}
|
|
442
465
|
}
|
|
443
466
|
destroy(err) {
|
package/dist/jassub.umd.js
CHANGED
|
@@ -1 +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});
|
|
1
|
+
(function(h,n){typeof exports=="object"&&typeof module!="undefined"?module.exports=n():typeof define=="function"&&define.amd?define(n):(h=typeof globalThis!="undefined"?globalThis:h||self,h.JASSUB=n())})(this,function(){"use strict";var p=Object.defineProperty;var g=Object.getOwnPropertySymbols;var y=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable;var u=(h,n,l)=>n in h?p(h,n,{enumerable:!0,configurable:!0,writable:!0,value:l}):h[n]=l,v=(h,n)=>{for(var l in n||(n={}))y.call(n,l)&&u(h,l,n[l]);if(g)for(var l of g(n))b.call(n,l)&&u(h,l,n[l]);return h};var _=(h,n,l)=>(u(h,typeof n!="symbol"?n+"":n,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[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(c=>i(o,c))},a=Date.now(),s=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(r=>i(s,r)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(l){cancelAnimationFrame(this._rvfcpolyfillmap[l]),delete this._rvfcpolyfillmap[l]});const n=class extends EventTarget{constructor(e={}){var s,r,o;super(),globalThis.Worker||this.destroy("Worker not supported"),n._test();const t=e.blendMode||"js",i=typeof createImageBitmap!="undefined"&&((s=e.asyncRender)!=null?s:!0),a=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=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&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(n._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||{"liberation sans":"./default.woff2"},fallbackFont:e.fallbackFont||"liberation sans",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:n._hasAlphaBug,useLocalFonts:"queryLocalFonts"in self&&!!e.useLocalFonts}),a===!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(n._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&&(n._supportsWebAssembly=new WebAssembly.Instance(o)instanceof WebAssembly.Instance)}}catch{n._supportsWebAssembly=!1}const i=document.createElement("canvas"),a=i.getContext("2d");e.width=i.width=1,e.height=i.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const s=a.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(e,0,0);const r=a.getImageData(0,0,1,1).data;n._hasAlphaBug=s[1]!==r[1],n._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),i.remove()}resize(e=0,t=0,i=0,a=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),a=s.x}(this._canvas.style.top!==i+"px"||this._canvas.style.left!==a+"px")&&(s!=null&&(this._canvas.style.top=i+"px",this._canvas.style.left=a+"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,a=t/i;let s=t,r=i;a>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 a=i<1?-1:1;let s=t;a*s*i<=a*this.prescaleHeightLimit?s*=i:a*s<a*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",this._timeupdate.bind(this),!1),e.addEventListener("progress",this._timeupdate.bind(this),!1),e.addEventListener("waiting",this._timeupdate.bind(this),!1),e.addEventListener("seeking",this._timeupdate.bind(this),!1),e.addEventListener("playing",this._timeupdate.bind(this),!1),e.addEventListener("ratechange",this.setRate.bind(this),!1)),e.videoWidth>0&&this.resize(),e.addEventListener("resize",this.resize.bind(this)),typeof ResizeObserver!="undefined"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize())),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)})}addFont(e){this.sendMessage("addFont",{font:e})}_getLocalFont({font:e}){try{navigator.permissions.request({name:"local-fonts"}).then(t=>{t.state==="granted"&&queryLocalFonts().then(i=>{const a=i&&i.filter(s=>s.fullName.toLowerCase()===e);a&&a.length&&a[0].blob().then(s=>{s.arrayBuffer().then(r=>{this.addFont(new Uint8Array(r))})})})})}catch(t){console.warn("Local fonts API:",t)}}_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 a=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()-a;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(n._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,a=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(a))},r=o=>{t(o),this._worker.removeEventListener("message",s),this._worker.removeEventListener("error",r),clearTimeout(a)};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),this._video.removeEventListener("resize",this.resize))}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=n;return _(h,"_supportsWebAssembly",null),_(h,"_hasAlphaBug",null),h});
|
package/package.json
CHANGED
package/src/jassub.js
CHANGED
|
@@ -25,8 +25,9 @@ export default class JASSUB extends EventTarget {
|
|
|
25
25
|
* @param {String} [options.subUrl=options.subContent] The URL of the subtitle file to play.
|
|
26
26
|
* @param {String} [options.subContent=options.subUrl] The content of the subtitle file to play.
|
|
27
27
|
* @param {String[]|Uint8Array[]} [options.fonts] An array of links or Uint8Arrays to the fonts used in the subtitle. If Uint8Array is used the array is copied, not referenced. This forces all the fonts in this array to be loaded by the renderer, regardless of if they are used.
|
|
28
|
-
* @param {Object} [options.availableFonts] Object with all available fonts - Key is font
|
|
29
|
-
* @param {String} [options.fallbackFont='
|
|
28
|
+
* @param {Object} [options.availableFonts={'liberation sans': './default.woff2'}] Object with all available fonts - Key is font family in lower case, value is link or Uint8Array: { arial: '/font1.ttf' }. These fonts are selectively loaded if detected as used in the current subtitle track.
|
|
29
|
+
* @param {String} [options.fallbackFont='liberation sans'] The font family key of the fallback font in availableFonts to use if the other font for the style is missing special glyphs or unicode.
|
|
30
|
+
* @param {Boolean} [options.useLocalFonts=false] If the Local Font Access API is enabled [chrome://flags/#font-access], the library will query for permissions to use local fonts and use them if any are missing. The permission can be queried beforehand using navigator.permissions.request({ name: 'local-fonts' }).
|
|
30
31
|
* @param {Number} [options.libassMemoryLimit] libass bitmap cache memory limit in MiB (approximate).
|
|
31
32
|
* @param {Number} [options.libassGlyphLimit] libass glyph cache memory limit in MiB (approximate).
|
|
32
33
|
*/
|
|
@@ -91,14 +92,15 @@ export default class JASSUB extends EventTarget {
|
|
|
91
92
|
subUrl: options.subUrl,
|
|
92
93
|
subContent: options.subContent || null,
|
|
93
94
|
fonts: options.fonts || [],
|
|
94
|
-
availableFonts: options.availableFonts ||
|
|
95
|
-
fallbackFont: options.fallbackFont || '
|
|
95
|
+
availableFonts: options.availableFonts || { 'liberation sans': './default.woff2' },
|
|
96
|
+
fallbackFont: options.fallbackFont || 'liberation sans',
|
|
96
97
|
debug: this.debug,
|
|
97
98
|
targetFps: options.targetFps || 24,
|
|
98
99
|
dropAllAnimations: options.dropAllAnimations,
|
|
99
100
|
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
100
101
|
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
101
|
-
hasAlphaBug: JASSUB._hasAlphaBug
|
|
102
|
+
hasAlphaBug: JASSUB._hasAlphaBug,
|
|
103
|
+
useLocalFonts: ('queryLocalFonts' in self) && !!options.useLocalFonts
|
|
102
104
|
})
|
|
103
105
|
if (_offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
|
104
106
|
this.setVideo(options.video)
|
|
@@ -275,21 +277,18 @@ export default class JASSUB extends EventTarget {
|
|
|
275
277
|
if (this._onDemandRender !== true) {
|
|
276
278
|
this._playstate = video.paused
|
|
277
279
|
|
|
278
|
-
video.addEventListener('timeupdate',
|
|
279
|
-
video.addEventListener('progress',
|
|
280
|
-
video.addEventListener('waiting',
|
|
281
|
-
video.addEventListener('seeking',
|
|
282
|
-
video.addEventListener('playing',
|
|
283
|
-
video.addEventListener('ratechange',
|
|
284
|
-
}
|
|
285
|
-
if (video.videoWidth > 0) {
|
|
286
|
-
this.resize()
|
|
287
|
-
} else {
|
|
288
|
-
video.addEventListener('loadedmetadata', () => this.resize(0, 0, 0, 0), false)
|
|
280
|
+
video.addEventListener('timeupdate', this._timeupdate.bind(this), false)
|
|
281
|
+
video.addEventListener('progress', this._timeupdate.bind(this), false)
|
|
282
|
+
video.addEventListener('waiting', this._timeupdate.bind(this), false)
|
|
283
|
+
video.addEventListener('seeking', this._timeupdate.bind(this), false)
|
|
284
|
+
video.addEventListener('playing', this._timeupdate.bind(this), false)
|
|
285
|
+
video.addEventListener('ratechange', this.setRate.bind(this), false)
|
|
289
286
|
}
|
|
287
|
+
if (video.videoWidth > 0) this.resize()
|
|
288
|
+
video.addEventListener('resize', this.resize.bind(this))
|
|
290
289
|
// Support Element Resize Observer
|
|
291
290
|
if (typeof ResizeObserver !== 'undefined') {
|
|
292
|
-
if (!this._ro) this._ro = new ResizeObserver(() => this.resize(
|
|
291
|
+
if (!this._ro) this._ro = new ResizeObserver(() => this.resize())
|
|
293
292
|
this._ro.observe(video)
|
|
294
293
|
}
|
|
295
294
|
} else {
|
|
@@ -470,6 +469,35 @@ export default class JASSUB extends EventTarget {
|
|
|
470
469
|
})
|
|
471
470
|
}
|
|
472
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Adds a font to the renderer.
|
|
474
|
+
* @param {String|Uint8Array} font Font to add.
|
|
475
|
+
*/
|
|
476
|
+
addFont (font) {
|
|
477
|
+
this.sendMessage('addFont', { font })
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
_getLocalFont ({ font }) {
|
|
481
|
+
try {
|
|
482
|
+
navigator.permissions.request({ name: 'local-fonts' }).then(permission => {
|
|
483
|
+
if (permission.state === 'granted') {
|
|
484
|
+
queryLocalFonts().then(fontData => {
|
|
485
|
+
const filtered = fontData && fontData.filter(obj => obj.fullName.toLowerCase() === font)
|
|
486
|
+
if (filtered && filtered.length) {
|
|
487
|
+
filtered[0].blob().then(blob => {
|
|
488
|
+
blob.arrayBuffer().then(buffer => {
|
|
489
|
+
this.addFont(new Uint8Array(buffer))
|
|
490
|
+
})
|
|
491
|
+
})
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
} catch (e) {
|
|
497
|
+
console.warn('Local fonts API:', e)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
473
501
|
_unbusy () {
|
|
474
502
|
this.busy = false
|
|
475
503
|
}
|
|
@@ -596,6 +624,7 @@ export default class JASSUB extends EventTarget {
|
|
|
596
624
|
this._video.removeEventListener('seeking', this._timeupdate)
|
|
597
625
|
this._video.removeEventListener('playing', this._timeupdate)
|
|
598
626
|
this._video.removeEventListener('ratechange', this.setRate)
|
|
627
|
+
this._video.removeEventListener('resize', this.resize)
|
|
599
628
|
}
|
|
600
629
|
}
|
|
601
630
|
|
package/dist/jassub-worker.data
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0"?>
|
|
2
|
-
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
|
3
|
-
<fontconfig>
|
|
4
|
-
<dir>/fonts</dir>
|
|
5
|
-
<match target="pattern">
|
|
6
|
-
<test qual="any" name="family">
|
|
7
|
-
<string>mono</string>
|
|
8
|
-
</test>
|
|
9
|
-
<edit name="family" mode="assign" binding="same">
|
|
10
|
-
<string>monospace</string>
|
|
11
|
-
</edit>
|
|
12
|
-
</match>
|
|
13
|
-
<match target="pattern">
|
|
14
|
-
<test qual="any" name="family">
|
|
15
|
-
<string>sans serif</string>
|
|
16
|
-
</test>
|
|
17
|
-
<edit name="family" mode="assign" binding="same">
|
|
18
|
-
<string>sans-serif</string>
|
|
19
|
-
</edit>
|
|
20
|
-
</match>
|
|
21
|
-
<match target="pattern">
|
|
22
|
-
<test qual="any" name="family">
|
|
23
|
-
<string>sans</string>
|
|
24
|
-
</test>
|
|
25
|
-
<edit name="family" mode="assign" binding="same">
|
|
26
|
-
<string>sans-serif</string>
|
|
27
|
-
</edit>
|
|
28
|
-
</match>
|
|
29
|
-
<cachedir>/fontconfig</cachedir>
|
|
30
|
-
<config>
|
|
31
|
-
<rescan>
|
|
32
|
-
<int>30</int>
|
|
33
|
-
</rescan>
|
|
34
|
-
</config>
|
|
35
|
-
</fontconfig>
|