From 1c522f181be42f1c3fe5ce309cd42d85e58ac4dc Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Tue, 15 Sep 2026 15:30:26 +0800 Subject: [PATCH] fix(thumbnail): [PKG-TOOL-THUMB-07] preserve falsy cleanup exceptions --- docs/compiled/artplayer-tool-thumbnail.js | 2 +- .../artplayer-tool-thumbnail.legacy.js | 2 +- docs/compiled/artplayer-tool-thumbnail.mjs | 8 +- package.json | 4 +- .../artplayer-tool-thumbnail/ARCHITECTURE.md | 10 + .../dist/artplayer-tool-thumbnail.js | 2 +- .../dist/artplayer-tool-thumbnail.legacy.js | 2 +- .../dist/artplayer-tool-thumbnail.mjs | 8 +- .../artplayer-tool-thumbnail/src/lifecycle.ts | 8 +- .../thumbnail-cleanup-errors-validation.json | 5540 +++++++++++++++++ ...-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md | 60 + refactor/plan.md | 10 +- refactor/progress.md | 14 + refactor/risk-table.md | 1 + refactor/risks.json | 24 + refactor/tasks.json | 22 +- test/browser/thumbnail-emitter.spec.js | 40 +- test/thumbnail-cleanup-errors.test.js | 85 + 18 files changed, 5822 insertions(+), 20 deletions(-) create mode 100644 refactor/baselines/thumbnail-cleanup-errors-validation.json create mode 100644 refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md create mode 100644 test/thumbnail-cleanup-errors.test.js diff --git a/docs/compiled/artplayer-tool-thumbnail.js b/docs/compiled/artplayer-tool-thumbnail.js index e2b100bfb..57c5a59b5 100644 --- a/docs/compiled/artplayer-tool-thumbnail.js +++ b/docs/compiled/artplayer-tool-thumbnail.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); diff --git a/docs/compiled/artplayer-tool-thumbnail.legacy.js b/docs/compiled/artplayer-tool-thumbnail.legacy.js index 8a27da060..5907d6760 100644 --- a/docs/compiled/artplayer-tool-thumbnail.legacy.js +++ b/docs/compiled/artplayer-tool-thumbnail.legacy.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); diff --git a/docs/compiled/artplayer-tool-thumbnail.mjs b/docs/compiled/artplayer-tool-thumbnail.mjs index 5adc54695..bd28deac1 100644 --- a/docs/compiled/artplayer-tool-thumbnail.mjs +++ b/docs/compiled/artplayer-tool-thumbnail.mjs @@ -115,14 +115,18 @@ function revoke(urls, url) { } function cleanupAll(callbacks) { let failure; + let failed = false; for (const callback of callbacks) { try { callback(); } catch (error) { - failure || (failure = error); + if (!failed) { + failed = true; + failure = error; + } } } - if (failure) + if (failed) throw failure; } function closeState(tool, reason = "destroyed") { diff --git a/package.json b/package.json index a99de83f7..2836b847b 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:llm && yarn build:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && 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/jassub-registration.test.js test/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.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-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.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-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/vast-compatibility.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/asr-media-error.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/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.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-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.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-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/vast-compatibility.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/asr-media-error.test.js test/thumbnail-cleanup-errors.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", @@ -85,7 +85,7 @@ "generate:media:hls": "node scripts/generate-hls-fixture.mjs", "test:iframe": "node --test test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe.test.js test/iframe-lifecycle.test.js refactor/scripts/iframe-contract.test.mjs refactor/scripts/iframe-types.test.mjs", "test:iframe-types-package": "node refactor/scripts/iframe-package-types.mjs", - "test:thumbnail": "node --test 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 refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs", + "test:thumbnail": "node --test 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 refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs test/thumbnail-cleanup-errors.test.js", "test:thumbnail-types-package": "node refactor/scripts/thumbnail-package-types.mjs", "test:auto-thumbnail": "node --test test/auto-thumbnail-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs refactor/scripts/auto-thumbnail-types.test.mjs", "test:vtt-thumbnail": "node --test test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs refactor/scripts/vtt-thumbnail-types.test.mjs", diff --git a/packages/artplayer-tool-thumbnail/ARCHITECTURE.md b/packages/artplayer-tool-thumbnail/ARCHITECTURE.md index 5c3fc8bd9..622e9dfa6 100644 --- a/packages/artplayer-tool-thumbnail/ARCHITECTURE.md +++ b/packages/artplayer-tool-thumbnail/ARCHITECTURE.md @@ -50,6 +50,16 @@ were overwritten. Current public URLs are also revoked, preserving historical destroy behavior. The video is paused, its src removed, load resets its decoder state, and its node is removed. Registration checks for reentrant closure/replacement. +Cleanup records whether an exception occurred separately from its value. It attempts +all remaining cleanup callbacks and then rethrows the first value unchanged, including +undefined, null, false, zero, an empty string or NaN. In particular, a destroy listener +must not have its exception swallowed just because it is falsy. The instance remains +closed after a failed destroy; a repeated destroy is a no-op. This is distinct from +emitter dispatch: an exception still stops the remaining listeners in that dispatch. +`test/thumbnail-cleanup-errors.test.js` compares recovered 3.5.31 and frozen workspace +behavior, checks first-error identity across nested cleanup and verifies resource release. +`test/browser/thumbnail-emitter.spec.js` repeats the listener contract with real DOM. + ## Extraction and event ordering Loading emits file before assigning video.src, then video synchronously as in diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js index e2b100bfb..57c5a59b5 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js index 8a27da060..5907d6760 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs index 5adc54695..bd28deac1 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs @@ -115,14 +115,18 @@ function revoke(urls, url) { } function cleanupAll(callbacks) { let failure; + let failed = false; for (const callback of callbacks) { try { callback(); } catch (error) { - failure || (failure = error); + if (!failed) { + failed = true; + failure = error; + } } } - if (failure) + if (failed) throw failure; } function closeState(tool, reason = "destroyed") { diff --git a/packages/artplayer-tool-thumbnail/src/lifecycle.ts b/packages/artplayer-tool-thumbnail/src/lifecycle.ts index a3d39e2da..fcdbb339b 100644 --- a/packages/artplayer-tool-thumbnail/src/lifecycle.ts +++ b/packages/artplayer-tool-thumbnail/src/lifecycle.ts @@ -37,15 +37,19 @@ export function revoke(urls: Set, url: string | undefined) { export function cleanupAll(callbacks: Cleanup[]) { let failure: unknown + let failed = false for (const callback of callbacks) { try { callback() } catch (error) { - failure ||= error + if (!failed) { + failed = true + failure = error + } } } - if (failure) + if (failed) throw failure } diff --git a/refactor/baselines/thumbnail-cleanup-errors-validation.json b/refactor/baselines/thumbnail-cleanup-errors-validation.json new file mode 100644 index 000000000..1c63ba669 --- /dev/null +++ b/refactor/baselines/thumbnail-cleanup-errors-validation.json @@ -0,0 +1,5540 @@ +{ + "schemaVersion": 1, + "task": "PKG-TOOL-THUMB-07", + "sourceCommit": "2a380679117b02499ff3a131cb449e1eda007cb2", + "capturedAt": "2026-09-15T07:27:46.314Z", + "status": "verified-fix", + "sourceHashesLF": { + "packages/artplayer-tool-thumbnail/src/lifecycle.ts": "54d6a2a78906c79ebdba4a12441138cb95f55b85c09c4a0213f12f5a4afa89b7", + "test/thumbnail-cleanup-errors.test.js": "a2f3832bdd97264722d098f9fba46e7c68836cbebe5a431eef0c171e02ef2fd3", + "test/browser/thumbnail-emitter.spec.js": "c0297b83de9754626558414bbca6f9a26c3e6db70e09ae2bd19dfa6e1bb26038", + "package.json": "ba08888543849e4436f74e9dd6283d8c85a171bf5ef082522ae67e323a4c72bc" + }, + "artifacts": { + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js": { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "bytes": 12538 + }, + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js": { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "bytes": 12887 + }, + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs": { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs", + "sha256": "9596144702d869797ded8306fc810abb3a81d4325d04917a176b2242ecc214c3", + "bytes": 22456 + } + }, + "before": { + "file": "refactor/.cache/thumbnail-cleanup-before.log", + "sha256": "d4e4da702b1ece4ece5e472e13f27c3aa5e3f2a5438df09ccec86b7166600efc", + "bytes": 2485, + "passed": 1, + "failed": 2, + "scope": "Frozen recovered 3.5.31 and workspace main/legacy propagate all seven falsy values; candidate loses the first undefined value and replaces it during nested cleanup." + }, + "checks": { + "after": { + "file": "refactor/.cache/thumbnail-cleanup-after.log", + "sha256": "d669f8958e5edbe4bb02db5823481557ffacb23c89b1a726b3e7d703de77dd71", + "bytes": 450 + }, + "unit": { + "file": "refactor/.cache/thumbnail-cleanup-unit.log", + "sha256": "cb97b6b6ba5358ccba664093cb5c0c87f97305e875d5516275755e6908553e80", + "bytes": 13157 + }, + "main-unit": { + "file": "refactor/.cache/thumbnail-cleanup-main-unit.log", + "sha256": "067a9b5d76665e276f83d079a35c8c6fa34755e684858e16d583abece26a1346", + "bytes": 4958 + }, + "legacy-unit": { + "file": "refactor/.cache/thumbnail-cleanup-legacy-unit.log", + "sha256": "e3bc17dbe00bcf05e41b71ccb0ebeb27f124870a5ba6ea78364f417d1395cbd6", + "bytes": 4956 + }, + "types": { + "file": "refactor/.cache/thumbnail-cleanup-types.log", + "sha256": "ede021b9e093303e43ce45472baedba3b47343f74c950eefb3eef8a6eceaead2", + "bytes": 443 + }, + "lint-final": { + "file": "refactor/.cache/thumbnail-cleanup-lint-final.log", + "sha256": "a3fec11fe5839806e7a03bd9e46192a4c19ac1b068a9688dc05e5df0ec5bc986", + "bytes": 536 + }, + "build": { + "file": "refactor/.cache/thumbnail-cleanup-build.log", + "sha256": "24669680afd91a6aa05c013bb4990c7fdcaf7e57acbff9e487a3f7e8dad3ecf9", + "bytes": 1455 + }, + "package": { + "file": "refactor/.cache/thumbnail-cleanup-package.log", + "sha256": "6c5c5f0cf166b6ae4443d640b116e02bac814b093cdc587953c64d73a98cd606", + "bytes": 621 + }, + "toolchain": { + "file": "refactor/.cache/thumbnail-cleanup-toolchain.log", + "sha256": "ffcff2e97d2dd4e9c74be2f67f7ce619d70cc5f331b546874ad18ef2f562846f", + "bytes": 510 + } + }, + "browser": [ + { + "name": "source", + "report": { + "file": "refactor/.cache/thumbnail-cleanup-source.json", + "sha256": "1907d171a56ef8f3413916520087938808ad2232bdfa2b8ff7169a334fd9dbeb", + "bytes": 230866 + }, + "stats": { + "startTime": "2026-09-15T07:22:31.103Z", + "duration": 15738.065999999999, + "expected": 15, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "thumbnail-emitter.spec.js / Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 195, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 147, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 142, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 146, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 138, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1478, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 382, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 380, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 403, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 352, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 660, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 468, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 423, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 453, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 420, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-source", + "sha256": "493b844576165662d05e139479b0c4c0df0a45113b87d60399278b4545573cd3", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + } + ] + }, + { + "name": "main", + "report": { + "file": "refactor/.cache/thumbnail-cleanup-main.json", + "sha256": "deddb0f633a7a7a76d7fc315b1c3c2f6d24b775e7ab86e7c6125897b5e9bd30f", + "bytes": 349734 + }, + "stats": { + "startTime": "2026-09-15T07:25:03.263Z", + "duration": 25858.714, + "expected": 21, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 185, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 150, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1214, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 460, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 487, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 436, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 508, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "value": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "destroy", + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/c9b8eb18-b79f-4f9b-a599-248c0db6f804", + "second": "blob:http://127.0.0.1:8084/1caa352e-b395-44c7-9a25-cf6e713c9aff", + "created": [ + "blob:http://127.0.0.1:8084/128938fe-14c8-41ef-b2f3-c64560464c15", + "blob:http://127.0.0.1:8084/f4c5befb-d3f6-48e7-9bda-30c5d1634c67", + "blob:http://127.0.0.1:8084/754e4af2-cc57-487e-a890-57bba7d3f468", + "blob:http://127.0.0.1:8084/692118e5-f3c4-40c9-a521-4f22b55c97f3", + "blob:http://127.0.0.1:8084/a7178ca6-5f69-44c9-8ad1-8429f0b93db5", + "blob:http://127.0.0.1:8084/fe936ac4-c972-4921-96ea-e600caf660fa", + "blob:http://127.0.0.1:8084/3e72940b-c69a-41fa-9092-c14f334c3934", + "blob:http://127.0.0.1:8084/1557b660-09fa-4aa9-aa5d-3ea613f670b1", + "blob:http://127.0.0.1:8084/c8f3b927-a862-4036-8016-2e58a1721e22", + "blob:http://127.0.0.1:8084/be2ad5fa-f1d2-4e94-b57c-9a9813b626eb", + "blob:http://127.0.0.1:8084/c9b8eb18-b79f-4f9b-a599-248c0db6f804", + "blob:http://127.0.0.1:8084/0f2aed35-bbd1-4cb0-a10f-dffc9da5c5e3", + "blob:http://127.0.0.1:8084/f7c7350f-7789-41d0-88e5-4698b53cc5af", + "blob:http://127.0.0.1:8084/af8ca5e6-27d9-4b13-9b8a-548da767eff2", + "blob:http://127.0.0.1:8084/b7445412-7447-4211-81da-4a7b2220a154", + "blob:http://127.0.0.1:8084/9dc975cf-ae4f-4230-85ad-d05d7e023304", + "blob:http://127.0.0.1:8084/bf2f9009-d37b-410e-ad9e-ff69dfb3a1eb", + "blob:http://127.0.0.1:8084/07510937-0983-4417-8987-5d403c43fca5", + "blob:http://127.0.0.1:8084/75338aa1-2630-4b73-b396-4ab86e2302bc", + "blob:http://127.0.0.1:8084/be050bd2-712b-45a7-aa39-8411d68947c9", + "blob:http://127.0.0.1:8084/1caa352e-b395-44c7-9a25-cf6e713c9aff" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f4c5befb-d3f6-48e7-9bda-30c5d1634c67", + "blob:http://127.0.0.1:8084/754e4af2-cc57-487e-a890-57bba7d3f468", + "blob:http://127.0.0.1:8084/692118e5-f3c4-40c9-a521-4f22b55c97f3", + "blob:http://127.0.0.1:8084/a7178ca6-5f69-44c9-8ad1-8429f0b93db5", + "blob:http://127.0.0.1:8084/fe936ac4-c972-4921-96ea-e600caf660fa", + "blob:http://127.0.0.1:8084/3e72940b-c69a-41fa-9092-c14f334c3934", + "blob:http://127.0.0.1:8084/1557b660-09fa-4aa9-aa5d-3ea613f670b1", + "blob:http://127.0.0.1:8084/c8f3b927-a862-4036-8016-2e58a1721e22", + "blob:http://127.0.0.1:8084/be2ad5fa-f1d2-4e94-b57c-9a9813b626eb", + "blob:http://127.0.0.1:8084/c9b8eb18-b79f-4f9b-a599-248c0db6f804", + "blob:http://127.0.0.1:8084/0f2aed35-bbd1-4cb0-a10f-dffc9da5c5e3", + "blob:http://127.0.0.1:8084/f7c7350f-7789-41d0-88e5-4698b53cc5af", + "blob:http://127.0.0.1:8084/af8ca5e6-27d9-4b13-9b8a-548da767eff2", + "blob:http://127.0.0.1:8084/b7445412-7447-4211-81da-4a7b2220a154", + "blob:http://127.0.0.1:8084/9dc975cf-ae4f-4230-85ad-d05d7e023304", + "blob:http://127.0.0.1:8084/bf2f9009-d37b-410e-ad9e-ff69dfb3a1eb", + "blob:http://127.0.0.1:8084/07510937-0983-4417-8987-5d403c43fca5", + "blob:http://127.0.0.1:8084/75338aa1-2630-4b73-b396-4ab86e2302bc", + "blob:http://127.0.0.1:8084/be050bd2-712b-45a7-aa39-8411d68947c9", + "blob:http://127.0.0.1:8084/128938fe-14c8-41ef-b2f3-c64560464c15", + "blob:http://127.0.0.1:8084/1caa352e-b395-44c7-9a25-cf6e713c9aff" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4cf9bdbe-e2f6-42a9-a453-5cb568ceaedf", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/128938fe-14c8-41ef-b2f3-c64560464c15", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 192, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/fd570673-aec9-42d9-9174-4aa5adf44c2f", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/b351ba88-1d17-4fcc-924e-efc509cb9302", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 219, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "value": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/37aabe14-5ed3-41a0-be47-8491adddbded" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/37aabe14-5ed3-41a0-be47-8491adddbded", + "blob:http://127.0.0.1:8084/65dd8b38-8187-4222-8664-38d37767ef77" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/37aabe14-5ed3-41a0-be47-8491adddbded", + "blob:http://127.0.0.1:8084/65dd8b38-8187-4222-8664-38d37767ef77" + ] + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/39fbc786-f662-44d0-8878-6ea9bacfc14a", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/65dd8b38-8187-4222-8664-38d37767ef77", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 347, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "value": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/d3d0ec5b-ec8b-47c8-8ab6-b9330bb531d5" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d3d0ec5b-ec8b-47c8-8ab6-b9330bb531d5" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4c477a02-e836-4d34-897b-2e5005b66d29", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/d3d0ec5b-ec8b-47c8-8ab6-b9330bb531d5", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 462, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "value": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "updateSources": [ + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1" + ], + "created": [ + "blob:http://127.0.0.1:8084/ec7755a9-a905-40d6-8db5-2836697512f9", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/7973b24e-b608-4084-ae83-6bd264b8f88a", + "blob:http://127.0.0.1:8084/10904818-8c42-4298-9acc-a21a03c2fc2a", + "blob:http://127.0.0.1:8084/56fc7d3c-7352-4ba6-bee7-176dbcca55f8", + "blob:http://127.0.0.1:8084/bf682cbe-409a-4428-93db-73da6cc0671f", + "blob:http://127.0.0.1:8084/e2d06095-0317-4129-aed6-4c40dfa49830", + "blob:http://127.0.0.1:8084/55b6716e-437a-4d6e-847f-ad3aad290d93", + "blob:http://127.0.0.1:8084/4df3b362-caf2-46dc-9b5a-62be09228b6f", + "blob:http://127.0.0.1:8084/56ddf64d-5949-4641-b20c-012483e38612", + "blob:http://127.0.0.1:8084/39b0aa31-7959-4477-b4c4-457beaf40acc", + "blob:http://127.0.0.1:8084/a8ce1f2c-2240-49a1-a2cc-bc9b71cd9b6e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ec7755a9-a905-40d6-8db5-2836697512f9", + "blob:http://127.0.0.1:8084/7973b24e-b608-4084-ae83-6bd264b8f88a", + "blob:http://127.0.0.1:8084/10904818-8c42-4298-9acc-a21a03c2fc2a", + "blob:http://127.0.0.1:8084/56fc7d3c-7352-4ba6-bee7-176dbcca55f8", + "blob:http://127.0.0.1:8084/bf682cbe-409a-4428-93db-73da6cc0671f", + "blob:http://127.0.0.1:8084/e2d06095-0317-4129-aed6-4c40dfa49830", + "blob:http://127.0.0.1:8084/55b6716e-437a-4d6e-847f-ad3aad290d93", + "blob:http://127.0.0.1:8084/4df3b362-caf2-46dc-9b5a-62be09228b6f", + "blob:http://127.0.0.1:8084/56ddf64d-5949-4641-b20c-012483e38612", + "blob:http://127.0.0.1:8084/39b0aa31-7959-4477-b4c4-457beaf40acc", + "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "blob:http://127.0.0.1:8084/a8ce1f2c-2240-49a1-a2cc-bc9b71cd9b6e" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/0371c2f1-7a12-4f32-961b-5b573f40f7fd", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/ec7755a9-a905-40d6-8db5-2836697512f9", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/2e5c97bd-c3dd-4d15-a9ed-e84223e796a1", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1265, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "value": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38563, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "destroy", + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/a357dafa-94e2-48e6-8186-8fbf2e554de5", + "second": "blob:http://127.0.0.1:8084/d4c993c3-1d01-48cb-949e-469ebc823482", + "created": [ + "blob:http://127.0.0.1:8084/56fd68a5-08fb-46f5-8762-4f3dd9ff70d8", + "blob:http://127.0.0.1:8084/3a63b53a-a74f-4096-a535-60bb41a71b13", + "blob:http://127.0.0.1:8084/724de285-9303-4f70-848f-18eabf35b0a1", + "blob:http://127.0.0.1:8084/430d6302-be3b-42d2-8636-a0b2f685e252", + "blob:http://127.0.0.1:8084/ece1a7b3-b57f-449a-a900-a1c638fa27ef", + "blob:http://127.0.0.1:8084/419e7051-a8d6-45ee-90d1-f34fb6100a3b", + "blob:http://127.0.0.1:8084/969bff12-46b5-44ba-b496-40a6a63e0738", + "blob:http://127.0.0.1:8084/5df66699-4c22-4131-a6f0-0a434a3e61fd", + "blob:http://127.0.0.1:8084/35839dcc-7ecf-4b6b-b2b8-f6b488f19001", + "blob:http://127.0.0.1:8084/a832fd97-d1b6-4ee3-805b-862ba60ce789", + "blob:http://127.0.0.1:8084/a357dafa-94e2-48e6-8186-8fbf2e554de5", + "blob:http://127.0.0.1:8084/e38d3a67-3ddf-4de0-96e3-cdb12cf87371", + "blob:http://127.0.0.1:8084/5e72d079-c8ed-4788-8191-1a9695d6241e", + "blob:http://127.0.0.1:8084/d8120f90-6780-4d1f-b33e-4b542385d122", + "blob:http://127.0.0.1:8084/50974f28-c2ec-42be-ac33-574705f7e261", + "blob:http://127.0.0.1:8084/15cb2aa5-e20e-4cc3-aa16-9ce50332b602", + "blob:http://127.0.0.1:8084/6d90552c-66b9-470f-b0a7-cc510a07c773", + "blob:http://127.0.0.1:8084/c7c5f4d0-619d-4b1a-b5aa-bb2dd1393146", + "blob:http://127.0.0.1:8084/eabe22c5-e30f-46f6-9baf-e84cc1893416", + "blob:http://127.0.0.1:8084/ecb4aa9f-56ac-4e83-8596-7e91c4e4eacf", + "blob:http://127.0.0.1:8084/d4c993c3-1d01-48cb-949e-469ebc823482" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3a63b53a-a74f-4096-a535-60bb41a71b13", + "blob:http://127.0.0.1:8084/724de285-9303-4f70-848f-18eabf35b0a1", + "blob:http://127.0.0.1:8084/430d6302-be3b-42d2-8636-a0b2f685e252", + "blob:http://127.0.0.1:8084/ece1a7b3-b57f-449a-a900-a1c638fa27ef", + "blob:http://127.0.0.1:8084/419e7051-a8d6-45ee-90d1-f34fb6100a3b", + "blob:http://127.0.0.1:8084/969bff12-46b5-44ba-b496-40a6a63e0738", + "blob:http://127.0.0.1:8084/5df66699-4c22-4131-a6f0-0a434a3e61fd", + "blob:http://127.0.0.1:8084/35839dcc-7ecf-4b6b-b2b8-f6b488f19001", + "blob:http://127.0.0.1:8084/a832fd97-d1b6-4ee3-805b-862ba60ce789", + "blob:http://127.0.0.1:8084/a357dafa-94e2-48e6-8186-8fbf2e554de5", + "blob:http://127.0.0.1:8084/e38d3a67-3ddf-4de0-96e3-cdb12cf87371", + "blob:http://127.0.0.1:8084/5e72d079-c8ed-4788-8191-1a9695d6241e", + "blob:http://127.0.0.1:8084/d8120f90-6780-4d1f-b33e-4b542385d122", + "blob:http://127.0.0.1:8084/50974f28-c2ec-42be-ac33-574705f7e261", + "blob:http://127.0.0.1:8084/15cb2aa5-e20e-4cc3-aa16-9ce50332b602", + "blob:http://127.0.0.1:8084/6d90552c-66b9-470f-b0a7-cc510a07c773", + "blob:http://127.0.0.1:8084/c7c5f4d0-619d-4b1a-b5aa-bb2dd1393146", + "blob:http://127.0.0.1:8084/eabe22c5-e30f-46f6-9baf-e84cc1893416", + "blob:http://127.0.0.1:8084/ecb4aa9f-56ac-4e83-8596-7e91c4e4eacf", + "blob:http://127.0.0.1:8084/56fd68a5-08fb-46f5-8762-4f3dd9ff70d8", + "blob:http://127.0.0.1:8084/d4c993c3-1d01-48cb-949e-469ebc823482" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 410, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 583, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "value": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/2322aa3e-db55-4748-b9af-28286e3b3ce1" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/2322aa3e-db55-4748-b9af-28286e3b3ce1", + "blob:http://127.0.0.1:8084/a948d7aa-598a-4654-bc9e-d25a9aa220b3" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2322aa3e-db55-4748-b9af-28286e3b3ce1", + "blob:http://127.0.0.1:8084/a948d7aa-598a-4654-bc9e-d25a9aa220b3" + ] + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 528, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "value": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/54490e08-f526-4c2c-b759-6f4aa41961e0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/54490e08-f526-4c2c-b759-6f4aa41961e0" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1204, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "value": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "updateSources": [ + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b" + ], + "created": [ + "blob:http://127.0.0.1:8084/876e32ee-dcdc-4776-a65e-fe4a45ba0a96", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/1de43dde-d921-49c2-bba5-78bd444d75cc", + "blob:http://127.0.0.1:8084/55777bdb-c5d7-4637-94ed-66306edb8150", + "blob:http://127.0.0.1:8084/3e6fb06d-2c85-4c79-893e-0a241089bb3b", + "blob:http://127.0.0.1:8084/7a18a4f3-73be-4ebc-8c05-30c58fb9a90b", + "blob:http://127.0.0.1:8084/441a89cc-31fd-45fc-a6d7-5bb12ec0065d", + "blob:http://127.0.0.1:8084/55aa3e7c-1669-4175-82b7-122a41c795c9", + "blob:http://127.0.0.1:8084/3fbc5d09-a8fe-4750-b50b-2c9000acaa36", + "blob:http://127.0.0.1:8084/087c0572-ef07-4b5f-9812-f04e501a47ec", + "blob:http://127.0.0.1:8084/500f5e08-2103-42fe-b113-febdc165b18e", + "blob:http://127.0.0.1:8084/be037fed-0455-442d-84ab-6e5e3ffd3d41" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/876e32ee-dcdc-4776-a65e-fe4a45ba0a96", + "blob:http://127.0.0.1:8084/1de43dde-d921-49c2-bba5-78bd444d75cc", + "blob:http://127.0.0.1:8084/55777bdb-c5d7-4637-94ed-66306edb8150", + "blob:http://127.0.0.1:8084/3e6fb06d-2c85-4c79-893e-0a241089bb3b", + "blob:http://127.0.0.1:8084/7a18a4f3-73be-4ebc-8c05-30c58fb9a90b", + "blob:http://127.0.0.1:8084/441a89cc-31fd-45fc-a6d7-5bb12ec0065d", + "blob:http://127.0.0.1:8084/55aa3e7c-1669-4175-82b7-122a41c795c9", + "blob:http://127.0.0.1:8084/3fbc5d09-a8fe-4750-b50b-2c9000acaa36", + "blob:http://127.0.0.1:8084/087c0572-ef07-4b5f-9812-f04e501a47ec", + "blob:http://127.0.0.1:8084/500f5e08-2103-42fe-b113-febdc165b18e", + "blob:http://127.0.0.1:8084/083cb017-5213-4ad6-8bd4-1f8e56be2d4b", + "blob:http://127.0.0.1:8084/be037fed-0455-442d-84ab-6e5e3ffd3d41" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1138, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1204, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1096, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1082, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1081, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + } + ] + }, + { + "name": "legacy", + "report": { + "file": "refactor/.cache/thumbnail-cleanup-legacy.json", + "sha256": "57433adf96d8f70bcd6716c9da262a9d088943049847339d3b4edbb18b13e71b", + "bytes": 349742 + }, + "stats": { + "startTime": "2026-09-15T07:25:41.861Z", + "duration": 26217.498, + "expected": 21, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 209, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 147, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1307, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 433, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail candidate-artifact: destroy preserves falsy listener exceptions with real DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 701, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-cleanup-errors", + "value": { + "implementation": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": [ + { + "index": 0, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 1, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 2, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 3, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 4, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 5, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + }, + { + "index": 6, + "threw": true, + "same": true, + "notified": 1, + "videoConnected": false + } + ] + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 435, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-emitter", + "value": { + "candidate": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=candidate&chapter=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 489, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "value": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "destroy", + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/2853ee92-e623-4cb5-8339-082624f1540b", + "second": "blob:http://127.0.0.1:8084/8c4a2000-9e30-427a-b88e-3a2cf991e43c", + "created": [ + "blob:http://127.0.0.1:8084/d5ae440b-503c-4678-8aea-5faa53bb6324", + "blob:http://127.0.0.1:8084/a3074c70-aa83-4f1b-99fb-f534f849066e", + "blob:http://127.0.0.1:8084/4e2bfb71-15bc-4503-b516-5823f28e1c12", + "blob:http://127.0.0.1:8084/b0423492-e7c8-43da-8b59-5e9fbb866cf3", + "blob:http://127.0.0.1:8084/51162db5-b6dd-4b00-846b-6a7fdb403d57", + "blob:http://127.0.0.1:8084/4e5b887a-ae0b-4d5e-8663-bd05c682faa5", + "blob:http://127.0.0.1:8084/93ccc81a-80d8-4a58-b14b-2d48a6048410", + "blob:http://127.0.0.1:8084/d354c92c-3c8d-45c2-866d-db10b9081133", + "blob:http://127.0.0.1:8084/ba51afd6-3ad6-4673-9649-972eefa4e9df", + "blob:http://127.0.0.1:8084/58696174-e615-43fc-8dc3-86f892758948", + "blob:http://127.0.0.1:8084/2853ee92-e623-4cb5-8339-082624f1540b", + "blob:http://127.0.0.1:8084/5749d30e-1005-44c5-8f51-ecd0a4083517", + "blob:http://127.0.0.1:8084/87e95025-422d-4199-acbd-c91abe43a73a", + "blob:http://127.0.0.1:8084/4292abb9-9992-499a-95d9-ea127c42e6a0", + "blob:http://127.0.0.1:8084/3fce1b3a-62af-4a8e-9cd0-519910dcabb6", + "blob:http://127.0.0.1:8084/d9dd3d9b-f2ca-4be2-a967-d58091b021e3", + "blob:http://127.0.0.1:8084/82d5f435-c51c-4d83-b34b-2fc064ac5713", + "blob:http://127.0.0.1:8084/1e0a80ca-860e-4afd-b0c1-de95c3b029da", + "blob:http://127.0.0.1:8084/fa77d7b9-1ac1-4dd7-9096-3f1347f37387", + "blob:http://127.0.0.1:8084/c427dc6f-f2ee-4de9-9989-43ca25c9d382", + "blob:http://127.0.0.1:8084/8c4a2000-9e30-427a-b88e-3a2cf991e43c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a3074c70-aa83-4f1b-99fb-f534f849066e", + "blob:http://127.0.0.1:8084/4e2bfb71-15bc-4503-b516-5823f28e1c12", + "blob:http://127.0.0.1:8084/b0423492-e7c8-43da-8b59-5e9fbb866cf3", + "blob:http://127.0.0.1:8084/51162db5-b6dd-4b00-846b-6a7fdb403d57", + "blob:http://127.0.0.1:8084/4e5b887a-ae0b-4d5e-8663-bd05c682faa5", + "blob:http://127.0.0.1:8084/93ccc81a-80d8-4a58-b14b-2d48a6048410", + "blob:http://127.0.0.1:8084/d354c92c-3c8d-45c2-866d-db10b9081133", + "blob:http://127.0.0.1:8084/ba51afd6-3ad6-4673-9649-972eefa4e9df", + "blob:http://127.0.0.1:8084/58696174-e615-43fc-8dc3-86f892758948", + "blob:http://127.0.0.1:8084/2853ee92-e623-4cb5-8339-082624f1540b", + "blob:http://127.0.0.1:8084/5749d30e-1005-44c5-8f51-ecd0a4083517", + "blob:http://127.0.0.1:8084/87e95025-422d-4199-acbd-c91abe43a73a", + "blob:http://127.0.0.1:8084/4292abb9-9992-499a-95d9-ea127c42e6a0", + "blob:http://127.0.0.1:8084/3fce1b3a-62af-4a8e-9cd0-519910dcabb6", + "blob:http://127.0.0.1:8084/d9dd3d9b-f2ca-4be2-a967-d58091b021e3", + "blob:http://127.0.0.1:8084/82d5f435-c51c-4d83-b34b-2fc064ac5713", + "blob:http://127.0.0.1:8084/1e0a80ca-860e-4afd-b0c1-de95c3b029da", + "blob:http://127.0.0.1:8084/fa77d7b9-1ac1-4dd7-9096-3f1347f37387", + "blob:http://127.0.0.1:8084/c427dc6f-f2ee-4de9-9989-43ca25c9d382", + "blob:http://127.0.0.1:8084/d5ae440b-503c-4678-8aea-5faa53bb6324", + "blob:http://127.0.0.1:8084/8c4a2000-9e30-427a-b88e-3a2cf991e43c" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/649efd1c-f879-4aad-b03a-240cb4b61c6d", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/d5ae440b-503c-4678-8aea-5faa53bb6324", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 193, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/0d40989f-a2de-43fe-8a77-9a3e9f74094e", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/d1b87ccb-1a09-4323-948b-4e1274e41bd7", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 219, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "value": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/9b8bbae3-64c2-4968-8304-54b5923efc71" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/9b8bbae3-64c2-4968-8304-54b5923efc71", + "blob:http://127.0.0.1:8084/73ed3b0b-3c60-463b-9ae4-c212ec894874" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/9b8bbae3-64c2-4968-8304-54b5923efc71", + "blob:http://127.0.0.1:8084/73ed3b0b-3c60-463b-9ae4-c212ec894874" + ] + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/fd367266-fe92-4316-8dd7-6db5b3950f50", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/73ed3b0b-3c60-463b-9ae4-c212ec894874", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 338, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "value": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/c07ed24c-0e58-44b0-91fb-e08c6b7d1337" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c07ed24c-0e58-44b0-91fb-e08c6b7d1337" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/2b582edc-d358-4532-a657-eb7e620ba2f5", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/c07ed24c-0e58-44b0-91fb-e08c6b7d1337", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "chromium", + "status": "passed", + "retry": 0, + "duration": 450, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "value": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "updateSources": [ + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe" + ], + "created": [ + "blob:http://127.0.0.1:8084/3b4cca7e-97af-44b3-8cec-9de1deb02db2", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/bf8dd05e-c3e0-4977-92a2-ff744b133f2a", + "blob:http://127.0.0.1:8084/d519798a-72cd-4897-b56c-40e07ee0dedc", + "blob:http://127.0.0.1:8084/f7d43693-24b7-4b4c-a7c7-36239e957d83", + "blob:http://127.0.0.1:8084/257f8b64-e892-4840-b3fb-c6e01d4bdb3a", + "blob:http://127.0.0.1:8084/8eb048b1-cf70-4d68-9074-814ddf9bc259", + "blob:http://127.0.0.1:8084/ec4ccf04-ad15-437b-90ca-7c67352fcc21", + "blob:http://127.0.0.1:8084/703b7b0b-75f7-4bc5-a5f2-30b6ff909495", + "blob:http://127.0.0.1:8084/43413ab9-74b4-4110-8373-29f2eb94ac4f", + "blob:http://127.0.0.1:8084/48796260-4b4a-4b84-aee6-4e6dee0b9839", + "blob:http://127.0.0.1:8084/40d78efd-84fc-4478-93e9-82c927cb4c6d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3b4cca7e-97af-44b3-8cec-9de1deb02db2", + "blob:http://127.0.0.1:8084/bf8dd05e-c3e0-4977-92a2-ff744b133f2a", + "blob:http://127.0.0.1:8084/d519798a-72cd-4897-b56c-40e07ee0dedc", + "blob:http://127.0.0.1:8084/f7d43693-24b7-4b4c-a7c7-36239e957d83", + "blob:http://127.0.0.1:8084/257f8b64-e892-4840-b3fb-c6e01d4bdb3a", + "blob:http://127.0.0.1:8084/8eb048b1-cf70-4d68-9074-814ddf9bc259", + "blob:http://127.0.0.1:8084/ec4ccf04-ad15-437b-90ca-7c67352fcc21", + "blob:http://127.0.0.1:8084/703b7b0b-75f7-4bc5-a5f2-30b6ff909495", + "blob:http://127.0.0.1:8084/43413ab9-74b4-4110-8373-29f2eb94ac4f", + "blob:http://127.0.0.1:8084/48796260-4b4a-4b84-aee6-4e6dee0b9839", + "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "blob:http://127.0.0.1:8084/40d78efd-84fc-4478-93e9-82c927cb4c6d" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/thumbnail-pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/64db58cf-6800-4aca-9a3c-ff408fc4aab2", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/3b4cca7e-97af-44b3-8cec-9de1deb02db2", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "blob:http://127.0.0.1:8084/4bd973cb-02d0-40fa-97be-ae35d2f66efe", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1493, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "value": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "progress": null, + "processing": false + }, + { + "event": "video", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "canvas", + "progress": null, + "processing": false + }, + { + "event": "update", + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "progress": 1, + "processing": true + }, + { + "event": "done", + "progress": null, + "processing": false + }, + { + "event": "destroy", + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/9d923366-ef60-498c-9248-90f3a0331f87", + "second": "blob:http://127.0.0.1:8084/0db72617-dc2c-45e9-810a-18d9a0762345", + "created": [ + "blob:http://127.0.0.1:8084/4fca6a03-45a9-4b2e-9d6c-a509fac640dc", + "blob:http://127.0.0.1:8084/607d4468-1549-4f52-8a20-6a914a698ecf", + "blob:http://127.0.0.1:8084/937bc16b-a47c-4cba-b616-610ad9a3bbf3", + "blob:http://127.0.0.1:8084/e6a773e0-2b0a-420f-8f05-fbba33e0bfd8", + "blob:http://127.0.0.1:8084/3e4bd643-3a79-4989-afab-457ad58d4dc0", + "blob:http://127.0.0.1:8084/4cd68166-3f1b-4bb1-882d-320052883e17", + "blob:http://127.0.0.1:8084/6da017ab-bcde-40d3-8977-0498027b8216", + "blob:http://127.0.0.1:8084/f75e4f99-b4d4-4da7-97dd-b099157b1d63", + "blob:http://127.0.0.1:8084/e7f7d0d7-71ca-41b0-b355-c6ee58429f15", + "blob:http://127.0.0.1:8084/83931f1f-bf5f-4240-b7e8-c436f9787bf9", + "blob:http://127.0.0.1:8084/9d923366-ef60-498c-9248-90f3a0331f87", + "blob:http://127.0.0.1:8084/dedb7524-53fb-4f43-9440-4c2b0f5692c5", + "blob:http://127.0.0.1:8084/d03b41a5-bd52-407c-a04b-c9d464efb709", + "blob:http://127.0.0.1:8084/8b8c09d3-1b17-40da-ba50-c1b14ec265d1", + "blob:http://127.0.0.1:8084/e628e0b0-98e0-40dd-85ec-2d5789e8a02d", + "blob:http://127.0.0.1:8084/2be81af0-6f53-4645-904f-3c9511e2fb29", + "blob:http://127.0.0.1:8084/3ffe0c3b-7dc9-4f63-9a5f-68513a675dbb", + "blob:http://127.0.0.1:8084/721f6f80-7130-4330-a37b-9da679a2a584", + "blob:http://127.0.0.1:8084/a1afecad-1daf-40e8-98f4-c63d7af6e4e7", + "blob:http://127.0.0.1:8084/b99a015b-2008-4e26-bedf-5e6ecd4ef480", + "blob:http://127.0.0.1:8084/0db72617-dc2c-45e9-810a-18d9a0762345" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/607d4468-1549-4f52-8a20-6a914a698ecf", + "blob:http://127.0.0.1:8084/937bc16b-a47c-4cba-b616-610ad9a3bbf3", + "blob:http://127.0.0.1:8084/e6a773e0-2b0a-420f-8f05-fbba33e0bfd8", + "blob:http://127.0.0.1:8084/3e4bd643-3a79-4989-afab-457ad58d4dc0", + "blob:http://127.0.0.1:8084/4cd68166-3f1b-4bb1-882d-320052883e17", + "blob:http://127.0.0.1:8084/6da017ab-bcde-40d3-8977-0498027b8216", + "blob:http://127.0.0.1:8084/f75e4f99-b4d4-4da7-97dd-b099157b1d63", + "blob:http://127.0.0.1:8084/e7f7d0d7-71ca-41b0-b355-c6ee58429f15", + "blob:http://127.0.0.1:8084/83931f1f-bf5f-4240-b7e8-c436f9787bf9", + "blob:http://127.0.0.1:8084/9d923366-ef60-498c-9248-90f3a0331f87", + "blob:http://127.0.0.1:8084/dedb7524-53fb-4f43-9440-4c2b0f5692c5", + "blob:http://127.0.0.1:8084/d03b41a5-bd52-407c-a04b-c9d464efb709", + "blob:http://127.0.0.1:8084/8b8c09d3-1b17-40da-ba50-c1b14ec265d1", + "blob:http://127.0.0.1:8084/e628e0b0-98e0-40dd-85ec-2d5789e8a02d", + "blob:http://127.0.0.1:8084/2be81af0-6f53-4645-904f-3c9511e2fb29", + "blob:http://127.0.0.1:8084/3ffe0c3b-7dc9-4f63-9a5f-68513a675dbb", + "blob:http://127.0.0.1:8084/721f6f80-7130-4330-a37b-9da679a2a584", + "blob:http://127.0.0.1:8084/a1afecad-1daf-40e8-98f4-c63d7af6e4e7", + "blob:http://127.0.0.1:8084/b99a015b-2008-4e26-bedf-5e6ecd4ef480", + "blob:http://127.0.0.1:8084/4fca6a03-45a9-4b2e-9d6c-a509fac640dc", + "blob:http://127.0.0.1:8084/0db72617-dc2c-45e9-810a-18d9a0762345" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 402, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 564, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "value": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/812180c7-0135-4770-8bdb-5be9f6bad5d6" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/812180c7-0135-4770-8bdb-5be9f6bad5d6", + "blob:http://127.0.0.1:8084/daa27d0f-65e1-4db1-a1ee-721497335f35" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/812180c7-0135-4770-8bdb-5be9f6bad5d6", + "blob:http://127.0.0.1:8084/daa27d0f-65e1-4db1-a1ee-721497335f35" + ] + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 528, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "value": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/5ec96e48-6298-427a-ae7f-3e5abba399dd" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5ec96e48-6298-427a-ae7f-3e5abba399dd" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "firefox", + "status": "passed", + "retry": 0, + "duration": 1281, + "errors": [], + "capabilityControl": false, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "value": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "updateSources": [ + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c" + ], + "created": [ + "blob:http://127.0.0.1:8084/aa043b6d-aa17-4f69-89db-e1e068b50eac", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/1ad4341a-10d4-4c27-889e-4714c57ab3e7", + "blob:http://127.0.0.1:8084/0a460c98-0620-44a3-b1ba-1a533fc39211", + "blob:http://127.0.0.1:8084/09d2e887-5d11-4c81-95c8-da23d73cd87b", + "blob:http://127.0.0.1:8084/ebd5137f-3eb8-42e1-a12b-b14e3f8fc363", + "blob:http://127.0.0.1:8084/3484b5ca-e9ca-4986-bf2d-d01385029b15", + "blob:http://127.0.0.1:8084/149ea2ee-2960-48bc-8912-a232e62d591e", + "blob:http://127.0.0.1:8084/91705ef8-e4fc-4045-9202-803af5b033d4", + "blob:http://127.0.0.1:8084/0ad47692-4d5c-4699-a75b-8e0171f28536", + "blob:http://127.0.0.1:8084/16fd9f8e-ee1d-46a8-96b0-f45efbe03d9e", + "blob:http://127.0.0.1:8084/1e4fc163-80f8-4f4a-bd0a-50140a3f7aaf" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/aa043b6d-aa17-4f69-89db-e1e068b50eac", + "blob:http://127.0.0.1:8084/1ad4341a-10d4-4c27-889e-4714c57ab3e7", + "blob:http://127.0.0.1:8084/0a460c98-0620-44a3-b1ba-1a533fc39211", + "blob:http://127.0.0.1:8084/09d2e887-5d11-4c81-95c8-da23d73cd87b", + "blob:http://127.0.0.1:8084/ebd5137f-3eb8-42e1-a12b-b14e3f8fc363", + "blob:http://127.0.0.1:8084/3484b5ca-e9ca-4986-bf2d-d01385029b15", + "blob:http://127.0.0.1:8084/149ea2ee-2960-48bc-8912-a232e62d591e", + "blob:http://127.0.0.1:8084/91705ef8-e4fc-4045-9202-803af5b033d4", + "blob:http://127.0.0.1:8084/0ad47692-4d5c-4699-a75b-8e0171f28536", + "blob:http://127.0.0.1:8084/16fd9f8e-ee1d-46a8-96b0-f45efbe03d9e", + "blob:http://127.0.0.1:8084/0ac51ae8-9198-4395-af47-0cf441e2e49c", + "blob:http://127.0.0.1:8084/1e4fc163-80f8-4f4a-bd0a-50140a3f7aaf" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1118, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1127, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1106, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1137, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "webkit", + "status": "passed", + "retry": 0, + "duration": 1075, + "errors": [], + "capabilityControl": true, + "attachments": [ + { + "name": "thumbnail-runtime", + "value": { + "name": "candidate-artifact", + "sha256": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "value": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + }, + { + "name": "browser-evidence", + "state": { + "url": "http://127.0.0.1:8084/test/player.html?core=published", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15", + "media": null + }, + "errors": [], + "consoleErrors": [], + "failedRequests": [] + } + ] + } + ] + } + ], + "installed": { + "report": { + "file": "refactor/.cache/thumbnail-package-types-c9yMsJ/report.json", + "sha256": "13a64c8b3a5a2c6f056f9f072affb197fbefe8a23616c86611ac8b7866a729a4", + "bytes": 19829 + }, + "task": "PKG-TOOL-THUMB-04", + "scope": "Frozen Git input fixture and candidate Yarn pack installed outside workspace. The frozen fixture is not an original npm archive; no recovered 3.5.31 complete archive is claimed. Candidate root/legacy runtime, offline frozen install, all files and strict consumers verified.", + "packages": [ + { + "label": "workspace-fixture", + "name": "artplayer-tool-thumbnail", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\thumbnail-package-types-c9yMsJ\\workspace-fixture.tgz", + "sha256": "6fbc7120b26efd0ab3f5c90d695c933be84b993e711bb7204bacb936bff98842", + "manifest": { + "name": "artplayer-tool-thumbnail", + "version": "4.4.0", + "description": "Thumbnail tool for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "legacy": "dist/artplayer-tool-thumbnail.legacy.js", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + "import": "./dist/artplayer-tool-thumbnail.esm.js", + "require": "./dist/artplayer-tool-thumbnail.js", + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "module": "dist/artplayer-tool-thumbnail.esm.js", + "types": "types/artplayer-tool-thumbnail.d.ts", + "browserslist": "last 1 Chrome version" + }, + "files": { + "package/README.md": "91477fe542a44713089e384308ae6f311f887cfe63aafe1483b778a37acfa458", + "package/dist/artplayer-tool-thumbnail.js": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "package/dist/artplayer-tool-thumbnail.legacy.js": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "package/dist/artplayer-tool-thumbnail.mjs": "4d46df8cccdc92d18efc6f12abedf85e8881737023f4ead28697c8b1e85eead1", + "package/package.json": "9f4f9c6628ee48068f380bc52f4058c1f6aac31c20de6ed069a2c96ba635e23f", + "package/src/emitter.js": "5aa71e4317037c06b94e259beed2e964bbbb7117edbe78d29cc941c4916fd2ff", + "package/src/index.js": "67bc1b6a58faf11b14c8d410ae829edd64b84dc9e7b4b388b2e2dd60788bcbe4", + "package/src/utils.js": "dc19fe2f338df7058084eb2301d37db59ec4c914a85ba819a856d270c1267105" + } + }, + { + "label": "candidate", + "name": "artplayer-tool-thumbnail", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\thumbnail-package-types-c9yMsJ\\candidate.tgz", + "sha256": "c8a7197c603d16a504cc8be80e521f1afddd62ee8c627f1373a7b8f52a355c11", + "manifest": { + "name": "artplayer-tool-thumbnail", + "version": "4.4.0", + "description": "Thumbnail tool for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "legacy": "dist/artplayer-tool-thumbnail.legacy.js", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "import": { + "types": "./types/artplayer-tool-thumbnail.d.mts", + "default": "./dist/artplayer-tool-thumbnail.mjs" + }, + "require": { + "types": "./types/artplayer-tool-thumbnail.d.cts", + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "./legacy": { + "types": "./types/artplayer-tool-thumbnail.d.cts", + "import": "./dist/artplayer-tool-thumbnail.legacy.js", + "require": "./dist/artplayer-tool-thumbnail.legacy.js", + "default": "./dist/artplayer-tool-thumbnail.legacy.js" + } + }, + "main": "./dist/artplayer-tool-thumbnail.js", + "module": "./dist/artplayer-tool-thumbnail.mjs", + "types": "./types/artplayer-tool-thumbnail.d.ts", + "browserslist": "last 1 Chrome version", + "typesVersions": { + "*": { + "legacy": [ + "types/artplayer-tool-thumbnail.d.ts" + ] + } + }, + "files": [ + "ARCHITECTURE.md", + "README.md", + "THIRD_PARTY_NOTICES", + "dist", + "types" + ] + }, + "files": { + "package/ARCHITECTURE.md": "628b394b29321e1f739a99cc2c866adba48e5b07bde00dbf5786087f5ca1938f", + "package/README.md": "24ed417415653307342281e31f6bf507c5d27f1cd8ab0be81af11ca50e1f3950", + "package/THIRD_PARTY_NOTICES": "7bea0832808c3d37b5a79f2ed6c65ef2ebdf7dbcbba124c469ebb90e2ec7c013", + "package/dist/artplayer-tool-thumbnail.js": "615e574d90dcba34c2491ff38311a36df2917e58207252cc8c7752a234af3916", + "package/dist/artplayer-tool-thumbnail.legacy.js": "e5574839b952d040cf62935af9e724831ca351fe4db83e00ad7410b8495f14e0", + "package/dist/artplayer-tool-thumbnail.mjs": "9596144702d869797ded8306fc810abb3a81d4325d04917a176b2242ecc214c3", + "package/package.json": "dafbe13cddee908b2932ab2f529b0eef86ad9d9de8f7ab7585ed073c221cdbe1", + "package/types/README.md": "5a6071256c2d810ca77d6022bf81479ec3d48666977fa0706444f9a514834215", + "package/types/artplayer-tool-thumbnail.d.cts": "f0573326178b57a6e22a08b53247eb15ef4d20a1652e40d0cae638ac7aaf8092", + "package/types/artplayer-tool-thumbnail.d.mts": "e96e0123bff89e109a00c1c169da99741c8bf66d3f4fd53b8e8ecf7c3f440533", + "package/types/artplayer-tool-thumbnail.d.ts": "ec2a39f3e2ac5ef1717d4da56546486c614a9bb6291c14c05723c1990546e79f" + } + } + ], + "matrix": [ + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "diagnostics": [ + { + "code": 2307, + "message": "Cannot find module 'artplayer-tool-thumbnail' or its corresponding type declarations." + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "bundler-esm", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "diagnostics": [ + { + "code": 2307, + "message": "Cannot find module 'artplayer-tool-thumbnail' or its corresponding type declarations." + } + ], + "negative": [] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "bundler-esm", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element | undefined'." + }, + { + "code": 2345, + "message": "Argument of type '\"video.mp4\"' is not assignable to parameter of type 'File | null | undefined'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': height, playsInline, poster, videoHeight, and 285 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs-no-interop", + "diagnostics": [], + "negative": [ + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + } + ] + }, + { + "package": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs-no-interop", + "diagnostics": [], + "negative": [ + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + } + ] + } + ], + "runtime": [ + { + "package": "workspace-fixture", + "commonjs": "function", + "esm": "ERR_MODULE_NOT_FOUND", + "typesMissing": true + }, + { + "package": "candidate", + "commonjs": "function", + "esm": "function", + "legacy": "function", + "ssrImport": true + } + ] + }, + "scope": [ + "123 source specialist tests pass; rebuilt main and legacy each pass 49 cleanup/input/extraction/emitter tests. New regression has one historical contract case and two candidate cases, each covering seven distinct falsy values.", + "57 browser results are passed, including 10 explicitly annotated Windows WebKit Blob capability controls; those ten are not successful extraction/cancellation scenarios. Source cleanup/DOM comparisons run in all three engines.", + "Source browser run preceded changing equivalent global references to window.ArtplayerToolThumbnail and lint formatting; installed main/legacy runs execute the final test driver. Production lifecycle code was unchanged across all final checks.", + "Candidate Yarn pack installed outside the workspace, reinstalled with frozen/offline checks, all file hashes checked. Three bundled formats and shipped architecture documentation match the verified installed copy; no original complete npm 3.5.31 archive is claimed.", + "Installed type matrix has seven candidate positive cases with zero diagnostics and 52 expected negative diagnostics, plus five exact historical fixture missing-entry/declaration failures; these are not twelve passing historical compilations.", + "No public declaration, version, dependency or lockfile changed. Root scripts add the regression to test:thumbnail and test:unit. Historical compatibility/default decision and final device/release gates remain open." + ] +} diff --git a/refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md b/refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md new file mode 100644 index 000000000..02db4cf5f --- /dev/null +++ b/refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md @@ -0,0 +1,60 @@ +# PKG-TOOL-THUMB-07 保留清理中的原始异常值 + +来源 HEAD:`2a380679117b02499ff3a131cb449e1eda007cb2`。 +本任务修复迁移中的兼容回归,不改变待批准的 Thumbnail 默认行为方案。 +对应 API-04 事件异常传播和 API-05 销毁/资源清理。 + +## 缺陷与修复 + +恢复的3.5.31 CDN文件及冻结工作区 main/legacy 在 destroy 监听器抛出 undefined、 +null、false、0、-0、空字符串或NaN时,均同步向调用方传播原值。迁移后的 +cleanupAll 使用异常值的 truthiness 判断是否失败,导致这些异常被吞掉;如果 +后续清理再抛出 Error,后一个异常还会覆盖第一个值。 + +新增回归在未修复候选上1通过、2失败:历史契约通过,候选在首个undefined的 +传播及后续异常覆盖检查失败。修复后的全部七种值都被实际检查,使用独立的 +threw 标记和 Object.is,避免“没有抛出”和“抛出undefined”混淆,也区分-0与0。 + +实现仅调整 src/lifecycle.ts 的 cleanupAll:使用单独 failed 布尔值记录异常发生, +保存第一个原始值;继续执行所有后续资源清理,然后按原值抛出。没有改变正常 +返回路径、清理顺序、事件名称、默认参数、同步/异步形状或类型声明。 + +销毁先将实例关闭,失败后再次 destroy 仍不重复执行。cleanupAll 的继续清理 +不代表 emitter 改为继续派发:destroy 的一个监听器抛出后,当前派发中其余 +监听器仍不执行。测试同时检查这两层边界。 + +## 验证 + +| 验证 | 结果与范围 | +| --- | --- | +| 新回归旧红/新绿 | 未修复1通过2失败;修复后3通过,包含七种 falsy 值、原始异常身份、继续清理和重复销毁 | +| 源码专项 | yarn test:thumbnail:123通过 | +| 正式 main/legacy | 各49项输入、抽帧、清理和 emitter 单测通过 | +| 源码真实浏览器 | 15通过:3份历史实现及候选的原始异常、真实DOM清理,以及现有事件注册回归 | +| 已安装 main/legacy 浏览器 | 各21报告通过;每轮5项Windows WebKit为Blob能力受限对照,不计抽帧成功 | +| 其余检查 | 严格包类型、定向lint、正常三格式构建、严格工具链通过 | + +浏览器共57报告通过,其中47项实际执行对应正常场景,10项是明确标注的 +WebKit能力对照;没有失败或重试,也没有把受限对照记作成功抽帧。main/legacy +还覆盖正常PNG像素/重复生成、拖放、损坏媒体后切源、迟到编码与销毁/替换取消。 + +Node24.21.0、YarnClassic1.22.22;隔离包由 +`yarn test:thumbnail-types-package` 实际打包、在仓库外安装、冻结离线重装并核对 +每个文件。安装后的7项候选正例通过、52项预期负例诊断成立;5项历史工作区 +缺入口/声明的预期失败保持原样。编译器为TS5.9.3及4.3.5,不能称为12项旧声明 +全部编译通过。历史工作区夹具也不是已找回完整npm3.5.31归档。 + +包内 ARCHITECTURE 已在打包前更新。三份dist、docs/compiled副本和已安装副本 +字节一致;公共types未变。源浏览器检查后仅规范了等价的window全局访问及格式, +最终测试文件已用于main/legacy运行。root test:thumbnail 与 test:unit 均新增此 +回归,无新增依赖、版本变更或锁文件修改。 + +机器证据:[thumbnail-cleanup-errors-validation.json](../baselines/thumbnail-cleanup-errors-validation.json)。 +命令入口和职责随包内文档一起维护。此任务不关闭04的默认值冲突,也不代替 +05/06的最终组合、设备或完整分发验收;本轮未推送、部署或发布。 + +## 回退与交付 + +回退本独立提交会恢复有缺陷的 truthiness 判断,撤销对应测试、文档和本轮 +构建产物;不撤销此前的TS迁移、输入/抽帧资源归属和emitter修复。 +本任务以PKG-TOOL-THUMB-07单独完成并提交,04继续doing。 diff --git a/refactor/plan.md b/refactor/plan.md index 4391493b4..80efff604 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -2,9 +2,9 @@ > 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。 -基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 267 项,范围 22 个包及工作区/示例。 +基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 268 项,范围 22 个包及工作区/示例。 -状态:todo 43 / doing 21 / blocked 0 / done 203 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 43 / doing 21 / blocked 0 / done 204 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -34,7 +34,7 @@ | artplayer-proxy-canvas | 1.1.0 | PKG-AMBILIGHT-PROXY-01, PKG-CANVAS-01, PKG-CANVAS-02, PKG-CANVAS-03, PKG-CANVAS-04, PKG-CANVAS-05, PKG-CANVAS-06, PKG-FACTORY-01, PKG-CANVAS-SUBTITLE-01 | | artplayer-proxy-mediabunny | 1.2.0 | PKG-MB-01, PKG-MB-02, PKG-MB-03, PKG-MB-04, PKG-MB-05, PKG-MB-06, PKG-MB-07, PKG-MB-08, PKG-MB-09, PKG-MB-10 | | artplayer-tool-iframe | 1.1.0 | PKG-IFRAME-01, PKG-IFRAME-02, PKG-IFRAME-03, PKG-IFRAME-04, PKG-IFRAME-05, PKG-IFRAME-06 | -| artplayer-tool-thumbnail | 4.4.0 | PKG-TOOL-THUMB-01, PKG-TOOL-THUMB-02, PKG-TOOL-THUMB-03, PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06 | +| artplayer-tool-thumbnail | 4.4.0 | PKG-TOOL-THUMB-01, PKG-TOOL-THUMB-02, PKG-TOOL-THUMB-03, PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06, PKG-TOOL-THUMB-07 | | artplayer-vitepress | 1.1.0 | SITE-01, SITE-SMOKE-01, SITE-02, SITE-LOAD-01, SITE-AI-DOCS-01, SITE-BUILD-01, SITE-03, SITE-04, SITE-05, SITE-06, SITE-07, SITE-VCONSOLE-01, ENG-LINT-02, SITE-CONSOLE-01 | ## 0 规划 @@ -389,9 +389,10 @@ | PKG-TOOL-THUMB-01 | artplayer-tool-thumbnail
核对包契约与历史用法 | BASE-05 | 构造/defaults/事件/方法/历史拼写、.esm.js 入口和缺失 types | 源码/声明/README/demo/可恢复发布内容差异已登记;公开形状和版本范围冻结;区分旧 npm 的 main/CSS 与当前工作区无 main、.esm.js/类型缺失,不把工作区状态冒称旧 npm 发布事实 | H | done | | PKG-TOOL-THUMB-02 | artplayer-tool-thumbnail
建立特有行为与错误测试 | PKG-TOOL-THUMB-01, ENG-03, ENG-05 | 文件输入/拖放、抽帧网格、begin/end、进度/失败/重复任务 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-TOOL-THUMB-03 | artplayer-tool-thumbnail
整理内部职责与资源 | PKG-TOOL-THUMB-02, CORE-01 | 输入、抽帧队列、网格导出、URL/监听清理分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | -| PKG-TOOL-THUMB-04 | artplayer-tool-thumbnail
迁移自有源码和公开类型 | PKG-TOOL-THUMB-03, ENG-04, ENG-06, CORE-07 | 补齐真实 API 声明,自有 emitter/utils TS 化 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;为 BASE-DIST-01 提供兼容入口/声明及消费者验证 | H | doing | +| PKG-TOOL-THUMB-04 | artplayer-tool-thumbnail
迁移自有源码和公开类型 | PKG-TOOL-THUMB-03, ENG-04, ENG-06, CORE-07, PKG-TOOL-THUMB-07 | 补齐真实 API 声明,自有 emitter/utils TS 化 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;为 BASE-DIST-01 提供兼容入口/声明及消费者验证 | H | doing | | PKG-TOOL-THUMB-05 | artplayer-tool-thumbnail
验证新旧核心和组合 | PKG-TOOL-THUMB-04, CORE-22 | 工具独立浏览器使用,生成缩略图在核心中显示 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-TOOL-THUMB-06 | artplayer-tool-thumbnail
验证分发并同步文档 | PKG-TOOL-THUMB-05, ENG-07 | tool.thumbnail.js、历史 ESM 兼容文件、types 路径和 tarball | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | +| PKG-TOOL-THUMB-07 | artplayer-tool-thumbnail
恢复清理和销毁中的原始异常传播 | PKG-TOOL-THUMB-03 | 清理记录与异常值分离,保留首个 falsy 抛出值并继续释放资源;历史对照、真实DOM、分发验证及维护记录 | 恢复旧 destroy 监听器同步异常语义,0/false/undefined/NaN等不被吞掉或覆盖;后续清理、重复销毁与正常抽帧回归通过,默认兼容决策不受影响 | M | done | ## 6 文档与消费者 @@ -700,3 +701,4 @@ - ENG-LINT-02: [记录](changes/2026-09-15-ENG-LINT-02-clean-checks.md) [记录](baselines/eng-lint02-validation.json) - SITE-CONSOLE-01: [记录](console-modernization.md) [记录](changes/2026-09-15-SITE-CONSOLE-01-typescript.md) [记录](baselines/site-console01-validation.json) - PKG-AUTO-THUMB-12: [记录](changes/2026-09-15-PKG-AUTO-THUMB-12-metadata.md) [记录](baselines/auto-thumbnail-metadata-validation.json) +- PKG-TOOL-THUMB-07: [记录](changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md) [记录](baselines/thumbnail-cleanup-errors-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index cb5350840..91d633a5e 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,19 @@ # 进度与证据 +## PKG-TOOL-THUMB-07 清理异常传播修复 + +修复cleanupAll吞掉destroy监听器抛出的falsy值、或被后续异常覆盖的兼容回归。 +历史3.5.31/工作区保留原值,未修复候选1通过2失败;修复后继续释放资源,保留 +第一个原始异常,重复销毁仍不执行,普通事件派发遇错仍停止。 + +123源码专项、main/legacy各49专项通过;源码及实际安装产物共57浏览器报告 +通过,其中10项是明确的Windows WebKit Blob能力对照,不计抽帧成功。7项安装 +类型正例、52预期负例诊断、5历史缺入口/声明对照成立;三格式/文档副本字节 +一致。详见[记录](changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md)和 +[证据](baselines/thumbnail-cleanup-errors-validation.json)。 +新增独立任务07完成,04的默认兼容决策保持待处理;204 done、21 doing、43 todo, +共268项。无新依赖、版本变更或发布操作。 + ## PKG-HLS-SDK-01 SDK顺序与销毁前崩溃 新增固定SDK序列诊断入口,逐页核验版本并保存实际顺序。在同一个Firefox实例 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 9f45a727a..bb78eba4a 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -281,3 +281,4 @@ | CONSOLE-ERROR-01 | resolved / 已复现 | Firefox/WebKit 控制台丢失原生 Error 消息 | SITE-CONSOLE-01, SITE-07 | | AUTO-THUMB-METADATA-01 | resolved / 已复现 | Private metadata loading can retain its decoder indefinitely without metadata or media errors | PKG-AUTO-THUMB-12 | | ASR-MEDIA-01 | resolved / 已复现 | ASR accepts obsolete recognition after native media error without pause | PKG-ASR-10, PKG-ASR-05 | +| THUMB-CLEANUP-01 | resolved / 已复现 | Thumbnail 清理吞掉 falsy 监听器异常或覆盖第一个异常值 | PKG-TOOL-THUMB-07, PKG-TOOL-THUMB-04 | diff --git a/refactor/risks.json b/refactor/risks.json index 1c3877d14..92da067c2 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -6605,6 +6605,30 @@ "refactor/baselines/asr-media-error-validation.json" ], "resolutionRationale": "Five prepatch unit failures and four native Chromium failures become passing cancellation/recovery checks. Source and actual installed main/legacy pass, including minimum5.3.0 and5.4.0 core controls; default routing and captured cleanup remain intact. WebKit capability skips and broader package acceptance are not waived." + }, + { + "id": "THUMB-CLEANUP-01", + "title": "Thumbnail 清理吞掉 falsy 监听器异常或覆盖第一个异常值", + "confirmation": "reproduced", + "status": "resolved", + "owners": [ + "PKG-TOOL-THUMB-07", + "PKG-TOOL-THUMB-04" + ], + "evidence": [ + "test/thumbnail-cleanup-errors.test.js", + "test/browser/thumbnail-emitter.spec.js", + "refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md", + "refactor/baselines/thumbnail-cleanup-errors-validation.json" + ], + "compatibleResolution": "独立标记是否发生异常,保留首个值并完成其余清理,再原样抛出;普通事件派发遇错停止,重复销毁保持幂等。", + "closureCriteria": "历史/未修复/修复候选对照、七种falsy原值身份、资源释放与真实DOM验证通过;三格式和隔离安装消费者通过,默认兼容冲突独立保留。", + "workspaceState": "PKG-TOOL-THUMB-07: before 1 pass/2 fail; source 123 pass, main/legacy 49 each; browser 57 passed reports include 10 explicit Windows WebKit Blob capability controls. Installed candidate positive/negative types and byte identity verified; no public types or defaults changed.", + "resolutionEvidence": [ + "refactor/changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md", + "refactor/baselines/thumbnail-cleanup-errors-validation.json" + ], + "resolutionRationale": "Frozen historical destroy contracts preserve all seven falsy values; the prepatch candidate fails exception propagation and first-value ownership. Fixed source and rebuilt main/legacy pass cleanup/resource and actual browser DOM regressions. Installed artifact and type checks pass; historical defaults and WebKit media capability gaps remain outside this narrowly resolved regression." } ] } diff --git a/refactor/tasks.json b/refactor/tasks.json index ac154e838..d635d1d4f 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -4205,7 +4205,8 @@ "PKG-TOOL-THUMB-03", "ENG-04", "ENG-06", - "CORE-07" + "CORE-07", + "PKG-TOOL-THUMB-07" ], "status": "doing", "risk": "H", @@ -5746,6 +5747,25 @@ "changes/2026-09-15-PKG-AUTO-THUMB-12-metadata.md", "baselines/auto-thumbnail-metadata-validation.json" ] + }, + { + "id": "PKG-TOOL-THUMB-07", + "phase": "5 包迁移:artplayer-tool-thumbnail", + "title": "恢复清理和销毁中的原始异常传播", + "scope": [ + "artplayer-tool-thumbnail" + ], + "dependsOn": [ + "PKG-TOOL-THUMB-03" + ], + "status": "done", + "risk": "M", + "deliverable": "清理记录与异常值分离,保留首个 falsy 抛出值并继续释放资源;历史对照、真实DOM、分发验证及维护记录", + "acceptance": "恢复旧 destroy 监听器同步异常语义,0/false/undefined/NaN等不被吞掉或覆盖;后续清理、重复销毁与正常抽帧回归通过,默认兼容决策不受影响", + "evidence": [ + "changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md", + "baselines/thumbnail-cleanup-errors-validation.json" + ] } ] } diff --git a/test/browser/thumbnail-emitter.spec.js b/test/browser/thumbnail-emitter.spec.js index dd0c9a9aa..1bb73bb4e 100644 --- a/test/browser/thumbnail-emitter.spec.js +++ b/test/browser/thumbnail-emitter.spec.js @@ -1,9 +1,43 @@ import { hash } from '../../refactor/scripts/releases.mjs' -import { thumbnailCandidate } from '../helpers/thumbnail.js' +import { thumbnailCandidate, thumbnailHistorical } from '../helpers/thumbnail.js' import { expect, test } from './fixtures.js' const candidate = await thumbnailCandidate() +for (const implementation of [...thumbnailHistorical(), candidate]) { + test(`Thumbnail ${implementation.name}: destroy preserves falsy listener exceptions with real DOM cleanup`, async ({ page }, testInfo) => { + await page.goto('/test/player.html?core=candidate&chapter=published') + await page.addScriptTag({ content: implementation.code }) + const result = await page.evaluate(() => { + return [undefined, null, false, 0, -0, '', Number.NaN].map((failure, index) => { + const input = document.createElement('input') + input.type = 'file' + document.body.append(input) + const tool = new window.ArtplayerToolThumbnail({ fileInput: input }) + let notified = 0 + let threw = false + let same = false + tool.on('destroy', () => { + notified++ + throw failure + }) + try { + tool.destroy() + } + catch (error) { + threw = true + same = Object.is(error, failure) + } + const videoConnected = tool.video.isConnected + input.remove() + return { index, threw, same, notified, videoConnected } + }) + }) + await testInfo.attach('thumbnail-cleanup-errors', { contentType: 'application/json', body: JSON.stringify({ implementation: implementation.name, sha256: hash(implementation.code), result }) }) + expect(result).toEqual(Array.from({ length: 7 }, (_, index) => ({ index, threw: true, same: true, notified: 1, videoConnected: false }))) + }) +} + test('Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup', async ({ page }, testInfo) => { await page.goto('/test/player.html?core=candidate&chapter=published') await page.addScriptTag({ content: candidate.code }) @@ -11,7 +45,7 @@ test('Thumbnail native browser event registry handles special names and nested o const input = document.createElement('input') input.type = 'file' document.body.append(input) - const tool = new ArtplayerToolThumbnail({ fileInput: input }) + const tool = new window.ArtplayerToolThumbnail({ fileInput: input }) const keys = [] for (const name of ['__proto__', 'constructor', 'toString', 'hasOwnProperty']) { let calls = 0 @@ -23,7 +57,7 @@ test('Thumbnail native browser event registry handles special names and nested o } let nested = false const seen = [] - tool.on('custom', value => { + tool.on('custom', (value) => { if (!nested) { nested = true tool.emit('custom', 'inner') diff --git a/test/thumbnail-cleanup-errors.test.js b/test/thumbnail-cleanup-errors.test.js new file mode 100644 index 000000000..874974f42 --- /dev/null +++ b/test/thumbnail-cleanup-errors.test.js @@ -0,0 +1,85 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Compare cleanup exception identity with frozen releases. +import test from 'node:test' +import { thumbnailCandidate, thumbnailEnvironment, thumbnailHistorical } from './helpers/thumbnail.js' + +const candidate = await thumbnailCandidate() +const failures = [undefined, null, false, 0, -0, '', Number.NaN] +function attempt(callback) { + try { + callback() + return { threw: false } + } + catch (error) { + return { threw: true, error } + } +} +function create(implementation) { + const env = thumbnailEnvironment(implementation) + const input = new env.Element('input') + const tool = new env.Factory({ fileInput: input }) + return { ...env, input, tool } +} + +test('Thumbnail historical destroy listeners synchronously propagate falsy thrown values', () => { + for (const implementation of thumbnailHistorical()) { + for (const failure of failures) { + const { tool } = create(implementation) + tool.on('destroy', () => { + throw failure + }) + const result = attempt(() => tool.destroy()) + assert.equal(result.threw, true, implementation.name) + assert(Object.is(result.error, failure), implementation.name) + } + } +}) + +test('Thumbnail destroy preserves falsy listener exceptions after releasing native resources', () => { + for (const failure of failures) { + const { tool, input } = create(candidate) + let first = 0 + let second = 0 + tool.on('destroy', () => { + first++ + throw failure + }).on('destroy', () => second++) + const result = attempt(() => tool.destroy()) + assert.equal(result.threw, true) + assert(Object.is(result.error, failure)) + assert.equal(tool.video.parentNode, null) + assert.equal(input.handlers.get('change')?.size || 0, 0) + assert.equal(first, 1) + assert.equal(second, 0, 'Normal emitter exception propagation still stops this dispatch') + assert.doesNotThrow(() => tool.destroy()) + assert.equal(first, 1, 'Failed destroy is still terminal') + } +}) + +test('Thumbnail cleanup retains the first falsy failure and still attempts later cleanup and destroy notification', () => { + for (const failure of failures) { + const { tool, input, operations } = create(candidate) + tool.loadVideo({ name: 'first.mp4', type: 'video/mp4' }) + const calls = [] + tool.video.pause = () => { + calls.push('pause') + throw failure + } + tool.video.removeAttribute = name => calls.push(`remove:${name}`) + tool.video.load = () => { + calls.push('load') + throw new Error('later decoder reset error') + } + tool.on('destroy', () => { + calls.push('destroy') + throw new Error('later listener error') + }) + const result = attempt(() => tool.destroy()) + assert.equal(result.threw, true) + assert(Object.is(result.error, failure), 'Later errors cannot replace the first thrown value') + assert.deepEqual(calls, ['pause', 'remove:src', 'load', 'destroy']) + assert.equal(tool.video.parentNode, null) + assert.equal(input.handlers.get('change')?.size || 0, 0) + assert.deepEqual(operations.filter(item => item.name === 'revokeURL').map(item => item.url), [tool.videoUrl]) + } +})