From 38e7b37878b6630f75de38ee7b353f44abb029c9 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Mon, 14 Sep 2026 01:12:03 +0800 Subject: [PATCH] refactor(jassub): [PKG-JASSUB-03] isolate registration and fix adapter cleanup --- docs/compiled/artplayer-plugin-jassub.js | 2 +- .../artplayer-plugin-jassub.legacy.js | 2 +- docs/compiled/artplayer-plugin-jassub.mjs | 44 +- package.json | 4 +- .../artplayer-plugin-jassub/ARCHITECTURE.md | 25 +- .../dist/artplayer-plugin-jassub.js | 2 +- .../dist/artplayer-plugin-jassub.legacy.js | 2 +- .../dist/artplayer-plugin-jassub.mjs | 44 +- packages/artplayer-plugin-jassub/src/index.js | 20 +- .../src/registration.js | 39 + .../jassub-registration-validation.json | 6186 +++++++++++++++++ .../2026-09-14-PKG-JASSUB-03-registration.md | 46 + refactor/plan.md | 5 +- refactor/progress.md | 9 + refactor/risk-table.md | 2 +- refactor/risks.json | 22 +- refactor/tasks.json | 7 +- test/browser/README.md | 7 + test/browser/jassub-native.spec.js | 26 +- test/helpers/jassub.js | 8 +- test/jassub-registration.test.js | 121 + 21 files changed, 6553 insertions(+), 70 deletions(-) create mode 100644 packages/artplayer-plugin-jassub/src/registration.js create mode 100644 refactor/baselines/jassub-registration-validation.json create mode 100644 refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md create mode 100644 test/jassub-registration.test.js diff --git a/docs/compiled/artplayer-plugin-jassub.js b/docs/compiled/artplayer-plugin-jassub.js index 985434418..c882d2acb 100644 --- a/docs/compiled/artplayer-plugin-jassub.js +++ b/docs/compiled/artplayer-plugin-jassub.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(t.artplayerPluginJassub=t(),define(function(){return t.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=(t,s,i)=>((t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i)(t,"symbol"!==typeof s?s+"":s,i);typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const s={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},i={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},a=class e extends EventTarget{constructor(t){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const s=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(t.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(t.offscreenRender??!0),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),s.then(()=>{this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:t.wasmUrl??"jassub-worker.wasm",legacyWasmUrl:t.legacyWasmUrl??"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(t.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(t.useLocalFonts??!0),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{e._supportsSIMD=!1}}static async _testImageBugs(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(await createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()}static async _test(){e._testSIMD(),await e._testImageBugs()}resize(e=0,t=0,s=0,i=0,a=this._video?.paused){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=s[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}};t(a,"_supportsSIMD",null),t(a,"_hasAlphaBug",null),t(a,"_hasBitmapBug",null);let r=a;return function(e){return t=>{const s=new r({video:t.video,...e});return s._canvasParent.style.zIndex=20,t.on("destroy",()=>{s.destroy()}),{name:"artplayerPluginJassub",instance:s}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginJassub=t(),define(function(){return e.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=(t,s,i)=>((t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i)(t,"symbol"!==typeof s?s+"":s,i);typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const s={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},i={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},a=class e extends EventTarget{constructor(t){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const s=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(t.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(t.offscreenRender??!0),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),s.then(()=>{this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:t.wasmUrl??"jassub-worker.wasm",legacyWasmUrl:t.legacyWasmUrl??"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(t.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(t.useLocalFonts??!0),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{e._supportsSIMD=!1}}static async _testImageBugs(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(await createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()}static async _test(){e._testSIMD(),await e._testImageBugs()}resize(e=0,t=0,s=0,i=0,a=this._video?.paused){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=s[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}};t(a,"_supportsSIMD",null),t(a,"_hasAlphaBug",null),t(a,"_hasBitmapBug",null);let r=a;return function(e){return t=>function(e,t){const s=new r({video:e.video,...t});let i=!1;const a=()=>{if(!i&&!s._destroyed){i=!0;try{s.destroy()}catch(e){throw i=!1,e}}};try{s._canvasParent&&(s._canvasParent.style.zIndex=20),e.on("destroy",a)}catch(n){try{e.off("destroy",a)}catch{}try{a()}catch{}throw n}return{name:"artplayerPluginJassub",instance:s}}(t,e)}}); diff --git a/docs/compiled/artplayer-plugin-jassub.legacy.js b/docs/compiled/artplayer-plugin-jassub.legacy.js index 908b38a90..f5c40ceed 100644 --- a/docs/compiled/artplayer-plugin-jassub.legacy.js +++ b/docs/compiled/artplayer-plugin-jassub.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(t.artplayerPluginJassub=t(),define(function(){return t.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i,r=(e,r)=>{for(var n in r||(r={}))s.call(r,n)&&a(e,n,r[n]);if(t)for(var n of t(r))i.call(r,n)&&a(e,n,r[n]);return e},n=(e,t,s)=>a(e,"symbol"!==typeof t?t+"":t,s),o=(e,t,s)=>new Promise((i,a)=>{var r=e=>{try{o(s.next(e))}catch(t){a(t)}},n=e=>{try{o(s.throw(e))}catch(t){a(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(r,n);o((s=s.apply(e,t)).next())});typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const h={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},d={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},l=class e extends EventTarget{constructor(t){var s,i;if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const a=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(null==(s=t.onDemandRender)||s),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(null==(i=t.offscreenRender)||i),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),a.then(()=>{var s,i,a,r;this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:null!=(s=t.wasmUrl)?s:"jassub-worker.wasm",legacyWasmUrl:null!=(i=t.legacyWasmUrl)?i:"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(null==(a=t.asyncRender)||a),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(null==(r=t.useLocalFonts)||r),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch(t){e._supportsSIMD=!1}}static _testImageBugs(){return o(this,null,function*(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch(o){console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(yield createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()})}static _test(){return o(this,null,function*(){e._testSIMD(),yield e._testImageBugs()})}resize(e=0,t=0,s=0,i=0,a=(e=>null==(e=this._video)?void 0:e.paused)()){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=null==t?void 0:t.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(null==(t=null==navigator?void 0:navigator.permissions)?void 0:t.query)?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=h[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e){return o(this,arguments,function*(e,t={},s){yield this._loaded,s?this._worker.postMessage(r({target:e,transferable:s},t),[...s]):this._worker.postMessage(r({target:e},t))})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){var t,s;return e&&(e=this._error(e)),this._video&&this._canvasParent&&(null==(t=this._video.parentNode)||t.removeChild(this._canvasParent)),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),null==(s=this._worker)||s.terminate(),e}};n(l,"_supportsSIMD",null),n(l,"_hasAlphaBug",null),n(l,"_hasBitmapBug",null);let c=l;return function(e){return t=>{const s=new c(r({video:t.video},e));return s._canvasParent.style.zIndex=20,t.on("destroy",()=>{s.destroy()}),{name:"artplayerPluginJassub",instance:s}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginJassub=t(),define(function(){return e.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i,r=(e,r)=>{for(var n in r||(r={}))s.call(r,n)&&a(e,n,r[n]);if(t)for(var n of t(r))i.call(r,n)&&a(e,n,r[n]);return e},n=(e,t,s)=>a(e,"symbol"!==typeof t?t+"":t,s),o=(e,t,s)=>new Promise((i,a)=>{var r=e=>{try{o(s.next(e))}catch(t){a(t)}},n=e=>{try{o(s.throw(e))}catch(t){a(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(r,n);o((s=s.apply(e,t)).next())});typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const h={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},d={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},l=class e extends EventTarget{constructor(t){var s,i;if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const a=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(null==(s=t.onDemandRender)||s),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(null==(i=t.offscreenRender)||i),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),a.then(()=>{var s,i,a,r;this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:null!=(s=t.wasmUrl)?s:"jassub-worker.wasm",legacyWasmUrl:null!=(i=t.legacyWasmUrl)?i:"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(null==(a=t.asyncRender)||a),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(null==(r=t.useLocalFonts)||r),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch(t){e._supportsSIMD=!1}}static _testImageBugs(){return o(this,null,function*(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch(o){console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(yield createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()})}static _test(){return o(this,null,function*(){e._testSIMD(),yield e._testImageBugs()})}resize(e=0,t=0,s=0,i=0,a=(e=>null==(e=this._video)?void 0:e.paused)()){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=null==t?void 0:t.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(null==(t=null==navigator?void 0:navigator.permissions)?void 0:t.query)?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=h[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e){return o(this,arguments,function*(e,t={},s){yield this._loaded,s?this._worker.postMessage(r({target:e,transferable:s},t),[...s]):this._worker.postMessage(r({target:e},t))})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){var t,s;return e&&(e=this._error(e)),this._video&&this._canvasParent&&(null==(t=this._video.parentNode)||t.removeChild(this._canvasParent)),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),null==(s=this._worker)||s.terminate(),e}};n(l,"_supportsSIMD",null),n(l,"_hasAlphaBug",null),n(l,"_hasBitmapBug",null);let c=l;return function(e){return t=>function(e,t){const s=new c(r({video:e.video},t));let i=!1;const a=()=>{if(!i&&!s._destroyed){i=!0;try{s.destroy()}catch(e){throw i=!1,e}}};try{s._canvasParent&&(s._canvasParent.style.zIndex=20),e.on("destroy",a)}catch(n){try{e.off("destroy",a)}catch(o){}try{a()}catch(o){}throw n}return{name:"artplayerPluginJassub",instance:s}}(t,e)}}); diff --git a/docs/compiled/artplayer-plugin-jassub.mjs b/docs/compiled/artplayer-plugin-jassub.mjs index b25489f1d..24386bec2 100644 --- a/docs/compiled/artplayer-plugin-jassub.mjs +++ b/docs/compiled/artplayer-plugin-jassub.mjs @@ -557,21 +557,39 @@ __publicField(_o, "_hasAlphaBug", null); /** @type {boolean|null} */ __publicField(_o, "_hasBitmapBug", null); let o = _o; -function artplayerPluginJassub(option) { - return (art) => { - const instance = new o({ - video: art.video, - ...option - }); - instance._canvasParent.style.zIndex = 20; - art.on("destroy", () => { +function registerJassub(art, option) { + const instance = new o({ video: art.video, ...option }); + let disposed = false; + const dispose = () => { + if (disposed || instance._destroyed) + return; + disposed = true; + try { instance.destroy(); - }); - return { - name: "artplayerPluginJassub", - instance - }; + } catch (error) { + disposed = false; + throw error; + } }; + try { + if (instance._canvasParent) + instance._canvasParent.style.zIndex = 20; + art.on("destroy", dispose); + } catch (error) { + try { + art.off("destroy", dispose); + } catch { + } + try { + dispose(); + } catch { + } + throw error; + } + return { name: "artplayerPluginJassub", instance }; +} +function artplayerPluginJassub(option) { + return (art) => registerJassub(art, option); } export { artplayerPluginJassub as default diff --git a/package.json b/package.json index d5ff76b98..861603ef5 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports", "test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js test/asr-distribution.test.js", "typecheck": "node scripts/typecheck.mjs", - "test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js", + "test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/jassub-registration.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js", "test:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs", "test": "yarn test:node && yarn test:baseline", "test:browser": "playwright test", @@ -90,7 +90,7 @@ "test:vtt-thumbnail-types-package": "node refactor/scripts/vtt-thumbnail-package-types.mjs", "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs refactor/scripts/multiple-subtitles-runtime-types.test.mjs refactor/scripts/multiple-subtitles-types.test.mjs", "test:multiple-subtitles-types-package": "node refactor/scripts/multiple-subtitles-package-types.mjs", - "test:jassub": "node --test test/jassub.test.js refactor/scripts/jassub-contract.test.mjs refactor/scripts/jassub-provenance.test.mjs refactor/scripts/jassub-failures.test.mjs", + "test:jassub": "node --test test/jassub.test.js test/jassub-registration.test.js refactor/scripts/jassub-contract.test.mjs refactor/scripts/jassub-provenance.test.mjs refactor/scripts/jassub-failures.test.mjs", "check:ci": "node refactor/scripts/ci-workflow.mjs", "test:ci": "node --test test/ci-summary.test.js refactor/scripts/ci-workflow.test.mjs refactor/scripts/impact.test.mjs test/package-runtime.test.js", "test:package:runtime": "node scripts/package-runtime.mjs --canonical", diff --git a/packages/artplayer-plugin-jassub/ARCHITECTURE.md b/packages/artplayer-plugin-jassub/ARCHITECTURE.md index 5ec8746cf..5c48d77ce 100644 --- a/packages/artplayer-plugin-jassub/ARCHITECTURE.md +++ b/packages/artplayer-plugin-jassub/ARCHITECTURE.md @@ -1,9 +1,25 @@ # JASSUB maintenance -`src/index.js` is the ArtPlayer-owned adapter. It forwards `{ video: art.video, ...option }` -to the vendored constructor, sets the canvas parent's z-index to 20, registers a destroy -listener, and synchronously returns `{ name: 'artplayerPluginJassub', instance }`. The instance -is the actual vendor object. Options remain live until registration; a supplied video wins. +`src/index.js` preserves the lazy factory and delegates registration to +`src/registration.js`. The registration module constructs the actual vendor object +with `{ video: art.video, ...option }`, styles only a vendor-created canvas parent, +binds host cleanup and synchronously returns `{ name: 'artplayerPluginJassub', instance }`. +Options remain live until registration; a supplied video wins. This small split keeps +the public factory separate from ownership/error handling, without a general lifecycle +framework or new runtime dependency. + +Caller-supplied canvas nodes remain caller-owned. The adapter does not invent a parent +or delete those nodes; vendor-created containers still get z-index 20. Host cleanup +dynamically calls the exposed instance's current destroy method, ignores already +destroyed instances and blocks synchronous reentry. A thrown disposal error is preserved +and the guard resets for a later attempt. Direct repeated vendor destroy is still a +separate PKG-JASSUB-07 problem; the adapter does not replace public instance methods. + +If styling or host subscription throws after construction, registration independently +attempts off with the exact callback and instance cleanup, then rethrows the original +error. Secondary rollback errors do not replace it. An invalid/custom host whose off +throws may retain an inert callback; failed vendor cleanup is not magically repaired. +Failures inside the vendor constructor before it returns an instance remain 07 work. `src/jassub.es.js` owns rendering, media listeners, canvas state, capability checks and worker messages. Keep this third-party file separate from the adapter's TypeScript migration. @@ -32,6 +48,7 @@ Use the pinned Node/Yarn toolchain: ```sh yarn test:jassub +node --test test/jassub-registration.test.js node refactor/scripts/jassub-provenance.test.mjs --network yarn build artplayer-plugin-jassub ``` diff --git a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js index 985434418..c882d2acb 100644 --- a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js +++ b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(t.artplayerPluginJassub=t(),define(function(){return t.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=(t,s,i)=>((t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i)(t,"symbol"!==typeof s?s+"":s,i);typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const s={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},i={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},a=class e extends EventTarget{constructor(t){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const s=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(t.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(t.offscreenRender??!0),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),s.then(()=>{this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:t.wasmUrl??"jassub-worker.wasm",legacyWasmUrl:t.legacyWasmUrl??"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(t.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(t.useLocalFonts??!0),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{e._supportsSIMD=!1}}static async _testImageBugs(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(await createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()}static async _test(){e._testSIMD(),await e._testImageBugs()}resize(e=0,t=0,s=0,i=0,a=this._video?.paused){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=s[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}};t(a,"_supportsSIMD",null),t(a,"_hasAlphaBug",null),t(a,"_hasBitmapBug",null);let r=a;return function(e){return t=>{const s=new r({video:t.video,...e});return s._canvasParent.style.zIndex=20,t.on("destroy",()=>{s.destroy()}),{name:"artplayerPluginJassub",instance:s}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginJassub=t(),define(function(){return e.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=(t,s,i)=>((t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i)(t,"symbol"!==typeof s?s+"":s,i);typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const s={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},i={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},a=class e extends EventTarget{constructor(t){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const s=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(t.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(t.offscreenRender??!0),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),s.then(()=>{this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:t.wasmUrl??"jassub-worker.wasm",legacyWasmUrl:t.legacyWasmUrl??"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(t.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(t.useLocalFonts??!0),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{e._supportsSIMD=!1}}static async _testImageBugs(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(await createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()}static async _test(){e._testSIMD(),await e._testImageBugs()}resize(e=0,t=0,s=0,i=0,a=this._video?.paused){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=s[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}};t(a,"_supportsSIMD",null),t(a,"_hasAlphaBug",null),t(a,"_hasBitmapBug",null);let r=a;return function(e){return t=>function(e,t){const s=new r({video:e.video,...t});let i=!1;const a=()=>{if(!i&&!s._destroyed){i=!0;try{s.destroy()}catch(e){throw i=!1,e}}};try{s._canvasParent&&(s._canvasParent.style.zIndex=20),e.on("destroy",a)}catch(n){try{e.off("destroy",a)}catch{}try{a()}catch{}throw n}return{name:"artplayerPluginJassub",instance:s}}(t,e)}}); diff --git a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js index 908b38a90..f5c40ceed 100644 --- a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js +++ b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(t.artplayerPluginJassub=t(),define(function(){return t.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i,r=(e,r)=>{for(var n in r||(r={}))s.call(r,n)&&a(e,n,r[n]);if(t)for(var n of t(r))i.call(r,n)&&a(e,n,r[n]);return e},n=(e,t,s)=>a(e,"symbol"!==typeof t?t+"":t,s),o=(e,t,s)=>new Promise((i,a)=>{var r=e=>{try{o(s.next(e))}catch(t){a(t)}},n=e=>{try{o(s.throw(e))}catch(t){a(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(r,n);o((s=s.apply(e,t)).next())});typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const h={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},d={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},l=class e extends EventTarget{constructor(t){var s,i;if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const a=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(null==(s=t.onDemandRender)||s),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(null==(i=t.offscreenRender)||i),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),a.then(()=>{var s,i,a,r;this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:null!=(s=t.wasmUrl)?s:"jassub-worker.wasm",legacyWasmUrl:null!=(i=t.legacyWasmUrl)?i:"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(null==(a=t.asyncRender)||a),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(null==(r=t.useLocalFonts)||r),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch(t){e._supportsSIMD=!1}}static _testImageBugs(){return o(this,null,function*(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch(o){console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(yield createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()})}static _test(){return o(this,null,function*(){e._testSIMD(),yield e._testImageBugs()})}resize(e=0,t=0,s=0,i=0,a=(e=>null==(e=this._video)?void 0:e.paused)()){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=null==t?void 0:t.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(null==(t=null==navigator?void 0:navigator.permissions)?void 0:t.query)?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=h[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e){return o(this,arguments,function*(e,t={},s){yield this._loaded,s?this._worker.postMessage(r({target:e,transferable:s},t),[...s]):this._worker.postMessage(r({target:e},t))})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){var t,s;return e&&(e=this._error(e)),this._video&&this._canvasParent&&(null==(t=this._video.parentNode)||t.removeChild(this._canvasParent)),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),null==(s=this._worker)||s.terminate(),e}};n(l,"_supportsSIMD",null),n(l,"_hasAlphaBug",null),n(l,"_hasBitmapBug",null);let c=l;return function(e){return t=>{const s=new c(r({video:t.video},e));return s._canvasParent.style.zIndex=20,t.on("destroy",()=>{s.destroy()}),{name:"artplayerPluginJassub",instance:s}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginJassub=t(),define(function(){return e.artplayerPluginJassub})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginJassub=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable,a=(t,s,i)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[s]=i,r=(e,r)=>{for(var n in r||(r={}))s.call(r,n)&&a(e,n,r[n]);if(t)for(var n of t(r))i.call(r,n)&&a(e,n,r[n]);return e},n=(e,t,s)=>a(e,"symbol"!==typeof t?t+"":t,s),o=(e,t,s)=>new Promise((i,a)=>{var r=e=>{try{o(s.next(e))}catch(t){a(t)}},n=e=>{try{o(s.throw(e))}catch(t){a(t)}},o=e=>e.done?i(e.value):Promise.resolve(e.value).then(r,n);o((s=s.apply(e,t)).next())});typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(e){const t=performance.now(),s=this.getVideoPlaybackQuality(),i=this.mozPresentedFrames||this.mozPaintedFrames||s.totalVideoFrames-s.droppedVideoFrames,a=(r,n)=>{const o=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||o.totalVideoFrames-o.droppedVideoFrames;if(h>i){const i=this.mozFrameDelay||o.totalFrameDelay-s.totalFrameDelay||0,a=n-r;e(n,{presentationTime:n+1e3*i,expectedDisplayTime:n+a,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+a/1e3,presentedFrames:h,processingDuration:i}),delete this._rvfcpolyfillmap[t]}else this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(n,e))};return this._rvfcpolyfillmap[t]=requestAnimationFrame(e=>a(t,e)),t},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(e){cancelAnimationFrame(this._rvfcpolyfillmap[e]),delete this._rvfcpolyfillmap[e]});const h={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},d={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},l=class e extends EventTarget{constructor(t){var s,i;if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!t)throw this.destroy("No options provided");this._loaded=new Promise(e=>{this._init=e});const a=e._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(null==(s=t.onDemandRender)||s),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!t.canvas&&(null==(i=t.offscreenRender)||i),this.timeOffset=t.timeOffset||0,this._video=t.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=t.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!t.debug,this.prescaleFactor=t.prescaleFactor||1,this.prescaleHeightLimit=t.prescaleHeightLimit||1080,this.maxRenderHeight=t.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(t.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(t.workerUrl||"jassub-worker.js"),this._worker.onmessage=e=>this._onmessage(e),this._worker.onerror=e=>this._error(e),a.then(()=>{var s,i,a,r;this._worker.postMessage({target:"init",wasmUrl:e._supportsSIMD&&t.modernWasmUrl?t.modernWasmUrl:null!=(s=t.wasmUrl)?s:"jassub-worker.wasm",legacyWasmUrl:null!=(i=t.legacyWasmUrl)?i:"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(null==(a=t.asyncRender)||a),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:t.blendMode||"js",subUrl:t.subUrl,subContent:t.subContent||null,fonts:t.fonts||[],availableFonts:t.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:t.fallbackFont||"liberation sans",debug:this.debug,targetFps:t.targetFps||24,dropAllAnimations:t.dropAllAnimations,dropAllBlur:t.dropAllBlur,libassMemoryLimit:t.libassMemoryLimit||0,libassGlyphLimit:t.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(null==(r=t.useLocalFonts)||r),hasBitmapBug:e._hasBitmapBug}),!0===this._offscreenRender&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._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._canvas}static _testSIMD(){if(null===e._supportsSIMD)try{e._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch(t){e._supportsSIMD=!1}}static _testImageBugs(){return o(this,null,function*(){if(null!==e._hasBitmapBug)return;const t=document.createElement("canvas"),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Canvas rendering not supported");if("function"==typeof ImageData.prototype.constructor)try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch(o){console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(e,t,i){const a=s.createImageData(t,i);return e&&a.data.set(e),a}}const i=document.createElement("canvas"),a=i.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");t.width=i.width=1,t.height=i.height=1,s.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=a.getImageData(0,0,1,1).data;s.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(t,0,0);const n=a.getImageData(0,0,1,1).data;if(e._hasAlphaBug=r[1]!==n[1],e._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const t=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(yield createImageBitmap(new ImageData(t,1)),0,0);const{data:s}=a.getImageData(0,0,1,1);e._hasBitmapBug=!1;for(const[i,a]of s.entries())if(Math.abs(t[i]-a)>15){e._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else e._hasBitmapBug=!1;t.remove(),i.remove()})}static _test(){return o(this,null,function*(){e._testSIMD(),yield e._testImageBugs()})}resize(e=0,t=0,s=0,i=0,a=(e=>null==(e=this._video)?void 0:e.paused)()){if((!e||!t)&&this._video){const a=this._getVideoPosition();let r=null;if(this._videoWidth){const e=this._video.videoWidth/this._videoWidth,t=this._video.videoHeight/this._videoHeight;r=this._computeCanvasSize((a.width||0)/e,(a.height||0)/t)}else r=this._computeCanvasSize(a.width||0,a.height||0);e=r.width,t=r.height,this._canvasParent&&(s=a.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=a.x),this._canvas.style.width=a.width+"px",this._canvas.style.height=a.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",a&&!1===this.busy?this.busy=!0:a=!1,this.sendMessage("canvas",{width:e,height:t,videoWidth:this._videoWidth||this._video.videoWidth,videoHeight:this._videoHeight||this._video.videoHeight,force:a})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:a}=this._video;e=i,t=a,i/a>s?e=Math.floor(a*s):t=Math.floor(i/s);return{width:e,height:t,x:(i-e)/2,y:(a-t)/2}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let r=t*i;a*r*s<=a*this.prescaleHeightLimit?r*=s:a*r0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const t={seeking:!0,waiting:!0,playing:!1}[e];null!=t&&(this._playstate=t),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(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}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}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:s})=>{e(t,s)})}styleOverride(e){this.sendMessage("styleOverride",{style:e})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}setDefaultFont(e){this.sendMessage("defaultFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=null==t?void 0:t.find(t=>t.fullName.toLowerCase()===e);s&&s.blob().then(e=>{e.arrayBuffer().then(e=>{this.addFont(new Uint8Array(e))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(null==(t=null==navigator?void 0:navigator.permissions)?void 0:t.query)?navigator.permissions.query({name:"local-fonts"}).then(t=>{"granted"===t.state&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=h[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:a,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==a)&&(this._canvasctrl.width=i,this._canvasctrl.height=a,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const n of e)n.image&&(t?(this._ctx.drawImage(n.image,n.x,n.y),n.image.close()):(this._bufferCanvas.width=n.w,this._bufferCanvas.height=n.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(n.image)),n.w,n.h),0,0),this._ctx.drawImage(this._bufferCanvas,n.x,n.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let t=0;const i=s.bitmaps||e.length;delete s.bitmaps;for(const e in s)t+=s[e];console.log("Bitmaps: "+i+" Total: "+(0|t)+"ms",s)}}_fixAlpha(t){if(e._hasAlphaBug)for(let e=3;e1?t[e]:1;return t}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e){return o(this,arguments,function*(e,t={},s){yield this._loaded,s?this._worker.postMessage(r({target:e,transferable:s},t),[...s]):this._worker.postMessage(r({target:e},t))})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),a=({data:e})=>{e.target===s&&(t(null,e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i))},r=e=>{t(e),this._worker.removeEventListener("message",a),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",a),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){var t,s;return e&&(e=this._error(e)),this._video&&this._canvasParent&&(null==(t=this._video.parentNode)||t.removeChild(this._canvasParent)),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),null==(s=this._worker)||s.terminate(),e}};n(l,"_supportsSIMD",null),n(l,"_hasAlphaBug",null),n(l,"_hasBitmapBug",null);let c=l;return function(e){return t=>function(e,t){const s=new c(r({video:e.video},t));let i=!1;const a=()=>{if(!i&&!s._destroyed){i=!0;try{s.destroy()}catch(e){throw i=!1,e}}};try{s._canvasParent&&(s._canvasParent.style.zIndex=20),e.on("destroy",a)}catch(n){try{e.off("destroy",a)}catch(o){}try{a()}catch(o){}throw n}return{name:"artplayerPluginJassub",instance:s}}(t,e)}}); diff --git a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs index b25489f1d..24386bec2 100644 --- a/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs +++ b/packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs @@ -557,21 +557,39 @@ __publicField(_o, "_hasAlphaBug", null); /** @type {boolean|null} */ __publicField(_o, "_hasBitmapBug", null); let o = _o; -function artplayerPluginJassub(option) { - return (art) => { - const instance = new o({ - video: art.video, - ...option - }); - instance._canvasParent.style.zIndex = 20; - art.on("destroy", () => { +function registerJassub(art, option) { + const instance = new o({ video: art.video, ...option }); + let disposed = false; + const dispose = () => { + if (disposed || instance._destroyed) + return; + disposed = true; + try { instance.destroy(); - }); - return { - name: "artplayerPluginJassub", - instance - }; + } catch (error) { + disposed = false; + throw error; + } }; + try { + if (instance._canvasParent) + instance._canvasParent.style.zIndex = 20; + art.on("destroy", dispose); + } catch (error) { + try { + art.off("destroy", dispose); + } catch { + } + try { + dispose(); + } catch { + } + throw error; + } + return { name: "artplayerPluginJassub", instance }; +} +function artplayerPluginJassub(option) { + return (art) => registerJassub(art, option); } export { artplayerPluginJassub as default diff --git a/packages/artplayer-plugin-jassub/src/index.js b/packages/artplayer-plugin-jassub/src/index.js index 3f6e53eef..9c93ae78a 100644 --- a/packages/artplayer-plugin-jassub/src/index.js +++ b/packages/artplayer-plugin-jassub/src/index.js @@ -1,21 +1,5 @@ -import JASSUB from './jassub.es.js' +import { registerJassub } from './registration.js' export default function artplayerPluginJassub(option) { - return (art) => { - const instance = new JASSUB({ - video: art.video, - ...option, - }) - - instance._canvasParent.style.zIndex = 20 - - art.on('destroy', () => { - instance.destroy() - }) - - return { - name: 'artplayerPluginJassub', - instance, - } - } + return art => registerJassub(art, option) } diff --git a/packages/artplayer-plugin-jassub/src/registration.js b/packages/artplayer-plugin-jassub/src/registration.js new file mode 100644 index 000000000..0308f4293 --- /dev/null +++ b/packages/artplayer-plugin-jassub/src/registration.js @@ -0,0 +1,39 @@ +import JASSUB from './jassub.es.js' + +export function registerJassub(art, option) { + const instance = new JASSUB({ video: art.video, ...option }) + let disposed = false + const dispose = () => { + if (disposed || instance._destroyed) + return + disposed = true + try { + instance.destroy() + } + catch (error) { + disposed = false + throw error + } + } + + try { + if (instance._canvasParent) + instance._canvasParent.style.zIndex = 20 + art.on('destroy', dispose) + } + catch (error) { + // Registration may throw after attaching its listener. Keep the original + // error while independently attempting listener and instance rollback. + try { + art.off('destroy', dispose) + } + catch {} + try { + dispose() + } + catch {} + throw error + } + + return { name: 'artplayerPluginJassub', instance } +} diff --git a/refactor/baselines/jassub-registration-validation.json b/refactor/baselines/jassub-registration-validation.json new file mode 100644 index 000000000..8b5ca2bbe --- /dev/null +++ b/refactor/baselines/jassub-registration-validation.json @@ -0,0 +1,6186 @@ +{ + "task": "PKG-JASSUB-03", + "sourceCommit": "944e2cc9d00701a29fffdfca9afa4beec6d728d6", + "node": "v24.21.0", + "sources": [ + { + "file": "packages/artplayer-plugin-jassub/src/index.js", + "sha256": "c9528e19469def684224a6240a3beea83e516600057907f20ac1477e11c74c32" + }, + { + "file": "packages/artplayer-plugin-jassub/src/registration.js", + "sha256": "5c014841936f90e08c5a3116ad6e913b15ca78c843f96a37568497091a2bc2da" + }, + { + "file": "test/helpers/jassub.js", + "sha256": "b23d50937bf593aac4d82878dfaed7f947280c96018623ce8fcfe07d5ee65e74" + }, + { + "file": "test/jassub-registration.test.js", + "sha256": "06024c8bc841d0bd0542e0598d8fa664af1fa0edf9cb93c58114b23f84fb6331" + }, + { + "file": "test/browser/jassub-native.spec.js", + "sha256": "6dec7c6e8528c802294a7099889892627d8233a9fb35e10a68a9b7b00265c5f0" + } + ], + "tests": [ + { + "file": "refactor/.cache/jassub03-tests-final.log", + "sha256": "864ea50bb93708db47ca18f1ed214879fa4b5fa3f6abac8dd8d2bc597da1987a", + "passed": 128, + "failed": 0 + }, + { + "file": "refactor/.cache/jassub03-contract-final.log", + "sha256": "1b66f46608f08cf88f5cf718da3c0892880ceb57d8ada0c73af54c165bf2dd37", + "passed": 7, + "failed": 0 + }, + { + "file": "refactor/.cache/jassub03-artifact-js.log", + "sha256": "665dd0cfca1c8bce48af34a07e645bd9ac4d2ac3e54d568a54173d7cc93a19a7", + "passed": 15, + "failed": 0 + }, + { + "file": "refactor/.cache/jassub03-artifact-legacy.js.log", + "sha256": "84688f10fe1c30357694a55e2100e74385df115be9eee32ba9ce9d7870c5424e", + "passed": 15, + "failed": 0 + } + ], + "oldArtifactRed": { + "file": "refactor/.cache/jassub03-registration-red.log", + "sha256": "b31b93da958f073e13ac29910e0c822b4820ee8476e50bf5e579ba005cba9608", + "passed": 1, + "failed": 7, + "artifact": "Unrebuilt workspace main artifact at sourceCommit before this source change" + }, + "build": { + "file": "refactor/.cache/jassub03-build-final.log", + "sha256": "620e0a7f9e7ace80943d9f9d513c3d82cbf9913170db4d2cc8995b614eccf58e" + }, + "outputs": [ + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba", + "docs": "docs/compiled/artplayer-plugin-jassub.js" + }, + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734", + "docs": "docs/compiled/artplayer-plugin-jassub.legacy.js" + }, + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs", + "sha256": "5d05fab072b8a1f4a4dcf39c9607f3f3200b51aa1afba9c83a8bf372c6a762b4", + "docs": "docs/compiled/artplayer-plugin-jassub.mjs" + } + ], + "native": [ + { + "name": "custom-main", + "report": { + "file": "refactor/.cache/jassub03-native-custom-main.json", + "sha256": "0159dd7b7de7e007e0ab4aea1edca705978093dbdcf50801bf1dfe20609d4d95" + }, + "stats": { + "startTime": "2026-09-13T17:06:06.006Z", + "duration": 25705.416, + "expected": 9, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.50658, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.014403, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.110943, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.110943, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "b14c548fa54a855a50dd491ff263052e44c081e366ad847e7caa9273dc7c6a0e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.495742, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.014097, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.122268, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.122268, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "b14c548fa54a855a50dd491ff263052e44c081e366ad847e7caa9273dc7c6a0e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.524229, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.011964, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.117039, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.117039, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "71f044dfa5143eaf113e1345e2de8b66f9149f4221a91d5ebe12bfd1a91a5e03" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.445664, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000027, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.0409, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.0409, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "d923a319c2c83db4468be47731b8e6c12ebae9feb21dbac8266b95b616098a74" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_ERROR_PARSED_DATA_CACHED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.445105, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000021, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040453, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040453, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "9c8008c4785d8021db2b3acc8af41c6fa43665f7976dcb25baf865bc28312488" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.428252, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000026, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040537, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040537, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "832a7ac308c27290f7ec6484e502f593271e1c1580bd4fe2dc44ecfab48bc2d2" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.3763334, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.0441458, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1129375, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1129375, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "22fb0cc47e03876979954ad569f383afc65830436285cbdc076088f804d408e9" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.4103542, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.0507708, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3265834, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3265834, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "0223ecaf1891151822845bde60d2dde700933749e8ad412a6576e8e8967755f0" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.4494167, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.0632708, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1331459, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1331459, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "3af81258e17e184711c69060d8ba8b1a19eceb3c2a86ce2ee622f74fc5cc4b15" + } + ] + } + ] + }, + { + "name": "custom-legacy", + "report": { + "file": "refactor/.cache/jassub03-native-custom-legacy.json", + "sha256": "60083a592e01e35f436d8864f0f879eb3cb1a2413ad1924eeb843b9fbd22465b" + }, + "stats": { + "startTime": "2026-09-13T17:07:14.057Z", + "duration": 25365.765, + "expected": 9, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.499631, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.012003, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.112677, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.112677, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "ecfe0b2a8b5b3b4609b8a478db467f91c45fef9c57ec1ebda03e0500cf75fe1e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.502569, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.011387, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.115491, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.115491, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "ecfe0b2a8b5b3b4609b8a478db467f91c45fef9c57ec1ebda03e0500cf75fe1e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.498497, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.013058, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.098578, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.098578, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "187d1a46c31c16f80c027d779ca78c961f29a250f6bd49dc2ede1174e67cfbae" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.447014, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000021, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.041955, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.041955, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "dcfe4e47ed962c6a6b98380878819a43be1581adb21722733b81fcac88897c2c" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_ERROR_PARSED_DATA_CACHED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.211442, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000023, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040596, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040596, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "dcfe4e47ed962c6a6b98380878819a43be1581adb21722733b81fcac88897c2c" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "video" + }, + { + "target": "getColorSpace" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.430012, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000022, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.042724, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.042724, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "832a7ac308c27290f7ec6484e502f593271e1c1580bd4fe2dc44ecfab48bc2d2" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "candidate", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.4420417, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.06075, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1313959, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.1313959, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "172e2b1537e8c1601e14763a1ccde634b1982e7013df0f1eaa2e5f3d6c01abd9" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.4715834, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.0591458, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3451876, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3451876, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "6cd7efece06526c1bd76579f9e791cfedd5fee283e4e3c8a3e4c900ca32586ff" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=true", + "engine": "webkit", + "browser": "26.6", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": false, + "customCanvas": true, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "sha256": "e186265ddbb7db1de3c8def9bbd9769c9a33f6d69c296aef9a3e252ca6e0e734" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "canvas", + "width": 2560, + "height": 1440 + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "video" + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 2560, + "height": 1440 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 13152, + "signature": 1263898348, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.383125, + "connected": true + }, + { + "phase": "after-seek", + "visible": 30818, + "signature": 827736580, + "width": 1280, + "height": 720, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.0663125, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3610834, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 119170, + "signature": 1826006850, + "width": 2560, + "height": 1440, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.3610834, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "f14374e4d09c380ffde78f08848a49e18d99c22a918f831626a21779b1612a77" + } + ] + } + ] + }, + { + "name": "default-main", + "report": { + "file": "refactor/.cache/jassub03-native-default-main.json", + "sha256": "87a02946d509d22a7161d4f5ccfb41d7ea1c62632338951dd1ff0a72f59dea13" + }, + "stats": { + "startTime": "2026-09-13T17:09:20.836Z", + "duration": 14363.769, + "expected": 6, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=false", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "candidate", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.4 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.44 + }, + { + "target": "demand", + "time": 0.48 + }, + { + "target": "demand", + "time": 0.52 + }, + { + "target": "demand", + "time": 0.56 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "demand", + "time": 10.12 + }, + { + "target": "demand", + "time": 10.16 + }, + { + "target": "demand", + "time": 10.2 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.505923, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.011903, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.116975, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.116975, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "b14c548fa54a855a50dd491ff263052e44c081e366ad847e7caa9273dc7c6a0e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=false", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.4 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.44 + }, + { + "target": "demand", + "time": 0.48 + }, + { + "target": "demand", + "time": 0.52 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "demand", + "time": 10.12 + }, + { + "target": "demand", + "time": 10.16 + }, + { + "target": "demand", + "time": 10.2 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.500641, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.015416, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.103355, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.103355, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "ecfe0b2a8b5b3b4609b8a478db467f91c45fef9c57ec1ebda03e0500cf75fe1e" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=false", + "engine": "chromium", + "browser": "153.0.8010.12", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.4 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.44 + }, + { + "target": "demand", + "time": 0.48 + }, + { + "target": "demand", + "time": 0.52 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "demand", + "time": 10.12 + }, + { + "target": "demand", + "time": 10.16 + }, + { + "target": "demand", + "time": 10.2 + }, + { + "target": "demand", + "time": 10.24 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.499834, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.014991, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.120658, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.120658, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "71f044dfa5143eaf113e1345e2de8b66f9149f4221a91d5ebe12bfd1a91a5e03" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with candidate core, customCanvas=false", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [], + "evidence": { + "core": "candidate", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.44 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.48 + }, + { + "target": "demand", + "time": 0.52 + }, + { + "target": "demand", + "time": 0.56 + }, + { + "target": "demand", + "time": 0.64 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "demand", + "time": 10.12 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.57174, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.000022, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040211, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040211, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "8be8ce0db38ec2cf617168c4471d622941db1257e155672425299b6b9ea901d5" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published core, customCanvas=false", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_ERROR_PARSED_DATA_CACHED" + } + } + ], + "evidence": { + "core": "published", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.2 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.24 + }, + { + "target": "demand", + "time": 0.28 + }, + { + "target": "demand", + "time": 0.32 + }, + { + "target": "demand", + "time": 0.4 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.334339, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.00002, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040181, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 22298, + "signature": 2781790832, + "width": 1280, + "height": 720, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.040181, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "dcfe4e47ed962c6a6b98380878819a43be1581adb21722733b81fcac88897c2c" + } + ] + }, + { + "title": "JASSUB candidate artifact renders actual WASM subtitles through seek and layout with published-5.3.1-beta.1 core, customCanvas=false", + "engine": "firefox", + "browser": "155.0", + "platform": "win32", + "status": "passed", + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.1-beta.1", + "integrity": "sha512-btjbQwXPl86U/Apja0WrCUF+AXLPacI60eUGAeJTx3EMuUKrVaOFrmWrIeeyMUi5aIzMSXM++tQjlSMJVnmhuQ==", + "member": "package/dist/artplayer.js" + }, + "sha256": "e91e64228c57bb97300c709789d5557ab07b133cab307267b4a2a4df3083c861", + "bytes": 159458 + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/assets/sample/steve-jobs.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_ERROR_PARSED_DATA_CACHED" + } + } + ], + "evidence": { + "core": "published-5.3.1-beta.1", + "onDemandRender": true, + "customCanvas": false, + "source": { + "artifact": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "sha256": "1d2fb75dec766c30edb724b5faceae07369a90560aeea07490373193f4a2a4ba" + }, + "subtitleSha256": "12994d1b6a07c45fd9776cb8709afc37725a8003bd90e4e829b7cc44fbadc0ec", + "resources": [ + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker.js", + "status": 200, + "contentType": "text/javascript" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/jassub-worker-modern.wasm", + "status": 200, + "contentType": "application/wasm" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + }, + { + "url": "http://127.0.0.1:8084/assets/jassub/default.woff2", + "status": 200, + "contentType": "font/woff2" + } + ], + "external": [], + "state": { + "workers": [ + { + "url": "/assets/jassub/jassub-worker.js", + "terminated": 1, + "sent": [ + { + "target": "init", + "width": 300, + "height": 150 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "canvas", + "width": 640, + "height": 360 + }, + { + "target": "demand", + "time": 0.44 + }, + { + "target": "getColorSpace" + }, + { + "target": "demand", + "time": 0.48 + }, + { + "target": "demand", + "time": 0.52 + }, + { + "target": "demand", + "time": 0.56 + }, + { + "target": "demand", + "time": 0.6 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "demand", + "time": 10 + }, + { + "target": "canvas", + "width": 1280, + "height": 720 + }, + { + "target": "demand", + "time": 10.04 + }, + { + "target": "demand", + "time": 10.08 + }, + { + "target": "destroy" + } + ], + "received": [ + { + "target": "ready" + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 0, + "width": 640, + "height": 360 + }, + { + "target": "verifyColorSpace" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 640, + "height": 360 + }, + { + "target": "unbusy" + }, + { + "target": "render", + "images": 2, + "width": 1280, + "height": 720 + }, + { + "target": "unbusy" + } + ] + } + ], + "snapshots": [ + { + "phase": "before-seek", + "visible": 2676, + "signature": 1690974404, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 0.566267, + "connected": true + }, + { + "phase": "after-seek", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 640, + "cssHeight": 360, + "time": 10.00002, + "connected": true + }, + { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.04324, + "connected": true + } + ], + "errors": [], + "lastPixel": { + "phase": "fullscreen", + "visible": 6216, + "signature": 3526987101, + "width": 640, + "height": 360, + "cssWidth": 1280, + "cssHeight": 720, + "time": 10.04324, + "connected": true + } + }, + "limitation": "Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction." + }, + "screenshots": [ + { + "name": "actual-jassub-page", + "sha256": "0bad85004bd017c84243a4e5587ae09d4718a2010afcf2709cc7d6484d1c1aaa" + } + ] + } + ] + } + ], + "unchanged": [ + { + "file": "packages/artplayer-plugin-jassub/src/jassub.es.js", + "sha256": "b973508ade00b5b9cea2f299d49f4943003372729be7350b6938e44fddb32038" + }, + { + "file": "packages/artplayer-plugin-jassub/types/artplayer-plugin-jassub.d.ts", + "sha256": "0c00ce8fd60780c7c01d23534c193cedd32898dc79739e7cf13d8700a6ab7676" + } + ], + "limitations": [ + "Vendor remains unchanged; construction rollback before instance return, cross-parent/repeated direct destroy, RVFC and ratechange remain task07.", + "Custom canvas native matrix explicitly uses onDemandRender=false; no closure of WebKit default frame-clock failure.", + "No ESM native loading, full OffscreenCanvas/devices, sustained heap/GPU or complete distribution acceptance.", + "Original registration error wins over secondary best-effort rollback errors; a broken host off may retain an inert listener." + ] +} diff --git a/refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md b/refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md new file mode 100644 index 000000000..6032ffc2b --- /dev/null +++ b/refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md @@ -0,0 +1,46 @@ +# PKG-JASSUB-03 自有注册与清理职责 + +入口仅保留惰性工厂,新增 registration.js 管理实例构造后的样式、宿主监听和 +失败回滚。仍在 registrar 调用时读取 art.video 再展开原 option;返回同步的 +原 vendor 实例,不复制 options、不包装公开 instance、不绑定或改写其 destroy。 +两文件职责明确,未建立额外通用框架。vendor JS、Worker/WASM、字体、声明未变。 +严格 TS/公开类型迁移继续由 04 处理;vendor 的实质修复在独立 07 中完成。 + +## 修复及兼容边界 + +只有 vendor 创建了 _canvasParent 时才设置原 z-index=20。用户自定义 canvas +不再在 Worker 创建后因空 parent 抛错,canvas 节点仍归调用者,销毁后不删除它。 +宿主清理检查 vendor 已销毁状态,并在调用期间防同步重入;仍动态查找当前 +instance.destroy,所以用户包装该方法继续生效。正常销毁错误原样传播, +内部 guard 在失败时复位,允许下次尝试;成功后本次注册不重复清理。 + +构造成功后若样式或宿主订阅失败,分别尽力移除相同 callback 并销毁实例, +始终抛原错误。off 自身失败不会阻止尝试释放实例;这种异常宿主可能保留已 +失效的 callback,不能宣称已强制清除所有外部状态。vendor 构造尚未返回实例 +时的遗留资源,以及 vendor 自己的 destroy 再次调用、跨父节点、旧 RVFC 和 +ratechange 问题继续保留在 07。JASSUB-DESTROY-01 因而仍 open。 + +## 验证 + +新增八项候选回归:自定义 canvas、直接再宿主销毁、ready 前销毁、同步重入、 +错误传播/重试、订阅失败的两个回滚边界、样式失败。相同断言先对未重建的旧 +工作区主产物运行,1 通过/7 失败;改造源码八项通过。历史 63 项失败/保护断言 +保持原期待,本包联合 128 项通过;候选公共行为七项通过。 + +正常脚本构建 main/legacy/ESM 并生成 docs 副本,产物各 15 项公共行为和清理 +测试通过。原生浏览器以实际候选产物和真实 Worker/WASM/字体验证自定义 canvas、 +seek 后字幕、网页全屏、直接 instance.destroy 再 art.destroy(false),并检查 +调用者 canvas 保留、Worker 只终止一次。运行选择、数量和指纹见 +[验证记录](../baselines/jassub-registration-validation.json)。自定义 canvas 矩阵 +显式使用 onDemandRender=false;不能据此关闭 WebKit 默认逐帧问题。默认逐帧 +模式另测 Chromium/Firefox 的 vendor 创建容器路径,仍保留原生验收边界。 + +test:jassub 与 test:unit 加入候选注册测试。原生测试支持 +ARTPLAYER_JASSUB_ARTIFACT 指定实际 global 产物,ARTPLAYER_JASSUB_CUSTOM_CANVAS=true +指定调用者 canvas,报告区分 npm 归档与候选文件。没有提供这些参数时仍是 +02 的真实发布基线;该套件不把 ESM 当普通 script 加载。 + +定向 lint、原有来源/资产校验及计划/风险校验通过;没有新增依赖、 +锁变化或远端运行。本次关闭自定义 canvas 的 adapter 缺陷,其他实例、设备、 +字体许可与完整分发问题保持原任务门槛。回退 index/registration 并正常重建 +即可恢复原行为;不要直接编辑 dist。独立本地提交,不推送或发布。 diff --git a/refactor/plan.md b/refactor/plan.md index b371a788d..c3d0f665b 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 232 项,范围 22 个包及工作区/示例。 -状态:todo 65 / doing 14 / blocked 0 / done 153 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 64 / doing 14 / blocked 0 / done 154 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -231,7 +231,7 @@ | --- | --- | --- | --- | --- | --- | --- | | PKG-JASSUB-01 | artplayer-plugin-jassub
核对包契约与历史用法 | BASE-05 | 选项透传、result.instance、vendor 来源、worker/WASM/font 路径 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结;接续 VENDOR-04/05,核对 wrapper/worker/WASM/font 的独立来源与通知,保持资源路径和选项透传 | H | done | | PKG-JASSUB-02 | artplayer-plugin-jassub
建立特有行为与错误测试 | PKG-JASSUB-01, ENG-03, ENG-05 | ASS 字体、时钟/seek/倍率/resize、加载失败和销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | -| PKG-JASSUB-03 | artplayer-plugin-jassub
整理内部职责与资源 | PKG-JASSUB-02, CORE-15, CORE-16 | 仅整理自有 adapter/销毁;保留第三方文件及来源 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | +| PKG-JASSUB-03 | artplayer-plugin-jassub
整理内部职责与资源 | PKG-JASSUB-02, CORE-15, CORE-16 | 仅整理自有 adapter/销毁;保留第三方文件及来源 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | | PKG-JASSUB-04 | artplayer-plugin-jassub
迁移自有源码和公开类型 | PKG-JASSUB-03, ENG-04, ENG-06, CORE-07 | JASSUB option/instance 的兼容类型包装,vendor JS 例外记录 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | | PKG-JASSUB-05 | artplayer-plugin-jassub
验证新旧核心和组合 | PKG-JASSUB-04, CORE-22, PKG-JASSUB-07 | 真实 worker/WASM 字幕渲染与全屏、旧核心测试 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-JASSUB-06 | artplayer-plugin-jassub
验证分发并同步文档 | PKG-JASSUB-05, ENG-07 | jassub.js、外部资源路径、许可和离线失败记录;接续 VENDOR-04/05 完整组件/字体通知、LGPL 源码分发核对及六份未明确字体的分发处置 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录;来源身份已由01冻结,完整通知和未明确字体的发布处理必须闭环,不能以来源相同替代许可结论 | H | todo | @@ -534,6 +534,7 @@ - PKG-MULTI-SUB-07: [记录](baselines/multiple-subtitles-timestamps.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md) - PKG-JASSUB-01: [记录](baselines/jassub-release.json) [记录](baselines/jassub-vendor.json) [记录](baselines/jassub-font-metadata.json) [记录](baselines/jassub-contract.md) [记录](baselines/jassub-contract-validation.json) [记录](changes/2026-09-13-PKG-JASSUB-01-baseline.md) [记录](baselines/jassub-provenance.json) [记录](baselines/jassub-provenance-validation.json) [记录](changes/2026-09-14-PKG-JASSUB-01-provenance.md) - PKG-JASSUB-02: [记录](changes/2026-09-14-PKG-JASSUB-02-failure-baseline.md) [记录](baselines/jassub-failures-validation.json) +- PKG-JASSUB-03: [记录](changes/2026-09-14-PKG-JASSUB-03-registration.md) [记录](baselines/jassub-registration-validation.json) - PKG-MASK-01: [记录](baselines/danmuku-mask-release.json) [记录](baselines/danmuku-mask-registry.json) [记录](baselines/danmuku-mask-contract.md) [记录](baselines/danmuku-mask-contract-validation.json) [记录](changes/2026-09-13-PKG-MASK-01-contract.md) - PKG-MASK-02: [记录](changes/2026-09-13-PKG-MASK-02-failures.md) [记录](baselines/danmuku-mask-failures-validation.json) - PKG-MASK-03: [记录](changes/2026-09-13-PKG-MASK-03-lifecycle.md) [记录](baselines/danmuku-mask-lifecycle-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 7962784aa..b726d6630 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,14 @@ # 进度与证据 +## PKG-JASSUB-03 自有注册/清理改造完成 + +入口与 registration 分离;自定义 canvas、直接实例销毁后的宿主清理、重入与 +注册异常回滚已修复。旧产物同断言 1 通过/7 失败,候选八项通过;联合 128 项、 +main/legacy 各 15 项和实际浏览器 24 项通过,三格式正常构建。 +[实现记录](changes/2026-09-14-PKG-JASSUB-03-registration.md)说明 onDemand=false 的 +自定义 canvas 验证边界;WebKit 默认逐帧和 vendor 其他问题仍在 07/05。04 接着 +完成自有 TS 和公开类型,vendor/资源/声明本次未变;每项单独本地提交。 + ## PKG-JASSUB-02 历史缺陷与原生渲染基线完成 联合 120 项通过,其中 63 项确认旧失败/已有保护;没有宣称修复。Chromium/Firefox diff --git a/refactor/risk-table.md b/refactor/risk-table.md index c6f44f5a1..075d339e5 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -243,7 +243,7 @@ | DANMUKU-SAMPLING-01 | resolved / 已复现 | Serial asynchronous placement stops native frame eligibility sampling and delays existing visible expiry | PKG-DANMUKU-12 | | MASK-SCHEDULING-01 | open / 已复现 | Native Mask startup can coincide with a missed narrow Danmuku timestamp window | PKG-MASK-05 | | JASSUB-DESTROY-01 | open / 已复现 | Repeated destruction or cross-parent setVideo can throw before JASSUB cleanup | PKG-JASSUB-02, PKG-JASSUB-03, PKG-JASSUB-07 | -| JASSUB-CANVAS-01 | open / 已复现 | Custom canvas registration throws after JASSUB Worker allocation | PKG-JASSUB-02, PKG-JASSUB-03 | +| JASSUB-CANVAS-01 | resolved / 已复现 | Custom canvas registration throws after JASSUB Worker allocation | PKG-JASSUB-02, PKG-JASSUB-03 | | JASSUB-CONSTRUCTION-01 | open / 已复现 | Worker construction failure leaves previously allocated JASSUB DOM and listeners | PKG-JASSUB-02, PKG-JASSUB-07 | | JASSUB-VIDEO-FRAME-01 | open / 已复现 | Old video frame callbacks can drive replacement subtitle time and duplicate chains | PKG-JASSUB-02, PKG-JASSUB-07 | | JASSUB-RATE-01 | open / 已复现 | Fallback ratechange sends an Event instead of numeric playbackRate | PKG-JASSUB-02, PKG-JASSUB-07 | diff --git a/refactor/risks.json b/refactor/risks.json index 1942c4a1a..f689d5c4f 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -5438,26 +5438,38 @@ ], "evidence": [ "refactor/changes/2026-09-14-PKG-JASSUB-02-failure-baseline.md", - "refactor/baselines/jassub-failures-validation.json" + "refactor/baselines/jassub-failures-validation.json", + "refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md", + "refactor/baselines/jassub-registration-validation.json", + "test/jassub-registration.test.js" ], - "compatibleResolution": "Use actual resource ownership and tolerate direct instance teardown before host teardown.", + "compatibleResolution": "Use actual resource ownership and tolerate direct instance teardown before host teardown. Task03 fixes host cleanup after direct teardown, reentry and retry/rollback. Direct vendor repetition and cross-parent cleanup remain task07 and this risk stays open.", "closureCriteria": "Candidate direct/repeated/host teardown and cross-parent video replacement release container, Worker and listeners without throwing or removing user nodes." }, { "id": "JASSUB-CANVAS-01", "title": "Custom canvas registration throws after JASSUB Worker allocation", "confirmation": "reproduced", - "status": "open", + "status": "resolved", "owners": [ "PKG-JASSUB-02", "PKG-JASSUB-03" ], "evidence": [ "refactor/changes/2026-09-14-PKG-JASSUB-02-failure-baseline.md", - "refactor/baselines/jassub-failures-validation.json" + "refactor/baselines/jassub-failures-validation.json", + "refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md", + "refactor/baselines/jassub-registration-validation.json", + "test/jassub-registration.test.js" ], "compatibleResolution": "Guard adapter-created parent styling; retain the supplied canvas and roll back failed registration.", - "closureCriteria": "Custom canvas registration succeeds and cleanup releases owned work while preserving caller DOM; public instance and option override behavior remain compatible." + "closureCriteria": "Custom canvas registration succeeds and cleanup releases owned work while preserving caller DOM; public instance and option override behavior remain compatible.", + "resolutionEvidence": [ + "refactor/changes/2026-09-14-PKG-JASSUB-03-registration.md", + "refactor/baselines/jassub-registration-validation.json", + "test/jassub-registration.test.js" + ], + "resolutionRationale": "Adapter no longer assumes a vendor-created parent for caller canvas. Old-artifact red tests and candidate rollback/ownership tests distinguish the fix. Actual main/legacy native Worker/WASM rendering, seek/fullscreen and direct-then-host cleanup pass with caller nodes preserved across three engines and three cores using explicit event-clock mode. Default WebKit frame delivery is a separate open risk." }, { "id": "JASSUB-CONSTRUCTION-01", diff --git a/refactor/tasks.json b/refactor/tasks.json index a824ab884..53f95b6e5 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2455,11 +2455,14 @@ "CORE-15", "CORE-16" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "仅整理自有 adapter/销毁;保留第三方文件及来源", "acceptance": "结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过", - "evidence": [] + "evidence": [ + "changes/2026-09-14-PKG-JASSUB-03-registration.md", + "baselines/jassub-registration-validation.json" + ] }, { "id": "PKG-JASSUB-04", diff --git a/test/browser/README.md b/test/browser/README.md index 90a0aaa5c..af39c5e1a 100644 --- a/test/browser/README.md +++ b/test/browser/README.md @@ -178,3 +178,10 @@ selected ARTPLAYER_JASSUB_ON_DEMAND=false diagnostic passes on the candidate cor This is a retained historical failure, not a waived browser gate. The server must serve WASM as application/wasm for native streaming compilation. Archive failed and successful runs independently. See the PKG-JASSUB-02 change record. + +For candidate registration regressions, set ARTPLAYER_JASSUB_ARTIFACT to a built +main/legacy global file. Those runs also destroy the exposed instance before host +cleanup. ARTPLAYER_JASSUB_CUSTOM_CANVAS=true supplies a caller-owned canvas and +asserts it remains connected after destroy(false). The native report records the +artifact hash and both mode choices. This does not change the default published +baseline or establish ESM/offscreen/device acceptance. diff --git a/test/browser/jassub-native.spec.js b/test/browser/jassub-native.spec.js index 570ed4481..69fd5b1eb 100644 --- a/test/browser/jassub-native.spec.js +++ b/test/browser/jassub-native.spec.js @@ -7,7 +7,9 @@ const baseline = JSON.parse(fs.readFileSync('refactor/baselines/jassub-release.j const release = baseline.release const archive = await ensureArchive(release) const member = `package/${release.manifest.main.replace(/^\.\//, '')}` -const code = readMember(archive, member).toString() +const artifact = process.env.ARTPLAYER_JASSUB_ARTIFACT +const customCanvas = process.env.ARTPLAYER_JASSUB_CUSTOM_CANVAS === 'true' +const code = artifact ? fs.readFileSync(artifact, 'utf8') : readMember(archive, member).toString() const onDemandRender = process.env.ARTPLAYER_JASSUB_ON_DEMAND !== 'false' const subtitles = `[Script Info] ScriptType: v4.00+ @@ -23,7 +25,7 @@ Dialogue: 0,0:00:05.00,0:00:30.00,Default,,0,0,0,,After seek with actual WASM ` for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { - test(`JASSUB published ${release.version} renders actual WASM subtitles through seek and layout with ${core} core`, async ({ page, context }, testInfo) => { + test(`JASSUB ${artifact ? 'candidate artifact' : `published ${release.version}`} renders actual WASM subtitles through seek and layout with ${core} core, customCanvas=${customCanvas}`, async ({ page, context }, testInfo) => { const resources = [] const external = [] const origin = new URL(testInfo.project.use.baseURL).origin @@ -68,8 +70,17 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { await page.locator('#play').click() await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1) await page.locator('#pause').click() - await page.evaluate(async ({ subContent, onDemandRender }) => { + await page.evaluate(async ({ subContent, onDemandRender, customCanvas }) => { const register = window.artplayerPluginJassub.default || window.artplayerPluginJassub + let canvas + if (customCanvas) { + canvas = document.createElement('canvas') + canvas.style.position = 'absolute' + canvas.style.pointerEvents = 'none' + canvas.style.zIndex = '20' + window.art.video.insertAdjacentElement('afterend', canvas) + window.jassubUserCanvas = canvas + } window.art.plugins.add(register({ subContent, workerUrl: '/assets/jassub/jassub-worker.js', @@ -79,6 +90,7 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { fonts: ['/assets/jassub/default.woff2'], offscreenRender: false, onDemandRender, + ...(canvas ? { canvas } : {}), })) window.jassub = window.art.plugins.artplayerPluginJassub.instance window.jassub.addEventListener('error', event => window.jassubNative.errors.push(String(event.error))) @@ -104,7 +116,7 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { window.jassubNative.snapshots.push(state) return state } - }, { subContent: subtitles, onDemandRender }) + }, { subContent: subtitles, onDemandRender, customCanvas }) await page.locator('#play').click() await expect.poll(() => page.evaluate(() => window.jassubPixels().visible)).toBeGreaterThan(100) const before = await page.evaluate(() => window.jassubPixels('before-seek')) @@ -128,7 +140,11 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { await expect.poll(() => page.evaluate(() => window.jassubPixels().visible)).toBeGreaterThan(100) await page.evaluate(() => window.jassubPixels('fullscreen')) await testInfo.attach('actual-jassub-page', { body: await page.screenshot(), contentType: 'image/png' }) + if (artifact) + await page.evaluate(() => window.jassub.destroy()) await page.evaluate(() => window.art.destroy(false)) + if (customCanvas) + expect(await page.evaluate(() => window.jassubUserCanvas.isConnected)).toBe(true) expect(await page.evaluate(() => document.querySelectorAll('.JASSUB').length)).toBe(0) expect(await page.evaluate(() => window.jassubNative.workers.map(worker => worker.terminated))).toEqual([1]) expect(await page.evaluate(() => window.jassubNative.errors)).toEqual([]) @@ -138,7 +154,7 @@ for (const core of ['candidate', 'published', 'published-5.3.1-beta.1']) { } finally { const state = await page.evaluate(() => window.jassubNative).catch(error => ({ unavailable: error.message })) - await testInfo.attach('actual-jassub-evidence', { body: JSON.stringify({ core, onDemandRender, release: { version: release.version, integrity: release.integrity, member, sha256: hash(code) }, subtitleSha256: hash(subtitles), resources, external, state, limitation: 'Actual published plugin, worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery, repeated destruction or device acceptance.' }, null, 2), contentType: 'application/json' }) + await testInfo.attach('actual-jassub-evidence', { body: JSON.stringify({ core, onDemandRender, customCanvas, source: artifact ? { artifact, sha256: hash(code) } : { version: release.version, integrity: release.integrity, member, sha256: hash(code) }, subtitleSha256: hash(subtitles), resources, external, state, limitation: 'Actual worker, WASM and local font with offscreenRender=false. No default offscreen path, failure recovery or device acceptance; candidate adds direct-then-host destruction.' }, null, 2), contentType: 'application/json' }) if (!page.isClosed()) { await page.evaluate(() => { if (window.art && !window.art.isDestroy) diff --git a/test/helpers/jassub.js b/test/helpers/jassub.js index 345f6bdb9..17de24ea9 100644 --- a/test/helpers/jassub.js +++ b/test/helpers/jassub.js @@ -141,6 +141,12 @@ export function jassubEnvironment(implementation, { script = false, simd = false listeners.set(name, []) listeners.get(name).push(callback) return art + }, off(name, callback) { + const callbacks = listeners.get(name) + const index = callbacks?.indexOf(callback) ?? -1 + if (index >= 0) + callbacks.splice(index, 1) + return art } } const emit = name => listeners.get(name)?.forEach(callback => callback()) const flush = async () => { @@ -154,5 +160,5 @@ export function jassubEnvironment(implementation, { script = false, simd = false workers.at(-1).onmessage({ data: { target: 'ready' } }) await flush() } - return { factory, exported, art, workers, parent, errors, listeners, emit, ready, flush, createVideo: () => parent.appendChild(new Video()) } + return { factory, exported, art, workers, parent, errors, listeners, emit, ready, flush, createVideo: () => parent.appendChild(new Video()), createCanvas: () => new Canvas() } } diff --git a/test/jassub-registration.test.js b/test/jassub-registration.test.js new file mode 100644 index 000000000..f5e7714f6 --- /dev/null +++ b/test/jassub-registration.test.js @@ -0,0 +1,121 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Candidate adapter regressions use the repository runner. +import test from 'node:test' +import { jassubCandidate, jassubEnvironment } from './helpers/jassub.js' + +const implementation = await jassubCandidate() + +test('JASSUB custom canvas registration keeps caller DOM while releasing its actual vendor instance', async () => { + const env = jassubEnvironment(implementation) + const canvas = env.parent.appendChild(env.createCanvas()) + const result = env.factory({ canvas })(env.art) + assert.equal(result.instance._canvas, canvas) + assert.equal(result.instance._canvasParent, undefined) + assert.equal(env.listeners.get('destroy').length, 1) + await env.ready() + assert.equal(env.workers[0].messages[0].target, 'init') + env.emit('destroy') + assert.equal(env.workers[0].terminated, 1) + assert.equal(canvas.parentNode, env.parent) + assert.equal(env.parent.children.length, 2) +}) + +test('JASSUB host cleanup tolerates an already destroyed exposed instance', async () => { + const env = jassubEnvironment(implementation) + const { instance } = env.factory()(env.art) + await env.ready() + instance.destroy() + assert.doesNotThrow(() => env.emit('destroy')) + assert.doesNotThrow(() => env.emit('destroy')) + assert.equal(env.workers[0].terminated, 1) + assert.equal(env.parent.children.length, 1) +}) + +test('JASSUB host cleanup before ready terminates once and retains the vendor destroyed-frame guard', async () => { + const env = jassubEnvironment(implementation) + const { instance } = env.factory()(env.art) + env.emit('destroy') + env.emit('destroy') + await env.flush() + assert.equal(instance._destroyed, true) + assert.equal(env.workers[0].terminated, 1) + assert.equal(env.parent.children.length, 1) +}) + +test('JASSUB host cleanup guards synchronous reentry without replacing the exposed destroy method', async () => { + const env = jassubEnvironment(implementation) + const { instance } = env.factory()(env.art) + await env.ready() + const original = instance.destroy + let calls = 0 + const custom = function () { + calls++ + env.emit('destroy') + return original.call(this) + } + instance.destroy = custom + env.emit('destroy') + assert.equal(calls, 1) + assert.equal(instance.destroy, custom) + assert.equal(env.workers[0].terminated, 1) +}) + +test('JASSUB failing host disposal preserves its error and allows another cleanup attempt', async () => { + const env = jassubEnvironment(implementation) + const { instance } = env.factory()(env.art) + await env.ready() + const original = instance.destroy + const failure = new Error('consumer cleanup failure') + instance.destroy = () => { + throw failure + } + assert.throws(() => env.emit('destroy'), error => error === failure) + assert.equal(env.workers[0].terminated, 0) + instance.destroy = original + env.emit('destroy') + assert.equal(env.workers[0].terminated, 1) +}) + +for (const offThrows of [false, true]) { + test(`JASSUB registration failure rolls back its instance and retains the original error when offThrows=${offThrows}`, async () => { + const env = jassubEnvironment(implementation) + const unrelated = () => {} + env.art.on('destroy', unrelated) + const on = env.art.on + const failure = new Error('host listener registration failed') + env.art.on = (name, listener) => { + on(name, listener) + throw failure + } + if (offThrows) + env.art.off = () => { throw new Error('secondary rollback failure') } + assert.throws(() => env.factory()(env.art), error => error === failure) + await env.flush() + assert.equal(env.workers[0].terminated, 1) + assert.equal(env.parent.children.length, 1) + if (!offThrows) + assert.deepEqual(env.listeners.get('destroy'), [unrelated]) + assert.doesNotThrow(() => env.emit('destroy')) + assert.equal(env.workers[0].terminated, 1) + }) +} + +test('JASSUB style setup failure releases the created instance before returning the original exception', async () => { + const env = jassubEnvironment(implementation) + const insert = env.art.video.insertAdjacentElement + const failure = new Error('style setter failed') + env.art.video.insertAdjacentElement = function (position, child) { + insert.call(this, position, child) + Object.defineProperty(child.style, 'zIndex', { + get() { return undefined }, + set() { + throw failure + }, + }) + } + assert.throws(() => env.factory()(env.art), error => error === failure) + await env.flush() + assert.equal(env.workers[0].terminated, 1) + assert.equal(env.parent.children.length, 1) + assert.equal(env.listeners.get('destroy'), undefined) +})