From b5f8b5e28e33cfb44e7aa93089b6dfbf247142f0 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Tue, 15 Sep 2026 23:54:24 +0800 Subject: [PATCH] refactor(thumbnail): [PKG-TOOL-THUMB-04] preserve approved published defaults --- docs/assets/ts/artplayer-tool-thumbnail.d.ts | 11 +- docs/compiled/artplayer-tool-thumbnail.js | 2 +- .../artplayer-tool-thumbnail.legacy.js | 2 +- docs/compiled/artplayer-tool-thumbnail.mjs | 88 +- package.json | 4 +- .../artplayer-tool-thumbnail/ARCHITECTURE.md | 39 +- packages/artplayer-tool-thumbnail/README.md | 23 +- .../dist/artplayer-tool-thumbnail.js | 2 +- .../dist/artplayer-tool-thumbnail.legacy.js | 2 +- .../dist/artplayer-tool-thumbnail.mjs | 88 +- .../src/extraction.ts | 25 +- .../artplayer-tool-thumbnail/src/index.ts | 6 +- .../artplayer-tool-thumbnail/src/input.ts | 8 +- .../artplayer-tool-thumbnail/src/policy.ts | 22 + .../artplayer-tool-thumbnail/src/source.ts | 22 +- .../artplayer-tool-thumbnail/src/types.ts | 4 + .../artplayer-tool-thumbnail/types/README.md | 23 +- .../types/artplayer-tool-thumbnail.d.mts | 2 +- .../types/artplayer-tool-thumbnail.d.ts | 13 +- .../thumbnail-policy-validation.json | 10488 ++++++++++++++++ ...09-15-PKG-TOOL-THUMB-04-approved-policy.md | 91 + refactor/plan.md | 6 +- refactor/progress.md | 13 + refactor/risk-table.md | 2 +- refactor/risks.json | 16 +- refactor/scripts/thumbnail-package-types.mjs | 18 +- refactor/scripts/thumbnail-types.test.mjs | 8 +- refactor/tasks.json | 6 +- refactor/thumbnail-compatibility-decision.md | 17 +- test/browser/thumbnail-editor-types.spec.js | 6 +- test/browser/thumbnail-input.spec.js | 2 +- test/browser/thumbnail-tool.spec.js | 29 +- test/thumbnail-input.test.js | 4 +- test/thumbnail-lifecycle.test.js | 5 +- test/thumbnail-policy.test.js | 188 + test/thumbnail-runtime.test.js | 9 +- test/types/thumbnail.ts | 14 +- 37 files changed, 11172 insertions(+), 136 deletions(-) create mode 100644 packages/artplayer-tool-thumbnail/src/policy.ts create mode 100644 refactor/baselines/thumbnail-policy-validation.json create mode 100644 refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md create mode 100644 test/thumbnail-policy.test.js diff --git a/docs/assets/ts/artplayer-tool-thumbnail.d.ts b/docs/assets/ts/artplayer-tool-thumbnail.d.ts index ee71e2feb..12a7b747a 100644 --- a/docs/assets/ts/artplayer-tool-thumbnail.d.ts +++ b/docs/assets/ts/artplayer-tool-thumbnail.d.ts @@ -2,7 +2,7 @@ /** Extract PNG thumbnail sheets from a locally selected video file. */ declare class ArtplayerToolThumbnail { constructor(option?: ArtplayerToolThumbnail.Option) - static readonly DEFAULTS: ArtplayerToolThumbnail.SheetOptions + static readonly DEFAULTS: ArtplayerToolThumbnail.DefaultOptions static ondragover(event: DragEvent): void static creatVideo(): HTMLVideoElement processing: boolean @@ -39,13 +39,22 @@ declare namespace ArtplayerToolThumbnail { begin: number end: number } + type Compatibility = 'published-3.5' | 'workspace-4.4' + interface DefaultOptions extends SheetOptions { + delay: number + } /** fileInput must be a file input or an Element wrapper when constructing. */ interface Option extends Partial { fileInput?: Element + compatibility?: Compatibility + /** Published mode delay in milliseconds, clamped to 10-1000. */ + delay?: number [name: string]: unknown } interface ResolvedOption extends SheetOptions { fileInput: HTMLInputElement + compatibility?: Compatibility + delay?: number [name: string]: unknown } interface ScreenshotPoint { diff --git a/docs/compiled/artplayer-tool-thumbnail.js b/docs/compiled/artplayer-tool-thumbnail.js index 57c5a59b5..75a8c84a8 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,o,n){const r=this.e||(this.e={});let i=e(r,t)?r[t]:void 0;return i||(i=[],Object.defineProperty(r,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:o,ctx:n}),this}once(e,t,o){const n=this,r=t;let i=!1;function s(...t){i||(i=!0,n.off(e,s),r.apply(o,t))}return s._=t,this.on(e,s,o)}emit(t,...o){const n=this.e||(this.e={}),r=(e(n,t)&&n[t]||[]).slice();for(let e=0;en.pause?.(),()=>n.removeAttribute?.("src"),()=>n.load?.(),()=>n.parentNode?.removeChild(n));s([...t.sourceListeners.splice(0),...c,...[...o].map(e=>()=>i(t.sourceUrls,e)),...[...r].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,o){return Math.max(Math.min(e,Math.max(t,o)),Math.min(t,o))}function u(e){const t="workspace-4.4"===e.compatibility;return{delay:t?null:e.delay,aspectHeight:t,resetInput:t}}function d(e,t){let o=t.epoch;const c=e.video;let a,d,h,p,f=!1,m=!1,b=()=>{};const y=[],g=new Promise((e,t)=>{a=e,d=t});g.catch(()=>{});const v=()=>!f&&!t.closed&&t.epoch===o&&t.job===p;function w(){return!f&&(f=!0,t.job===p&&(t.job=null,e.processing=!1),s([()=>clearTimeout(h),b,...y.splice(0)]),!0)}function U(t,o=!0){if(!f){try{w()}catch(n){t||(t=n)}if(o)try{e.emit("error",t?.message)}catch(r){t=r}d(t)}}function E(){if(v())try{w(),e.emit("done"),a()}catch(t){let n=t;try{e.emit("error",t?.message)}catch(o){n=o}d(n)}}function T(t){if(!v())return;m=!0,e.processing=!0;let o=0;const r=()=>{if(!v())return;if(o===t.points.length)return void(null!==t.delay?h=setTimeout(E,2*Number(t.delay)):E());const s=t.points[o];let a=!1,l=!1,u=null===t.delay;const d=c.oncanplay,p=()=>{if(!(!v()||!u||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(b(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!v())return;t.canvas.toBlob(s=>{if(v()&&!l){l=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,o){const r=n(e),s=URL.createObjectURL(t);if(r.thumbnailUrls.add(s),!o())return i(r.thumbnailUrls,s),null;const c=new Set([...r.thumbnailUrls,e.thumbnailUrl]);r.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&i(r.thumbnailUrls,n);return o()?s:null}(e,s,v);if(!c)return;e.emit("update",c,(o+1)/t.number),o++,Promise.resolve().then(r)}catch(c){U(c)}}})}catch(d){U(d)}}};b=()=>{c.oncanplay===p&&(c.oncanplay=d),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,null!==t.delay?(h=setTimeout(()=>{u=!0,p()},t.delay),v()||clearTimeout(h)):c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){U(f)}};Promise.resolve().then(r)}function j(o=!1){if(v()&&!m)if(clearTimeout(h),!t.loading&&c.duration){m=!0;try{const t=function(e,t){const{video:o}=t,{width:n,number:r,begin:i,end:s}=e.option,c=u(e.option),a=c.aspectHeight?o.videoHeight/o.videoWidth*n:e.option.height,d=(o,n)=>{try{e.errorHandle(o,n)}catch(r){throw t.reported=r,t.wasReported=!0,r}};e.option.height=a,e.option.begin=l(i,0,o.duration),e.option.end=l(s||o.duration,i,o.duration),d(e.option.end>e.option.begin,"End time must be greater than the start time"),d(Number.isFinite(o.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=r/e.duration,d(e.file&&o,"Please select the video file first"),d(!e.processing,"There is currently a task in progress, please wait a moment..."),d(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const h=e.creatScreenshotDate(),p=e.creatCanvas(),f=p.getContext("2d");return e.emit("canvas",p),{width:n,height:a,number:r,points:h,canvas:p,context:f,delay:c.delay}}(e,p);T(t)}catch(n){if(U(n,!o&&!(p.wasReported&&n===p.reported)),o)throw n}}else h=setTimeout(()=>j(),1e3),v()||clearTimeout(h)}p={video:c,promise:g,ready:j,fail:U,cancel(e){U(r(e),!1)},adopt:t=>!f&&!m&&c===e.video&&(o=t,!0)},t.job=p;try{for(const[e,t]of[["error",()=>{v()&&U(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>j()],["durationchange",()=>j()]])if(y.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!v())return s([()=>c.removeEventListener(e,t),...y.splice(0)]),g;c.error?U(new Error(`Unable to load video: media error ${c.error.code}`)):j(!0)}catch(k){throw U(k,!1),k}return g}const h=new WeakMap;function p(e){let t;for(const[n,r]of e.listeners.splice(0))try{e.input.removeEventListener(n,r)}catch(o){t||(t=o)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(o){t||(t=o)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function f(e,t){try{for(const[o,n]of t)e.listeners.push([o,n]),e.input.addEventListener(o,n)}catch(o){try{p(e)}catch{}throw o}}function m(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;if(e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element"),function(e,t){t(void 0===e.compatibility||"published-3.5"===e.compatibility||"workspace-4.4"===e.compatibility,"The compatibility option must be published-3.5 or workspace-4.4");const o=u(e).aspectHeight,n=o?["number","width","column","begin","end"]:["delay","number","width","height","column","begin","end"];for(const r of n)t("number"===typeof e[r],`The '${r}' is not a number`);e.number=l(e.number,10,1e3),e.width=l(e.width,10,1e3),e.column=l(e.column,1,1e3),o||(e.delay=l(Number(e.delay),10,1e3),e.height=l(e.height,10,1e3))}(c,(t,o)=>e.errorHandle(t,o)),!s())throw r("input setup superseded");const d=h.get(e);if(d&&(a===d.input||a===d.wrapper))return c.fileInput=d.input,e.option=c,c;const m={input:a,wrapper:null,position:"",listeners:[],callbacks:d?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(m.wrapper=a,m.position=a.style.position,m.input=document.createElement("input"),m.input.type="file",Object.assign(m.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(m.input)),!s())throw r("input setup superseded");if(m.callbacks&&f(m,m.callbacks),!s())throw r("input setup superseded")}catch(b){try{p(m)}catch{}throw b}return c.fileInput=m.input,h.set(e,m),e.option=c,d&&p(d),c}function b(e){const t=h.get(e);t&&(h.delete(e),p(t))}class y extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},y.DEFAULTS,e)),this.video=y.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const o=h.get(e);o.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],f(o,o.callbacks)}(this,y.ondragover)}catch(t){c(this,"construction failed");try{b(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{delay:300,number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||m(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(n(this).closed)return;const{resetInput:t}=u(this.option),o=this.option.fileInput.files[0];this.loadVideo(o),t&&(e.target.value="")}loadVideo(e){!function(e,t){const o=n(e);if(!t||o.closed)return;const r=o.epoch,c=e.video,{delay:a}=u(e.option),l=c.canPlayType(t.type);if(e.errorHandle("maybe"===l||"probably"===l,`Playback of this file format is not supported: ${t.type}`),o.closed||o.epoch!==r)return;const d=URL.createObjectURL(t);if(o.sourceUrls.add(d),o.closed||o.epoch!==r)return void i(o.sourceUrls,d);const h=++o.epoch;!o.sourceUrl&&!e.file&&o.job?.adopt(h)||o.job?.cancel("source changed"),o.loading=!0;const p=()=>!o.closed&&o.epoch===h,f=o.sourceListeners,m=[];o.sourceListeners=m;let b,y=()=>{};const g=()=>clearTimeout(b);m.push(g);let v=!1;const w=()=>{if(!p()||v||c.currentSrc&&c.currentSrc!==d)return;v=!0,g(),o.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);o.job?o.job.fail(t):e.emit("error",t.message)},U=()=>{!p()||c.currentSrc&&c.currentSrc!==d||(o.loading=!1,o.job?.ready())};try{if(s(f.splice(0)),!p())return void i(o.sourceUrls,d);for(const[e,t]of[["error",w],["loadedmetadata",U]])if(y=()=>c.removeEventListener(e,t),m.push(y),c.addEventListener(e,t),!p())return s([y,...m.splice(0)]),void i(o.sourceUrls,d);if(e.videoUrl=d,e.file=t,e.emit("file",t),!p())return void i(o.sourceUrls,d);if(c.src=d,!p())return void i(o.sourceUrls,d);o.sourceUrl=d;for(const e of[...o.sourceUrls])e!==d&&i(o.sourceUrls,e);p()&&!v&&(null!==a?(b=setTimeout(()=>{g(),p()&&!v&&e.emit("video",c)},a),p()&&!v||g()):e.emit("video",c))}catch(E){p()&&(o.loading=!1,o.job?.cancel("source setup failed"));try{s([y,...m.splice(0)])}catch{}throw i(o.sourceUrls,d),E}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),d(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:o,width:n,height:r,column:i,begin:s}=e,c=t/o,a=[s+c];for(;a.length({time:e-c/2,x:t%i*n,y:Math.floor(t/i)*r}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:o,height:n,column:r}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=o*r,i.height=Math.ceil(t/r)*n+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: ${o}, Height: ${n}, Column: ${r}`,10,i.height-11),i}(this.option)}download(){if(n(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 o=document.createElement("a"),n=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;o.download=n,o.href=t;try{document.body.appendChild(o),o.click()}finally{o.parentNode&&o.parentNode.removeChild(o)}return n}(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([()=>b(this),()=>a(this),()=>this.emit("destroy")])}}return y}); diff --git a/docs/compiled/artplayer-tool-thumbnail.legacy.js b/docs/compiled/artplayer-tool-thumbnail.legacy.js index 5907d6760..2586b02ea 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,o){const r=this.e||(this.e={});let i=e(r,t)?r[t]:void 0;return i||(i=[],Object.defineProperty(r,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:o}),this}once(e,t,n){const o=this,r=t;let i=!1;function s(...t){i||(i=!0,o.off(e,s),r.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const o=this.e||(this.e={}),r=(e(o,t)&&o[t]||[]).slice();for(let e=0;e{var e;return null==(e=o.pause)?void 0:e.call(o)},()=>{var e;return null==(e=o.removeAttribute)?void 0:e.call(o,"src")},()=>{var e;return null==(e=o.load)?void 0:e.call(o)},()=>{var e;return null==(e=o.parentNode)?void 0:e.removeChild(o)});s([...t.sourceListeners.splice(0),...l,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...r].map(e=>()=>i(t.thumbnailUrls,e))])}function c(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e){const t="workspace-4.4"===e.compatibility;return{delay:t?null:e.delay,aspectHeight:t,resetInput:t}}function d(e,t){let n=t.epoch;const l=e.video;let a,d,h,p,f=!1,m=!1,b=()=>{};const y=[],v=new Promise((e,t)=>{a=e,d=t});v.catch(()=>{});const g=()=>!f&&!t.closed&&t.epoch===n&&t.job===p;function w(){return!f&&(f=!0,t.job===p&&(t.job=null,e.processing=!1),s([()=>clearTimeout(h),b,...y.splice(0)]),!0)}function U(t,n=!0){if(!f){try{w()}catch(o){t||(t=o)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(r){t=r}d(t)}}function E(){if(g())try{w(),e.emit("done"),a()}catch(t){let o=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){o=n}d(o)}}function T(t){if(!g())return;m=!0,e.processing=!0;let n=0;const r=()=>{if(!g())return;if(n===t.points.length)return void(null!==t.delay?h=setTimeout(E,2*Number(t.delay)):E());const s=t.points[n];let a=!1,c=!1,u=null===t.delay;const d=l.oncanplay,p=()=>{if(!(!g()||!u||a||l.seeking||void 0!==l.readyState&&l.readyState<2)){a=!0;try{if(b(),t.context.drawImage(l,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!c){c=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const l=function(e,t,n){const r=o(e),s=URL.createObjectURL(t);if(r.thumbnailUrls.add(s),!n())return i(r.thumbnailUrls,s),null;const l=new Set([...r.thumbnailUrls,e.thumbnailUrl]);r.thumbnailUrl=s,e.thumbnailUrl=s;for(const o of l)o&&o!==s&&i(r.thumbnailUrls,o);return n()?s:null}(e,s,g);if(!l)return;e.emit("update",l,(n+1)/t.number),n++,Promise.resolve().then(r)}catch(l){U(l)}}})}catch(d){U(d)}}};b=()=>{l.oncanplay===p&&(l.oncanplay=d),l.removeEventListener("seeked",p)};try{l.oncanplay=p,l.addEventListener("seeked",p),l.currentTime=s.time,null!==t.delay?(h=setTimeout(()=>{u=!0,p()},t.delay),g()||clearTimeout(h)):l.readyState>=2&&!l.seeking&&Promise.resolve().then(p)}catch(f){U(f)}};Promise.resolve().then(r)}function j(n=!1){if(g()&&!m)if(clearTimeout(h),!t.loading&&l.duration){m=!0;try{const t=function(e,t){const{video:n}=t,{width:o,number:r,begin:i,end:s}=e.option,l=u(e.option),a=l.aspectHeight?n.videoHeight/n.videoWidth*o:e.option.height,d=(n,o)=>{try{e.errorHandle(n,o)}catch(r){throw t.reported=r,t.wasReported=!0,r}};e.option.height=a,e.option.begin=c(i,0,n.duration),e.option.end=c(s||n.duration,i,n.duration),d(e.option.end>e.option.begin,"End time must be greater than the start time"),d(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=r/e.duration,d(e.file&&n,"Please select the video file first"),d(!e.processing,"There is currently a task in progress, please wait a moment..."),d(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const h=e.creatScreenshotDate(),p=e.creatCanvas(),f=p.getContext("2d");return e.emit("canvas",p),{width:o,height:a,number:r,points:h,canvas:p,context:f,delay:l.delay}}(e,p);T(t)}catch(o){if(U(o,!n&&!(p.wasReported&&o===p.reported)),n)throw o}}else h=setTimeout(()=>j(),1e3),g()||clearTimeout(h)}p={video:l,promise:v,ready:j,fail:U,cancel(e){U(r(e),!1)},adopt:t=>!f&&!m&&l===e.video&&(n=t,!0)},t.job=p;try{for(const[e,t]of[["error",()=>{var e;g()&&U(new Error(`Unable to load video: media error ${(null==(e=l.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>j()],["durationchange",()=>j()]])if(y.push(()=>l.removeEventListener(e,t)),l.addEventListener(e,t),!g())return s([()=>l.removeEventListener(e,t),...y.splice(0)]),v;l.error?U(new Error(`Unable to load video: media error ${l.error.code}`)):j(!0)}catch(k){throw U(k,!1),k}return v}const h=new WeakMap;function p(e){let t;for(const[o,r]of e.listeners.splice(0))try{e.input.removeEventListener(o,r)}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 f(e,t){try{for(const[n,o]of t)e.listeners.push([n,o]),e.input.addEventListener(n,o)}catch(n){try{p(e)}catch(o){}throw n}}function m(e,t){const n=o(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,l=Object.assign({},e.option,t),a=l.fileInput;if(e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element"),function(e,t){t(void 0===e.compatibility||"published-3.5"===e.compatibility||"workspace-4.4"===e.compatibility,"The compatibility option must be published-3.5 or workspace-4.4");const n=u(e).aspectHeight,o=n?["number","width","column","begin","end"]:["delay","number","width","height","column","begin","end"];for(const r of o)t("number"===typeof e[r],`The '${r}' is not a number`);e.number=c(e.number,10,1e3),e.width=c(e.width,10,1e3),e.column=c(e.column,1,1e3),n||(e.delay=c(Number(e.delay),10,1e3),e.height=c(e.height,10,1e3))}(l,(t,n)=>e.errorHandle(t,n)),!s())throw r("input setup superseded");const d=h.get(e);if(d&&(a===d.input||a===d.wrapper))return l.fileInput=d.input,e.option=l,l;const m={input:a,wrapper:null,position:"",listeners:[],callbacks:(null==d?void 0:d.callbacks)||null};try{if("INPUT"===a.tagName&&"file"===a.type||(m.wrapper=a,m.position=a.style.position,m.input=document.createElement("input"),m.input.type="file",Object.assign(m.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(m.input)),!s())throw r("input setup superseded");if(m.callbacks&&f(m,m.callbacks),!s())throw r("input setup superseded")}catch(b){try{p(m)}catch(y){}throw b}return l.fileInput=m.input,h.set(e,m),e.option=l,d&&p(d),l}function b(e){const t=h.get(e);t&&(h.delete(e),p(t))}class y extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},y.DEFAULTS,e)),this.video=y.creatVideo(),o(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=h.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],f(n,n.callbacks)}(this,y.ondragover)}catch(t){l(this,"construction failed");try{b(this)}catch(n){}try{a(this)}catch(n){}throw t}}static get DEFAULTS(){return{delay:300,number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(o(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return o(this).closed||m(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(o(this).closed)return;const{resetInput:t}=u(this.option),n=this.option.fileInput.files[0];this.loadVideo(n),t&&(e.target.value="")}loadVideo(e){!function(e,t){var n,r,l;const a=o(e);if(!t||a.closed)return;const c=a.epoch,d=e.video,{delay:h}=u(e.option),p=d.canPlayType(t.type);if(e.errorHandle("maybe"===p||"probably"===p,`Playback of this file format is not supported: ${t.type}`),a.closed||a.epoch!==c)return;const f=URL.createObjectURL(t);if(a.sourceUrls.add(f),a.closed||a.epoch!==c)return void i(a.sourceUrls,f);const m=++a.epoch;!a.sourceUrl&&!e.file&&(null==(n=a.job)?void 0:n.adopt(m))||null==(r=a.job)||r.cancel("source changed"),a.loading=!0;const b=()=>!a.closed&&a.epoch===m,y=a.sourceListeners,v=[];a.sourceListeners=v;let g,w=()=>{};const U=()=>clearTimeout(g);v.push(U);let E=!1;const T=()=>{var t;if(!b()||E||d.currentSrc&&d.currentSrc!==f)return;E=!0,U(),a.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=d.error)?void 0:t.code)||0}`);a.job?a.job.fail(n):e.emit("error",n.message)},j=()=>{var e;!b()||d.currentSrc&&d.currentSrc!==f||(a.loading=!1,null==(e=a.job)||e.ready())};try{if(s(y.splice(0)),!b())return void i(a.sourceUrls,f);for(const[e,t]of[["error",T],["loadedmetadata",j]])if(w=()=>d.removeEventListener(e,t),v.push(w),d.addEventListener(e,t),!b())return s([w,...v.splice(0)]),void i(a.sourceUrls,f);if(e.videoUrl=f,e.file=t,e.emit("file",t),!b())return void i(a.sourceUrls,f);if(d.src=f,!b())return void i(a.sourceUrls,f);a.sourceUrl=f;for(const e of[...a.sourceUrls])e!==f&&i(a.sourceUrls,e);b()&&!E&&(null!==h?(g=setTimeout(()=>{U(),b()&&!E&&e.emit("video",d)},h),b()&&!E||U()):e.emit("video",d))}catch(k){b()&&(a.loading=!1,null==(l=a.job)||l.cancel("source setup failed"));try{s([w,...v.splice(0)])}catch(L){}throw i(a.sourceUrls,f),k}}(this,e)}start(){return function(e){const t=o(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),d(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:o,height:r,column:i,begin:s}=e,l=t/n,a=[s+l];for(;a.length({time:e-l/2,x:t%i*o,y:Math.floor(t/i)*r}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:o,column:r}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*r,i.height=Math.ceil(t/r)*o+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: ${o}, Column: ${r}`,10,i.height-11),i}(this.option)}download(){if(o(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"),o=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=o,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return o}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){l(this)&&s([()=>b(this),()=>a(this),()=>this.emit("destroy")])}}return y}); diff --git a/docs/compiled/artplayer-tool-thumbnail.mjs b/docs/compiled/artplayer-tool-thumbnail.mjs index bd28deac1..98f93576b 100644 --- a/docs/compiled/artplayer-tool-thumbnail.mjs +++ b/docs/compiled/artplayer-tool-thumbnail.mjs @@ -160,12 +160,39 @@ function releaseMedia(tool) { ...[...thumbnails].map((url) => () => revoke(state.thumbnailUrls, url)) ]); } +function getFileName(name) { + const nameArray = name.split("."); + nameArray.pop(); + return nameArray.join("."); +} +function clamp(num, a, b) { + return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); +} +function thumbnailPolicy(option) { + const workspace = option.compatibility === "workspace-4.4"; + return { delay: workspace ? null : option.delay, aspectHeight: workspace, resetInput: workspace }; +} +function normalizePolicy(option, validate) { + validate(option.compatibility === void 0 || option.compatibility === "published-3.5" || option.compatibility === "workspace-4.4", "The compatibility option must be published-3.5 or workspace-4.4"); + const workspace = thumbnailPolicy(option).aspectHeight; + const fields = workspace ? ["number", "width", "column", "begin", "end"] : ["delay", "number", "width", "height", "column", "begin", "end"]; + for (const name of fields) + validate(typeof option[name] === "number", `The '${name}' is not a number`); + option.number = clamp(option.number, 10, 1e3); + option.width = clamp(option.width, 10, 1e3); + option.column = clamp(option.column, 1, 1e3); + if (!workspace) { + option.delay = clamp(Number(option.delay), 10, 1e3); + option.height = clamp(option.height, 10, 1e3); + } +} function loadSource(tool, file) { const state = stateFor(tool); if (!file || state.closed) return; const initialEpoch = state.epoch; const video = tool.video; + const { delay } = thumbnailPolicy(tool.option); const support = video.canPlayType(file.type); tool.errorHandle(support === "maybe" || support === "probably", `Playback of this file format is not supported: ${file.type}`); if (state.closed || state.epoch !== initialEpoch) @@ -187,11 +214,15 @@ function loadSource(tool, file) { state.sourceListeners = listeners; let pendingCleanup = () => { }; + let notificationTimer; + const cancelNotification = () => clearTimeout(notificationTimer); + listeners.push(cancelNotification); let reported = false; const error = () => { if (!current() || reported || video.currentSrc && video.currentSrc !== url) return; reported = true; + cancelNotification(); state.loading = false; const failure = new Error(`Unable to load video: media error ${video.error?.code || 0}`); if (state.job) @@ -238,8 +269,19 @@ function loadSource(tool, file) { if (previous !== url) revoke(state.sourceUrls, previous); } - if (current()) - tool.emit("video", video); + if (current() && !reported) { + if (delay !== null) { + notificationTimer = setTimeout(() => { + cancelNotification(); + if (current() && !reported) + tool.emit("video", video); + }, delay); + if (!current() || reported) + cancelNotification(); + } else { + tool.emit("video", video); + } + } } catch (error2) { if (current()) { state.loading = false; @@ -270,18 +312,11 @@ function replaceThumbnail(tool, blob, live) { } return live() ? url : null; } -function getFileName(name) { - const nameArray = name.split("."); - nameArray.pop(); - return nameArray.join("."); -} -function clamp(num, a, b) { - return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); -} function prepare(tool, job) { const { video } = job; const { width, number, begin, end } = tool.option; - const height = video.videoHeight / video.videoWidth * width; + const policy = thumbnailPolicy(tool.option); + const height = policy.aspectHeight ? video.videoHeight / video.videoWidth * width : tool.option.height; const guard = (condition, message) => { try { tool.errorHandle(condition, message); @@ -305,7 +340,7 @@ function prepare(tool, job) { const canvas = tool.creatCanvas(); const context = canvas.getContext("2d"); tool.emit("canvas", canvas); - return { width, height, number, points, canvas, context }; + return { width, height, number, points, canvas, context, delay: policy.delay }; } function createJob(tool, state) { let epoch = state.epoch; @@ -381,15 +416,19 @@ function createJob(tool, state) { if (!live()) return; if (index === plan.points.length) { - complete(); + if (plan.delay !== null) + timer = setTimeout(complete, Number(plan.delay) * 2); + else + complete(); return; } const point = plan.points[index]; let drawing = false; let encoded = false; + let delayElapsed = plan.delay === null; const previous = video.oncanplay; const draw = () => { - if (!live() || drawing || video.seeking || video.readyState !== void 0 && video.readyState < 2) + if (!live() || !delayElapsed || drawing || video.seeking || video.readyState !== void 0 && video.readyState < 2) return; drawing = true; try { @@ -427,8 +466,16 @@ function createJob(tool, state) { video.oncanplay = draw; video.addEventListener("seeked", draw); video.currentTime = point.time; - if (video.readyState >= 2 && !video.seeking) + if (plan.delay !== null) { + timer = setTimeout(() => { + delayElapsed = true; + draw(); + }, plan.delay); + if (!live()) + clearTimeout(timer); + } else if (video.readyState >= 2 && !video.seeking) { Promise.resolve().then(draw); + } } catch (error) { fail(error); } @@ -552,11 +599,7 @@ function setupInput(tool, patch) { const option = Object.assign({}, tool.option, patch); const target = option.fileInput; tool.errorHandle(target instanceof Element, "The 'fileInput' is not a Element"); - for (const name of ["number", "width", "column", "begin", "end"]) - tool.errorHandle(typeof option[name] === "number", `The '${name}' is not a number`); - option.number = clamp(option.number, 10, 1e3); - option.width = clamp(option.width, 10, 1e3); - option.column = clamp(option.column, 1, 1e3); + normalizePolicy(option, (condition, message) => tool.errorHandle(condition, message)); if (!current()) throw cancellation("input setup superseded"); const previous = inputs.get(tool); @@ -681,6 +724,7 @@ class ArtplayerToolThumbnail extends Emitter { } static get DEFAULTS() { return { + delay: 300, number: 60, width: 160, height: 90, @@ -723,9 +767,11 @@ class ArtplayerToolThumbnail extends Emitter { inputChange(event) { if (stateFor(this).closed) return; + const { resetInput } = thumbnailPolicy(this.option); const file = this.option.fileInput.files[0]; this.loadVideo(file); - event.target.value = ""; + if (resetInput) + event.target.value = ""; } loadVideo(file) { loadSource(this, file); diff --git a/package.json b/package.json index 6cc141fa9..8f0bc43bc 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-registration.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/dash-cleanup-errors.test.js test/hls-example.test.js test/dash-example.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-registration.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/dash-cleanup-errors.test.js test/hls-example.test.js test/dash-example.test.js test/thumbnail-policy.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-cleanup-errors.test.js", + "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-policy.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-registration.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 622e9dfa6..1011943d9 100644 --- a/packages/artplayer-tool-thumbnail/ARCHITECTURE.md +++ b/packages/artplayer-tool-thumbnail/ARCHITECTURE.md @@ -2,14 +2,13 @@ The runtime responsibilities were separated under PKG-TOOL-THUMB-03 and are now strict TypeScript under PKG-TOOL-THUMB-04. Public declarations and isolated installed -entrypoints now have checks; the historical default-policy decision and final -integration/release gates remain open. +entrypoints now have checks; final integration/release gates remain open. -The pending choice is now specified in the -[compatibility proposal](../../refactor/thumbnail-compatibility-decision.md): +The user approved the +[compatibility decision](../../refactor/thumbnail-compatibility-decision.md) on 2026-09-15: recovered 3.5.31 defaults versus opt-in workspace 4.4.0 behavior, including the -class-level DEFAULTS boundary. It is not approved or implemented; do not infer -permission from the approved VAST or other package decisions. +class-level DEFAULTS boundary. The root constructor now defaults to the published +policy; `compatibility: 'workspace-4.4'` selects the previous workspace behavior. | File | Responsibility | | --- | --- | @@ -18,6 +17,7 @@ permission from the approved VAST or other package decisions. | src/source.ts | File loading, native metadata/error listeners and source/thumbnail Blob URLs | | src/extraction.ts | Owned metadata wait and serial frame job, callback/error completion and cancellation | | src/input.ts | Option validation/clamps, file-input wrapper creation, listener registration/replacement and release | +| src/policy.ts | Approved mode selection and numeric normalization; no DOM or timer ownership | | src/sheet.ts | Midpoint grid, canvas/footer geometry, temporary download anchor | | src/emitter.ts | Typed local adaptation of tiny-emitter; preserves on/once/emit/off behavior | | src/utils.ts | Pure clamp and filename helpers; unused sleep/serial helpers removed | @@ -30,8 +30,8 @@ Input/source use lifecycle state, extraction uses lifecycle/source, and sheet us only filename calculation. types.ts contains no imports or executable state. Input records live in a private WeakMap, so callers replacing `option` cannot lose ownership of generated inputs/listeners. Normal construction preserves the -existing instance-field order and bound inputChange/ondrop methods. DEFAULTS, -file/video event order, public method spelling and return values stay unchanged. +existing instance-field order and bound inputChange/ondrop methods. Public method +spelling and return values stay unchanged. DEFAULTS includes published delay: 300. Input setup validates options before changing DOM or committed options. A wrapper receives one owned input; repeated setup with that wrapper reuses it. Replacement @@ -62,8 +62,9 @@ behavior, checks first-error identity across nested cleanup and verifies resourc ## Extraction and event ordering -Loading emits file before assigning video.src, then video synchronously as in -workspace 4.4.0. A source generation prevents an older file callback or URL creation +Loading emits file before assigning video.src, then video after the configured +delay in the default published policy, or synchronously in workspace mode. +A source generation prevents an older file callback or URL creation hook from overwriting a nested newer load. Successful replacement revokes old source URLs. Native errors report once for their source; stale listeners cannot fail the latest job. Existing sheet URLs stay available until the next sheet frame @@ -76,7 +77,15 @@ Preflight stays synchronous when metadata is ready. Canvas precedes processing=t updates observe true, done observes false and may start another job without old completion clearing the new state. -Frames wait for readiness/seek, accept readiness/Blob callbacks once and restore +Published mode keeps configured height (clamped to 10-1000), waits delay after each +seek and delay * 2 after the last update before done. Delay defaults to 300 and is +clamped to 10-1000. Workspace mode derives height from video aspect ratio, ignores +delay and resets the selected input value; published mode retains that value. +Source notifications and extraction waits own their timers separately, so replacement, +error and destroy cancel both without late events. A null policy delay means no +timer; NaN retains the historical asynchronous browser timer behavior. + +Frames wait for readiness/seek as well as any policy delay, accept readiness/Blob callbacks once and restore oncanplay only while still owning it. Seek/draw/encoding failures, null Blob and throwing callbacks settle the promise and detach job resources. Source replacement or destruction rejects with AbortError without an error event. The public promise @@ -117,14 +126,16 @@ Safari/WebKit evidence in 05. The independent tool example is `test/thumbnail-lifecycle.test.js` covers jobs, source cancellation, native errors, callback failures, reentrancy and private resources. Browser tests hold real PNG callbacks and replace a selected file during encoding; only the latest job may -complete. Continue published declarations and installed consumers in 04; preserve -old tests and the default delay/height boundary. The [public type guide](types/README.md) +complete. Preserve old tests and the approved delay/height boundary. The new +`test/thumbnail-policy.test.js` covers both modes, owned delays and cancellation; +browser extraction checks both policies against the frozen historical bundles. +The [public type guide](types/README.md) documents declaration ownership, module entries, events and installed consumers. See [task plan](../../refactor/plan.md) and [risk ledger](../../refactor/risks.json). ## Type and provenance boundaries -All eight executable source modules and the shared type module are checked with +All nine executable source modules and the shared type module are checked with strict, noUncheckedIndexedAccess, noImplicitOverride, skipLibCheck=false and no ambient Node/test globals. Public fields use declare so TypeScript does not add early undefined properties or change constructor property order. File/URL/density diff --git a/packages/artplayer-tool-thumbnail/README.md b/packages/artplayer-tool-thumbnail/README.md index 11e6cf924..987701b3f 100644 --- a/packages/artplayer-tool-thumbnail/README.md +++ b/packages/artplayer-tool-thumbnail/README.md @@ -4,9 +4,30 @@ Thumbnail tool for ArtPlayer Implementation and migration notes: [ARCHITECTURE.md](./ARCHITECTURE.md). +The default constructor preserves published 3.5.31 behavior: fixed `height: 90`, +`delay: 300` before the video event and each frame, and twice that delay before +completion. Numeric height/delay values are clamped to 10-1000. File selection +retains the input value. Existing methods, including `creatCanvas` and +`creatScreenshotDate`, retain their names. + +Consumers of the unpublished 4.4 workspace behavior should select it explicitly: + +```js +const tool = new ArtplayerToolThumbnail({ + fileInput: document.querySelector('input[type=file]'), + compatibility: 'workspace-4.4', +}) +``` + +This mode calculates height from the video aspect ratio, emits `video` synchronously, +has no fixed extraction delays and clears the file input value. Class-level +`DEFAULTS` always describes the published policy, including `delay`; it does not +vary by instance. Both modes wait for frame readiness and cancel owned work on +source replacement or destruction. + ## Demo -[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-tool-thumbnail/index.js&example=thumbnail) +[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-tool-thumbnail/index.js&example=tool.thumbnail) ## License diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js index 57c5a59b5..75a8c84a8 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,o,n){const r=this.e||(this.e={});let i=e(r,t)?r[t]:void 0;return i||(i=[],Object.defineProperty(r,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:o,ctx:n}),this}once(e,t,o){const n=this,r=t;let i=!1;function s(...t){i||(i=!0,n.off(e,s),r.apply(o,t))}return s._=t,this.on(e,s,o)}emit(t,...o){const n=this.e||(this.e={}),r=(e(n,t)&&n[t]||[]).slice();for(let e=0;en.pause?.(),()=>n.removeAttribute?.("src"),()=>n.load?.(),()=>n.parentNode?.removeChild(n));s([...t.sourceListeners.splice(0),...c,...[...o].map(e=>()=>i(t.sourceUrls,e)),...[...r].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,o){return Math.max(Math.min(e,Math.max(t,o)),Math.min(t,o))}function u(e){const t="workspace-4.4"===e.compatibility;return{delay:t?null:e.delay,aspectHeight:t,resetInput:t}}function d(e,t){let o=t.epoch;const c=e.video;let a,d,h,p,f=!1,m=!1,b=()=>{};const y=[],g=new Promise((e,t)=>{a=e,d=t});g.catch(()=>{});const v=()=>!f&&!t.closed&&t.epoch===o&&t.job===p;function w(){return!f&&(f=!0,t.job===p&&(t.job=null,e.processing=!1),s([()=>clearTimeout(h),b,...y.splice(0)]),!0)}function U(t,o=!0){if(!f){try{w()}catch(n){t||(t=n)}if(o)try{e.emit("error",t?.message)}catch(r){t=r}d(t)}}function E(){if(v())try{w(),e.emit("done"),a()}catch(t){let n=t;try{e.emit("error",t?.message)}catch(o){n=o}d(n)}}function T(t){if(!v())return;m=!0,e.processing=!0;let o=0;const r=()=>{if(!v())return;if(o===t.points.length)return void(null!==t.delay?h=setTimeout(E,2*Number(t.delay)):E());const s=t.points[o];let a=!1,l=!1,u=null===t.delay;const d=c.oncanplay,p=()=>{if(!(!v()||!u||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(b(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!v())return;t.canvas.toBlob(s=>{if(v()&&!l){l=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,o){const r=n(e),s=URL.createObjectURL(t);if(r.thumbnailUrls.add(s),!o())return i(r.thumbnailUrls,s),null;const c=new Set([...r.thumbnailUrls,e.thumbnailUrl]);r.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&i(r.thumbnailUrls,n);return o()?s:null}(e,s,v);if(!c)return;e.emit("update",c,(o+1)/t.number),o++,Promise.resolve().then(r)}catch(c){U(c)}}})}catch(d){U(d)}}};b=()=>{c.oncanplay===p&&(c.oncanplay=d),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,null!==t.delay?(h=setTimeout(()=>{u=!0,p()},t.delay),v()||clearTimeout(h)):c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){U(f)}};Promise.resolve().then(r)}function j(o=!1){if(v()&&!m)if(clearTimeout(h),!t.loading&&c.duration){m=!0;try{const t=function(e,t){const{video:o}=t,{width:n,number:r,begin:i,end:s}=e.option,c=u(e.option),a=c.aspectHeight?o.videoHeight/o.videoWidth*n:e.option.height,d=(o,n)=>{try{e.errorHandle(o,n)}catch(r){throw t.reported=r,t.wasReported=!0,r}};e.option.height=a,e.option.begin=l(i,0,o.duration),e.option.end=l(s||o.duration,i,o.duration),d(e.option.end>e.option.begin,"End time must be greater than the start time"),d(Number.isFinite(o.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=r/e.duration,d(e.file&&o,"Please select the video file first"),d(!e.processing,"There is currently a task in progress, please wait a moment..."),d(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const h=e.creatScreenshotDate(),p=e.creatCanvas(),f=p.getContext("2d");return e.emit("canvas",p),{width:n,height:a,number:r,points:h,canvas:p,context:f,delay:c.delay}}(e,p);T(t)}catch(n){if(U(n,!o&&!(p.wasReported&&n===p.reported)),o)throw n}}else h=setTimeout(()=>j(),1e3),v()||clearTimeout(h)}p={video:c,promise:g,ready:j,fail:U,cancel(e){U(r(e),!1)},adopt:t=>!f&&!m&&c===e.video&&(o=t,!0)},t.job=p;try{for(const[e,t]of[["error",()=>{v()&&U(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>j()],["durationchange",()=>j()]])if(y.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!v())return s([()=>c.removeEventListener(e,t),...y.splice(0)]),g;c.error?U(new Error(`Unable to load video: media error ${c.error.code}`)):j(!0)}catch(k){throw U(k,!1),k}return g}const h=new WeakMap;function p(e){let t;for(const[n,r]of e.listeners.splice(0))try{e.input.removeEventListener(n,r)}catch(o){t||(t=o)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(o){t||(t=o)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function f(e,t){try{for(const[o,n]of t)e.listeners.push([o,n]),e.input.addEventListener(o,n)}catch(o){try{p(e)}catch{}throw o}}function m(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;if(e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element"),function(e,t){t(void 0===e.compatibility||"published-3.5"===e.compatibility||"workspace-4.4"===e.compatibility,"The compatibility option must be published-3.5 or workspace-4.4");const o=u(e).aspectHeight,n=o?["number","width","column","begin","end"]:["delay","number","width","height","column","begin","end"];for(const r of n)t("number"===typeof e[r],`The '${r}' is not a number`);e.number=l(e.number,10,1e3),e.width=l(e.width,10,1e3),e.column=l(e.column,1,1e3),o||(e.delay=l(Number(e.delay),10,1e3),e.height=l(e.height,10,1e3))}(c,(t,o)=>e.errorHandle(t,o)),!s())throw r("input setup superseded");const d=h.get(e);if(d&&(a===d.input||a===d.wrapper))return c.fileInput=d.input,e.option=c,c;const m={input:a,wrapper:null,position:"",listeners:[],callbacks:d?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(m.wrapper=a,m.position=a.style.position,m.input=document.createElement("input"),m.input.type="file",Object.assign(m.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(m.input)),!s())throw r("input setup superseded");if(m.callbacks&&f(m,m.callbacks),!s())throw r("input setup superseded")}catch(b){try{p(m)}catch{}throw b}return c.fileInput=m.input,h.set(e,m),e.option=c,d&&p(d),c}function b(e){const t=h.get(e);t&&(h.delete(e),p(t))}class y extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},y.DEFAULTS,e)),this.video=y.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const o=h.get(e);o.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],f(o,o.callbacks)}(this,y.ondragover)}catch(t){c(this,"construction failed");try{b(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{delay:300,number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||m(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(n(this).closed)return;const{resetInput:t}=u(this.option),o=this.option.fileInput.files[0];this.loadVideo(o),t&&(e.target.value="")}loadVideo(e){!function(e,t){const o=n(e);if(!t||o.closed)return;const r=o.epoch,c=e.video,{delay:a}=u(e.option),l=c.canPlayType(t.type);if(e.errorHandle("maybe"===l||"probably"===l,`Playback of this file format is not supported: ${t.type}`),o.closed||o.epoch!==r)return;const d=URL.createObjectURL(t);if(o.sourceUrls.add(d),o.closed||o.epoch!==r)return void i(o.sourceUrls,d);const h=++o.epoch;!o.sourceUrl&&!e.file&&o.job?.adopt(h)||o.job?.cancel("source changed"),o.loading=!0;const p=()=>!o.closed&&o.epoch===h,f=o.sourceListeners,m=[];o.sourceListeners=m;let b,y=()=>{};const g=()=>clearTimeout(b);m.push(g);let v=!1;const w=()=>{if(!p()||v||c.currentSrc&&c.currentSrc!==d)return;v=!0,g(),o.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);o.job?o.job.fail(t):e.emit("error",t.message)},U=()=>{!p()||c.currentSrc&&c.currentSrc!==d||(o.loading=!1,o.job?.ready())};try{if(s(f.splice(0)),!p())return void i(o.sourceUrls,d);for(const[e,t]of[["error",w],["loadedmetadata",U]])if(y=()=>c.removeEventListener(e,t),m.push(y),c.addEventListener(e,t),!p())return s([y,...m.splice(0)]),void i(o.sourceUrls,d);if(e.videoUrl=d,e.file=t,e.emit("file",t),!p())return void i(o.sourceUrls,d);if(c.src=d,!p())return void i(o.sourceUrls,d);o.sourceUrl=d;for(const e of[...o.sourceUrls])e!==d&&i(o.sourceUrls,e);p()&&!v&&(null!==a?(b=setTimeout(()=>{g(),p()&&!v&&e.emit("video",c)},a),p()&&!v||g()):e.emit("video",c))}catch(E){p()&&(o.loading=!1,o.job?.cancel("source setup failed"));try{s([y,...m.splice(0)])}catch{}throw i(o.sourceUrls,d),E}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),d(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:o,width:n,height:r,column:i,begin:s}=e,c=t/o,a=[s+c];for(;a.length({time:e-c/2,x:t%i*n,y:Math.floor(t/i)*r}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:o,height:n,column:r}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=o*r,i.height=Math.ceil(t/r)*n+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: ${o}, Height: ${n}, Column: ${r}`,10,i.height-11),i}(this.option)}download(){if(n(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 o=document.createElement("a"),n=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;o.download=n,o.href=t;try{document.body.appendChild(o),o.click()}finally{o.parentNode&&o.parentNode.removeChild(o)}return n}(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([()=>b(this),()=>a(this),()=>this.emit("destroy")])}}return y}); 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 5907d6760..2586b02ea 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,o){const r=this.e||(this.e={});let i=e(r,t)?r[t]:void 0;return i||(i=[],Object.defineProperty(r,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:o}),this}once(e,t,n){const o=this,r=t;let i=!1;function s(...t){i||(i=!0,o.off(e,s),r.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const o=this.e||(this.e={}),r=(e(o,t)&&o[t]||[]).slice();for(let e=0;e{var e;return null==(e=o.pause)?void 0:e.call(o)},()=>{var e;return null==(e=o.removeAttribute)?void 0:e.call(o,"src")},()=>{var e;return null==(e=o.load)?void 0:e.call(o)},()=>{var e;return null==(e=o.parentNode)?void 0:e.removeChild(o)});s([...t.sourceListeners.splice(0),...l,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...r].map(e=>()=>i(t.thumbnailUrls,e))])}function c(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e){const t="workspace-4.4"===e.compatibility;return{delay:t?null:e.delay,aspectHeight:t,resetInput:t}}function d(e,t){let n=t.epoch;const l=e.video;let a,d,h,p,f=!1,m=!1,b=()=>{};const y=[],v=new Promise((e,t)=>{a=e,d=t});v.catch(()=>{});const g=()=>!f&&!t.closed&&t.epoch===n&&t.job===p;function w(){return!f&&(f=!0,t.job===p&&(t.job=null,e.processing=!1),s([()=>clearTimeout(h),b,...y.splice(0)]),!0)}function U(t,n=!0){if(!f){try{w()}catch(o){t||(t=o)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(r){t=r}d(t)}}function E(){if(g())try{w(),e.emit("done"),a()}catch(t){let o=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){o=n}d(o)}}function T(t){if(!g())return;m=!0,e.processing=!0;let n=0;const r=()=>{if(!g())return;if(n===t.points.length)return void(null!==t.delay?h=setTimeout(E,2*Number(t.delay)):E());const s=t.points[n];let a=!1,c=!1,u=null===t.delay;const d=l.oncanplay,p=()=>{if(!(!g()||!u||a||l.seeking||void 0!==l.readyState&&l.readyState<2)){a=!0;try{if(b(),t.context.drawImage(l,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!c){c=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const l=function(e,t,n){const r=o(e),s=URL.createObjectURL(t);if(r.thumbnailUrls.add(s),!n())return i(r.thumbnailUrls,s),null;const l=new Set([...r.thumbnailUrls,e.thumbnailUrl]);r.thumbnailUrl=s,e.thumbnailUrl=s;for(const o of l)o&&o!==s&&i(r.thumbnailUrls,o);return n()?s:null}(e,s,g);if(!l)return;e.emit("update",l,(n+1)/t.number),n++,Promise.resolve().then(r)}catch(l){U(l)}}})}catch(d){U(d)}}};b=()=>{l.oncanplay===p&&(l.oncanplay=d),l.removeEventListener("seeked",p)};try{l.oncanplay=p,l.addEventListener("seeked",p),l.currentTime=s.time,null!==t.delay?(h=setTimeout(()=>{u=!0,p()},t.delay),g()||clearTimeout(h)):l.readyState>=2&&!l.seeking&&Promise.resolve().then(p)}catch(f){U(f)}};Promise.resolve().then(r)}function j(n=!1){if(g()&&!m)if(clearTimeout(h),!t.loading&&l.duration){m=!0;try{const t=function(e,t){const{video:n}=t,{width:o,number:r,begin:i,end:s}=e.option,l=u(e.option),a=l.aspectHeight?n.videoHeight/n.videoWidth*o:e.option.height,d=(n,o)=>{try{e.errorHandle(n,o)}catch(r){throw t.reported=r,t.wasReported=!0,r}};e.option.height=a,e.option.begin=c(i,0,n.duration),e.option.end=c(s||n.duration,i,n.duration),d(e.option.end>e.option.begin,"End time must be greater than the start time"),d(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=r/e.duration,d(e.file&&n,"Please select the video file first"),d(!e.processing,"There is currently a task in progress, please wait a moment..."),d(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const h=e.creatScreenshotDate(),p=e.creatCanvas(),f=p.getContext("2d");return e.emit("canvas",p),{width:o,height:a,number:r,points:h,canvas:p,context:f,delay:l.delay}}(e,p);T(t)}catch(o){if(U(o,!n&&!(p.wasReported&&o===p.reported)),n)throw o}}else h=setTimeout(()=>j(),1e3),g()||clearTimeout(h)}p={video:l,promise:v,ready:j,fail:U,cancel(e){U(r(e),!1)},adopt:t=>!f&&!m&&l===e.video&&(n=t,!0)},t.job=p;try{for(const[e,t]of[["error",()=>{var e;g()&&U(new Error(`Unable to load video: media error ${(null==(e=l.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>j()],["durationchange",()=>j()]])if(y.push(()=>l.removeEventListener(e,t)),l.addEventListener(e,t),!g())return s([()=>l.removeEventListener(e,t),...y.splice(0)]),v;l.error?U(new Error(`Unable to load video: media error ${l.error.code}`)):j(!0)}catch(k){throw U(k,!1),k}return v}const h=new WeakMap;function p(e){let t;for(const[o,r]of e.listeners.splice(0))try{e.input.removeEventListener(o,r)}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 f(e,t){try{for(const[n,o]of t)e.listeners.push([n,o]),e.input.addEventListener(n,o)}catch(n){try{p(e)}catch(o){}throw n}}function m(e,t){const n=o(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,l=Object.assign({},e.option,t),a=l.fileInput;if(e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element"),function(e,t){t(void 0===e.compatibility||"published-3.5"===e.compatibility||"workspace-4.4"===e.compatibility,"The compatibility option must be published-3.5 or workspace-4.4");const n=u(e).aspectHeight,o=n?["number","width","column","begin","end"]:["delay","number","width","height","column","begin","end"];for(const r of o)t("number"===typeof e[r],`The '${r}' is not a number`);e.number=c(e.number,10,1e3),e.width=c(e.width,10,1e3),e.column=c(e.column,1,1e3),n||(e.delay=c(Number(e.delay),10,1e3),e.height=c(e.height,10,1e3))}(l,(t,n)=>e.errorHandle(t,n)),!s())throw r("input setup superseded");const d=h.get(e);if(d&&(a===d.input||a===d.wrapper))return l.fileInput=d.input,e.option=l,l;const m={input:a,wrapper:null,position:"",listeners:[],callbacks:(null==d?void 0:d.callbacks)||null};try{if("INPUT"===a.tagName&&"file"===a.type||(m.wrapper=a,m.position=a.style.position,m.input=document.createElement("input"),m.input.type="file",Object.assign(m.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(m.input)),!s())throw r("input setup superseded");if(m.callbacks&&f(m,m.callbacks),!s())throw r("input setup superseded")}catch(b){try{p(m)}catch(y){}throw b}return l.fileInput=m.input,h.set(e,m),e.option=l,d&&p(d),l}function b(e){const t=h.get(e);t&&(h.delete(e),p(t))}class y extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},y.DEFAULTS,e)),this.video=y.creatVideo(),o(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=h.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],f(n,n.callbacks)}(this,y.ondragover)}catch(t){l(this,"construction failed");try{b(this)}catch(n){}try{a(this)}catch(n){}throw t}}static get DEFAULTS(){return{delay:300,number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(o(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return o(this).closed||m(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(o(this).closed)return;const{resetInput:t}=u(this.option),n=this.option.fileInput.files[0];this.loadVideo(n),t&&(e.target.value="")}loadVideo(e){!function(e,t){var n,r,l;const a=o(e);if(!t||a.closed)return;const c=a.epoch,d=e.video,{delay:h}=u(e.option),p=d.canPlayType(t.type);if(e.errorHandle("maybe"===p||"probably"===p,`Playback of this file format is not supported: ${t.type}`),a.closed||a.epoch!==c)return;const f=URL.createObjectURL(t);if(a.sourceUrls.add(f),a.closed||a.epoch!==c)return void i(a.sourceUrls,f);const m=++a.epoch;!a.sourceUrl&&!e.file&&(null==(n=a.job)?void 0:n.adopt(m))||null==(r=a.job)||r.cancel("source changed"),a.loading=!0;const b=()=>!a.closed&&a.epoch===m,y=a.sourceListeners,v=[];a.sourceListeners=v;let g,w=()=>{};const U=()=>clearTimeout(g);v.push(U);let E=!1;const T=()=>{var t;if(!b()||E||d.currentSrc&&d.currentSrc!==f)return;E=!0,U(),a.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=d.error)?void 0:t.code)||0}`);a.job?a.job.fail(n):e.emit("error",n.message)},j=()=>{var e;!b()||d.currentSrc&&d.currentSrc!==f||(a.loading=!1,null==(e=a.job)||e.ready())};try{if(s(y.splice(0)),!b())return void i(a.sourceUrls,f);for(const[e,t]of[["error",T],["loadedmetadata",j]])if(w=()=>d.removeEventListener(e,t),v.push(w),d.addEventListener(e,t),!b())return s([w,...v.splice(0)]),void i(a.sourceUrls,f);if(e.videoUrl=f,e.file=t,e.emit("file",t),!b())return void i(a.sourceUrls,f);if(d.src=f,!b())return void i(a.sourceUrls,f);a.sourceUrl=f;for(const e of[...a.sourceUrls])e!==f&&i(a.sourceUrls,e);b()&&!E&&(null!==h?(g=setTimeout(()=>{U(),b()&&!E&&e.emit("video",d)},h),b()&&!E||U()):e.emit("video",d))}catch(k){b()&&(a.loading=!1,null==(l=a.job)||l.cancel("source setup failed"));try{s([w,...v.splice(0)])}catch(L){}throw i(a.sourceUrls,f),k}}(this,e)}start(){return function(e){const t=o(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),d(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:o,height:r,column:i,begin:s}=e,l=t/n,a=[s+l];for(;a.length({time:e-l/2,x:t%i*o,y:Math.floor(t/i)*r}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:o,column:r}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*r,i.height=Math.ceil(t/r)*o+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: ${o}, Column: ${r}`,10,i.height-11),i}(this.option)}download(){if(o(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"),o=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=o,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return o}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){l(this)&&s([()=>b(this),()=>a(this),()=>this.emit("destroy")])}}return y}); diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs index bd28deac1..98f93576b 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs @@ -160,12 +160,39 @@ function releaseMedia(tool) { ...[...thumbnails].map((url) => () => revoke(state.thumbnailUrls, url)) ]); } +function getFileName(name) { + const nameArray = name.split("."); + nameArray.pop(); + return nameArray.join("."); +} +function clamp(num, a, b) { + return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); +} +function thumbnailPolicy(option) { + const workspace = option.compatibility === "workspace-4.4"; + return { delay: workspace ? null : option.delay, aspectHeight: workspace, resetInput: workspace }; +} +function normalizePolicy(option, validate) { + validate(option.compatibility === void 0 || option.compatibility === "published-3.5" || option.compatibility === "workspace-4.4", "The compatibility option must be published-3.5 or workspace-4.4"); + const workspace = thumbnailPolicy(option).aspectHeight; + const fields = workspace ? ["number", "width", "column", "begin", "end"] : ["delay", "number", "width", "height", "column", "begin", "end"]; + for (const name of fields) + validate(typeof option[name] === "number", `The '${name}' is not a number`); + option.number = clamp(option.number, 10, 1e3); + option.width = clamp(option.width, 10, 1e3); + option.column = clamp(option.column, 1, 1e3); + if (!workspace) { + option.delay = clamp(Number(option.delay), 10, 1e3); + option.height = clamp(option.height, 10, 1e3); + } +} function loadSource(tool, file) { const state = stateFor(tool); if (!file || state.closed) return; const initialEpoch = state.epoch; const video = tool.video; + const { delay } = thumbnailPolicy(tool.option); const support = video.canPlayType(file.type); tool.errorHandle(support === "maybe" || support === "probably", `Playback of this file format is not supported: ${file.type}`); if (state.closed || state.epoch !== initialEpoch) @@ -187,11 +214,15 @@ function loadSource(tool, file) { state.sourceListeners = listeners; let pendingCleanup = () => { }; + let notificationTimer; + const cancelNotification = () => clearTimeout(notificationTimer); + listeners.push(cancelNotification); let reported = false; const error = () => { if (!current() || reported || video.currentSrc && video.currentSrc !== url) return; reported = true; + cancelNotification(); state.loading = false; const failure = new Error(`Unable to load video: media error ${video.error?.code || 0}`); if (state.job) @@ -238,8 +269,19 @@ function loadSource(tool, file) { if (previous !== url) revoke(state.sourceUrls, previous); } - if (current()) - tool.emit("video", video); + if (current() && !reported) { + if (delay !== null) { + notificationTimer = setTimeout(() => { + cancelNotification(); + if (current() && !reported) + tool.emit("video", video); + }, delay); + if (!current() || reported) + cancelNotification(); + } else { + tool.emit("video", video); + } + } } catch (error2) { if (current()) { state.loading = false; @@ -270,18 +312,11 @@ function replaceThumbnail(tool, blob, live) { } return live() ? url : null; } -function getFileName(name) { - const nameArray = name.split("."); - nameArray.pop(); - return nameArray.join("."); -} -function clamp(num, a, b) { - return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); -} function prepare(tool, job) { const { video } = job; const { width, number, begin, end } = tool.option; - const height = video.videoHeight / video.videoWidth * width; + const policy = thumbnailPolicy(tool.option); + const height = policy.aspectHeight ? video.videoHeight / video.videoWidth * width : tool.option.height; const guard = (condition, message) => { try { tool.errorHandle(condition, message); @@ -305,7 +340,7 @@ function prepare(tool, job) { const canvas = tool.creatCanvas(); const context = canvas.getContext("2d"); tool.emit("canvas", canvas); - return { width, height, number, points, canvas, context }; + return { width, height, number, points, canvas, context, delay: policy.delay }; } function createJob(tool, state) { let epoch = state.epoch; @@ -381,15 +416,19 @@ function createJob(tool, state) { if (!live()) return; if (index === plan.points.length) { - complete(); + if (plan.delay !== null) + timer = setTimeout(complete, Number(plan.delay) * 2); + else + complete(); return; } const point = plan.points[index]; let drawing = false; let encoded = false; + let delayElapsed = plan.delay === null; const previous = video.oncanplay; const draw = () => { - if (!live() || drawing || video.seeking || video.readyState !== void 0 && video.readyState < 2) + if (!live() || !delayElapsed || drawing || video.seeking || video.readyState !== void 0 && video.readyState < 2) return; drawing = true; try { @@ -427,8 +466,16 @@ function createJob(tool, state) { video.oncanplay = draw; video.addEventListener("seeked", draw); video.currentTime = point.time; - if (video.readyState >= 2 && !video.seeking) + if (plan.delay !== null) { + timer = setTimeout(() => { + delayElapsed = true; + draw(); + }, plan.delay); + if (!live()) + clearTimeout(timer); + } else if (video.readyState >= 2 && !video.seeking) { Promise.resolve().then(draw); + } } catch (error) { fail(error); } @@ -552,11 +599,7 @@ function setupInput(tool, patch) { const option = Object.assign({}, tool.option, patch); const target = option.fileInput; tool.errorHandle(target instanceof Element, "The 'fileInput' is not a Element"); - for (const name of ["number", "width", "column", "begin", "end"]) - tool.errorHandle(typeof option[name] === "number", `The '${name}' is not a number`); - option.number = clamp(option.number, 10, 1e3); - option.width = clamp(option.width, 10, 1e3); - option.column = clamp(option.column, 1, 1e3); + normalizePolicy(option, (condition, message) => tool.errorHandle(condition, message)); if (!current()) throw cancellation("input setup superseded"); const previous = inputs.get(tool); @@ -681,6 +724,7 @@ class ArtplayerToolThumbnail extends Emitter { } static get DEFAULTS() { return { + delay: 300, number: 60, width: 160, height: 90, @@ -723,9 +767,11 @@ class ArtplayerToolThumbnail extends Emitter { inputChange(event) { if (stateFor(this).closed) return; + const { resetInput } = thumbnailPolicy(this.option); const file = this.option.fileInput.files[0]; this.loadVideo(file); - event.target.value = ""; + if (resetInput) + event.target.value = ""; } loadVideo(file) { loadSource(this, file); diff --git a/packages/artplayer-tool-thumbnail/src/extraction.ts b/packages/artplayer-tool-thumbnail/src/extraction.ts index 2b804d8bd..803d9418e 100644 --- a/packages/artplayer-tool-thumbnail/src/extraction.ts +++ b/packages/artplayer-tool-thumbnail/src/extraction.ts @@ -1,13 +1,15 @@ import type ArtplayerToolThumbnail from './index' import type { Cleanup, ExtractionJob, LifecycleState } from './types' import { cancellation, cleanupAll, stateFor } from './lifecycle' +import { thumbnailPolicy } from './policy' import { replaceThumbnail } from './source' import { clamp } from './utils' function prepare(tool: ArtplayerToolThumbnail, job: ExtractionJob) { const { video } = job const { width, number, begin, end } = tool.option - const height = (video.videoHeight / video.videoWidth) * width + const policy = thumbnailPolicy(tool.option) + const height = policy.aspectHeight ? (video.videoHeight / video.videoWidth) * width : tool.option.height const guard = (condition: unknown, message: string) => { try { tool.errorHandle(condition, message) @@ -32,7 +34,7 @@ function prepare(tool: ArtplayerToolThumbnail, job: ExtractionJob) { const canvas = tool.creatCanvas() const context = canvas.getContext('2d')! tool.emit('canvas', canvas) - return { width, height, number, points, canvas, context } + return { width, height, number, points, canvas, context, delay: policy.delay } } function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) { @@ -112,15 +114,19 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) { if (!live()) return if (index === plan.points.length) { - complete() + if (plan.delay !== null) + timer = setTimeout(complete, Number(plan.delay) * 2) + else + complete() return } const point = plan.points[index]! let drawing = false let encoded = false + let delayElapsed = plan.delay === null const previous = video.oncanplay const draw = () => { - if (!live() || drawing || video.seeking || (video.readyState !== undefined && video.readyState < 2)) + if (!live() || !delayElapsed || drawing || video.seeking || (video.readyState !== undefined && video.readyState < 2)) return drawing = true try { @@ -160,8 +166,17 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) { video.oncanplay = draw video.addEventListener('seeked', draw) video.currentTime = point.time - if (video.readyState >= 2 && !video.seeking) + if (plan.delay !== null) { + timer = setTimeout(() => { + delayElapsed = true + draw() + }, plan.delay) + if (!live()) + clearTimeout(timer) + } + else if (video.readyState >= 2 && !video.seeking) { Promise.resolve().then(draw) + } } catch (error) { fail(error) diff --git a/packages/artplayer-tool-thumbnail/src/index.ts b/packages/artplayer-tool-thumbnail/src/index.ts index 156814c12..ed6e0b372 100644 --- a/packages/artplayer-tool-thumbnail/src/index.ts +++ b/packages/artplayer-tool-thumbnail/src/index.ts @@ -3,6 +3,7 @@ import Emitter from './emitter' import { startExtraction } from './extraction' import { connectInput, releaseInput, setupInput } from './input' import { cleanupAll, closeState, releaseMedia, stateFor } from './lifecycle' +import { thumbnailPolicy } from './policy' import { createSheet, downloadSheet, screenshotPoints } from './sheet' import { loadSource } from './source' @@ -46,6 +47,7 @@ export default class ArtplayerToolThumbnail extends Emitter { static get DEFAULTS() { return { + delay: 300, number: 60, width: 160, height: 90, @@ -94,9 +96,11 @@ export default class ArtplayerToolThumbnail extends Emitter { inputChange(event: Event) { if (stateFor(this).closed) return + const { resetInput } = thumbnailPolicy(this.option) const file = this.option.fileInput.files![0] this.loadVideo(file) - ;(event.target as HTMLInputElement).value = '' + if (resetInput) + (event.target as HTMLInputElement).value = '' } loadVideo(file?: File | null) { diff --git a/packages/artplayer-tool-thumbnail/src/input.ts b/packages/artplayer-tool-thumbnail/src/input.ts index adf7b707b..c0f427425 100644 --- a/packages/artplayer-tool-thumbnail/src/input.ts +++ b/packages/artplayer-tool-thumbnail/src/input.ts @@ -1,7 +1,7 @@ import type ArtplayerToolThumbnail from './index' import type { InputOptions } from './types' import { cancellation, stateFor } from './lifecycle' -import { clamp } from './utils' +import { normalizePolicy } from './policy' type Registration = readonly [name: string, callback: EventListener] interface InputRecord { @@ -62,11 +62,7 @@ export function setupInput(tool: ArtplayerToolThumbnail, patch: InputOptions) { const option = Object.assign({}, tool.option, patch) const target = option.fileInput! tool.errorHandle(target instanceof Element, 'The \'fileInput\' is not a Element') - for (const name of ['number', 'width', 'column', 'begin', 'end']) - tool.errorHandle(typeof option[name] === 'number', `The '${name}' is not a number`) - option.number = clamp(option.number, 10, 1000) - option.width = clamp(option.width, 10, 1000) - option.column = clamp(option.column, 1, 1000) + normalizePolicy(option, (condition, message) => tool.errorHandle(condition, message)) if (!current()) throw cancellation('input setup superseded') diff --git a/packages/artplayer-tool-thumbnail/src/policy.ts b/packages/artplayer-tool-thumbnail/src/policy.ts new file mode 100644 index 000000000..c9b61efde --- /dev/null +++ b/packages/artplayer-tool-thumbnail/src/policy.ts @@ -0,0 +1,22 @@ +import type { RuntimeOptions } from './types' +import { clamp } from './utils' + +export function thumbnailPolicy(option: RuntimeOptions) { + const workspace = option.compatibility === 'workspace-4.4' + return { delay: workspace ? null : option.delay, aspectHeight: workspace, resetInput: workspace } +} + +export function normalizePolicy(option: RuntimeOptions, validate: (condition: unknown, message: string) => void) { + validate(option.compatibility === undefined || option.compatibility === 'published-3.5' || option.compatibility === 'workspace-4.4', 'The compatibility option must be published-3.5 or workspace-4.4') + const workspace = thumbnailPolicy(option).aspectHeight + const fields = workspace ? ['number', 'width', 'column', 'begin', 'end'] : ['delay', 'number', 'width', 'height', 'column', 'begin', 'end'] + for (const name of fields) + validate(typeof option[name] === 'number', `The '${name}' is not a number`) + option.number = clamp(option.number, 10, 1000) + option.width = clamp(option.width, 10, 1000) + option.column = clamp(option.column, 1, 1000) + if (!workspace) { + option.delay = clamp(Number(option.delay), 10, 1000) + option.height = clamp(option.height, 10, 1000) + } +} diff --git a/packages/artplayer-tool-thumbnail/src/source.ts b/packages/artplayer-tool-thumbnail/src/source.ts index 16773212d..32272fb8c 100644 --- a/packages/artplayer-tool-thumbnail/src/source.ts +++ b/packages/artplayer-tool-thumbnail/src/source.ts @@ -1,6 +1,7 @@ import type ArtplayerToolThumbnail from './index' import type { Cleanup } from './types' import { cleanupAll, revoke, stateFor } from './lifecycle' +import { thumbnailPolicy } from './policy' export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) { const state = stateFor(tool) @@ -8,6 +9,7 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) { return const initialEpoch = state.epoch const video = tool.video + const { delay } = thumbnailPolicy(tool.option) const support = video.canPlayType(file.type) tool.errorHandle(support === 'maybe' || support === 'probably', `Playback of this file format is not supported: ${file.type}`) if (state.closed || state.epoch !== initialEpoch) @@ -28,11 +30,15 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) { const listeners: Cleanup[] = [] state.sourceListeners = listeners let pendingCleanup = () => {} + let notificationTimer: ReturnType | undefined + const cancelNotification = () => clearTimeout(notificationTimer) + listeners.push(cancelNotification) let reported = false const error = () => { if (!current() || reported || (video.currentSrc && video.currentSrc !== url)) return reported = true + cancelNotification() state.loading = false const failure = new Error(`Unable to load video: media error ${video.error?.code || 0}`) if (state.job) @@ -79,8 +85,20 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) { if (previous !== url) revoke(state.sourceUrls, previous) } - if (current()) - tool.emit('video', video) + if (current() && !reported) { + if (delay !== null) { + notificationTimer = setTimeout(() => { + cancelNotification() + if (current() && !reported) + tool.emit('video', video) + }, delay) + if (!current() || reported) + cancelNotification() + } + else { + tool.emit('video', video) + } + } } catch (error) { if (current()) { diff --git a/packages/artplayer-tool-thumbnail/src/types.ts b/packages/artplayer-tool-thumbnail/src/types.ts index 09d832aee..feaaa4757 100644 --- a/packages/artplayer-tool-thumbnail/src/types.ts +++ b/packages/artplayer-tool-thumbnail/src/types.ts @@ -9,11 +9,15 @@ export interface SheetOptions { export interface InputOptions extends Partial { fileInput?: Element + delay?: number + compatibility?: 'published-3.5' | 'workspace-4.4' [name: string]: unknown } export interface RuntimeOptions extends SheetOptions { fileInput: HTMLInputElement + delay?: number + compatibility?: 'published-3.5' | 'workspace-4.4' [name: string]: unknown } diff --git a/packages/artplayer-tool-thumbnail/types/README.md b/packages/artplayer-tool-thumbnail/types/README.md index b9f75cdae..59790a81c 100644 --- a/packages/artplayer-tool-thumbnail/types/README.md +++ b/packages/artplayer-tool-thumbnail/types/README.md @@ -4,13 +4,13 @@ The declaration describes the existing tool class, not an ArtPlayer plugin facto The previous workspace manifest pointed to a missing declaration. There is no recovered legacy declaration to claim as an exact TypeScript baseline. -| Entry | Declaration | Runtime | -| --- | --- | --- | -| Root ESM import | artplayer-tool-thumbnail.d.mts | Default constructor in dist/artplayer-tool-thumbnail.mjs | -| Root require | artplayer-tool-thumbnail.d.cts | Direct constructor in dist/artplayer-tool-thumbnail.js | -| Older Node resolution / TS 4.3 | artplayer-tool-thumbnail.d.ts | main points to the direct CommonJS constructor | -| artplayer-tool-thumbnail/legacy | d.cts or typesVersions fallback | Direct legacy constructor; native ESM gets its default | -| Browser script / editor | Generated docs/assets/ts declaration | ArtplayerToolThumbnail global | +| Entry | Declaration | Runtime | +| ------------------------------- | ------------------------------------ | -------------------------------------------------------- | +| Root ESM import | artplayer-tool-thumbnail.d.mts | Default constructor in dist/artplayer-tool-thumbnail.mjs | +| Root require | artplayer-tool-thumbnail.d.cts | Direct constructor in dist/artplayer-tool-thumbnail.js | +| Older Node resolution / TS 4.3 | artplayer-tool-thumbnail.d.ts | main points to the direct CommonJS constructor | +| artplayer-tool-thumbnail/legacy | d.cts or typesVersions fallback | Direct legacy constructor; native ESM gets its default | +| Browser script / editor | Generated docs/assets/ts declaration | ArtplayerToolThumbnail global | The d.ts file is the public source of truth. d.cts forwards its export assignment; d.mts exposes the default constructor and type-only named exports. No runtime @@ -44,7 +44,7 @@ and source replacement/destroy reject with AbortError. Error events usually cont strings, but callback throws can supply any message value, so narrow unknown first. Source/declaration constructors are mutually checked in test/types/thumbnail-source.ts. -Current and TS 4.3.5 consumers include ten invalid uses. Installed checks pack/install +Current and TS 4.3.5 consumers include twelve invalid uses. Installed checks pack/install outside the workspace, offline with an unchanged frozen lock, verify every file and exercise native Node require/import without a DOM. @@ -57,4 +57,9 @@ yarn test:browser test/browser/thumbnail-editor-types.spec.js build:ts selects package declaration entries rather than auxiliary types and emits the uppercase global. Actual file extraction, old-core composition and the full -demo remain separate checks. The 3.5.31 versus 4.4.0 default policy remains pending. +demo remain separate checks. The approved default is published 3.5.31; +`compatibility: 'workspace-4.4'` opts into the previous workspace behavior. +`Compatibility` names these two modes. `DefaultOptions` extends `SheetOptions` +with the numeric static delay; existing `SheetOptions` literals need no new field. +`Option.delay` is optional and numeric; workspace mode ignores it, including +explicit undefined. The public resolved option therefore keeps delay optional. diff --git a/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.mts b/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.mts index fa41d6985..7e2c37419 100644 --- a/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.mts +++ b/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.mts @@ -1,2 +1,2 @@ export { default } from './artplayer-tool-thumbnail.cjs' -export type { EventArgs, EventRegistry, Events, Listener, Option, ResolvedOption, ScreenshotPoint, SheetOptions } from './artplayer-tool-thumbnail.js' +export type { Compatibility, DefaultOptions, EventArgs, EventRegistry, Events, Listener, Option, ResolvedOption, ScreenshotPoint, SheetOptions } from './artplayer-tool-thumbnail.js' diff --git a/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts b/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts index 705f387a2..ff5a558e3 100644 --- a/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts +++ b/packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts @@ -2,7 +2,7 @@ declare class ArtplayerToolThumbnail { constructor(option?: ArtplayerToolThumbnail.Option) - static readonly DEFAULTS: ArtplayerToolThumbnail.SheetOptions + static readonly DEFAULTS: ArtplayerToolThumbnail.DefaultOptions static ondragover(event: DragEvent): void static creatVideo(): HTMLVideoElement @@ -44,14 +44,25 @@ declare namespace ArtplayerToolThumbnail { end: number } + type Compatibility = 'published-3.5' | 'workspace-4.4' + + interface DefaultOptions extends SheetOptions { + delay: number + } + /** fileInput must be a file input or an Element wrapper when constructing. */ interface Option extends Partial { fileInput?: Element + compatibility?: Compatibility + /** Published mode delay in milliseconds, clamped to 10-1000. */ + delay?: number [name: string]: unknown } interface ResolvedOption extends SheetOptions { fileInput: HTMLInputElement + compatibility?: Compatibility + delay?: number [name: string]: unknown } diff --git a/refactor/baselines/thumbnail-policy-validation.json b/refactor/baselines/thumbnail-policy-validation.json new file mode 100644 index 000000000..d3e09906e --- /dev/null +++ b/refactor/baselines/thumbnail-policy-validation.json @@ -0,0 +1,10488 @@ +{ + "schemaVersion": 1, + "task": "PKG-TOOL-THUMB-04", + "status": "done", + "verifiedAt": "2026-09-15T15:51:54.088Z", + "startedFrom": "490e157717a0ac06893359fb8c619df0eea1a298", + "approval": { + "date": "2026-09-15", + "decision": "Published 3.5.31 defaults; explicit workspace-4.4 behavior, including class DEFAULTS delay." + }, + "node": "v24.21.0", + "yarn": "1.22.22", + "unit": { + "command": "yarn test:thumbnail", + "pass": 136, + "fail": 0, + "skipped": 0, + "durationMs": 14968.2823, + "log": "refactor/.cache/thumbnail-policy-final-unit.log" + }, + "installedLegacy": { + "pass": 63, + "fail": 0, + "durationMs": 2770.2957, + "log": "refactor/.cache/thumbnail-policy-installed-legacy.log", + "originalArtifact": "refactor/.cache/thumbnail-package-types-qNuqdd/installed-artifacts/dist/artplayer-tool-thumbnail.legacy.js", + "finalArtifactSha256": "3a3dd905d48c89de05fe232b1fe7aca522510238959c6170a7003b6df2f3df2c" + }, + "types": { + "strictSource": "passed", + "positiveNegativeModes": 5, + "rejectedCallsPerMode": 12, + "installed": { + "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-IH4Ouy\\workspace-fixture.tgz", + "sha256": "15087c77fed243938fdc6829595e899728d935f924139131eaa53c157f68c14a", + "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-IH4Ouy\\candidate.tgz", + "sha256": "e09bd96babde6589d4d3f0482b0819ba937622d2f6958ce127b0831f90efb63a", + "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": "44534a91510f99ff1d64a013953c77cfb6c00ece9f4dfad86a12e66b1999a3ec", + "package/README.md": "959562ac29bd0162e22c30041da9c3c9bfc9039e20cd6f210fb26b597d7f1c74", + "package/THIRD_PARTY_NOTICES": "7bea0832808c3d37b5a79f2ed6c65ef2ebdf7dbcbba124c469ebb90e2ec7c013", + "package/dist/artplayer-tool-thumbnail.js": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "package/dist/artplayer-tool-thumbnail.legacy.js": "3a3dd905d48c89de05fe232b1fe7aca522510238959c6170a7003b6df2f3df2c", + "package/dist/artplayer-tool-thumbnail.mjs": "39a3c48eca45c1c97e976d864f11d8b758d64cbae98654f88de3e26f1f36b60b", + "package/package.json": "dafbe13cddee908b2932ab2f529b0eef86ad9d9de8f7ab7585ed073c221cdbe1", + "package/types/README.md": "dd0bf1f872feac29b4c10424bec22c66174848d30724f067e463763de2ccea62", + "package/types/artplayer-tool-thumbnail.d.cts": "f0573326178b57a6e22a08b53247eb15ef4d20a1652e40d0cae638ac7aaf8092", + "package/types/artplayer-tool-thumbnail.d.mts": "f7a2fea001883706400b250588363533605d3f3a075cef1e31ae973c22258cb3", + "package/types/artplayer-tool-thumbnail.d.ts": "3da1a6a68db16c173dfb688ccc49ed130de2f7fb4790f1888230f2fd4c5847ac" + } + } + ], + "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." + }, + { + "code": 2322, + "message": "Type '\"latest\"' is not assignable to type 'Compatibility | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + } + ] + }, + { + "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." + }, + { + "code": 2322, + "message": "Type '\"latest\"' is not assignable to type 'Compatibility | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + } + ] + }, + { + "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." + }, + { + "code": 2322, + "message": "Type '\"latest\"' is not assignable to type 'Compatibility | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + } + ] + }, + { + "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." + }, + { + "code": 2322, + "message": "Type '\"latest\"' is not assignable to type 'Compatibility | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + } + ] + }, + { + "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." + }, + { + "code": 2322, + "message": "Type '\"latest\"' is not assignable to type 'Compatibility | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number | undefined'." + } + ] + }, + { + "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 + } + ] + } + }, + "lint": { + "exitCode": 0, + "log": "refactor/.cache/thumbnail-policy-lint.log" + }, + "builds": { + "formats": [ + "main", + "legacy", + "esm" + ], + "exitCode": 0, + "log": "refactor/.cache/thumbnail-policy-build.log" + }, + "browser": { + "source": { + "report": "refactor/.cache/thumbnail-policy-browser-source-final/report.json", + "sha256": "b51a5cd2311c9b434da5d618ce1271a5b556a8cc82c77e5a6557d993ca2af9fe", + "stats": { + "startTime": "2026-09-15T15:48:30.848Z", + "duration": 66422.918, + "expected": 90, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "capabilityControlCases": 23, + "cases": [ + { + "file": "thumbnail-editor-types.spec.js", + "title": "Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "browser": "chromium", + "status": "expected", + "duration": 939, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-editor": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "declarations": { + "artplayer": "6b038ad9084766023baaa037e6abcd9edde2a3c40b8b9b1dfc5e45c20be7b7ba", + "artplayer-tool-thumbnail": "f2845ec0a890d001829dbca089ccc450f8e77817e497c5738f964c2920772f3f", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-editor-types.spec.js", + "title": "Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "browser": "firefox", + "status": "expected", + "duration": 2236, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-editor": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "declarations": { + "artplayer": "6b038ad9084766023baaa037e6abcd9edde2a3c40b8b9b1dfc5e45c20be7b7ba", + "artplayer-tool-thumbnail": "f2845ec0a890d001829dbca089ccc450f8e77817e497c5738f964c2920772f3f", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-editor-types.spec.js", + "title": "Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "browser": "webkit", + "status": "expected", + "duration": 1342, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-editor": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "declarations": { + "artplayer": "6b038ad9084766023baaa037e6abcd9edde2a3c40b8b9b1dfc5e45c20be7b7ba", + "artplayer-tool-thumbnail": "f2845ec0a890d001829dbca089ccc450f8e77817e497c5738f964c2920772f3f", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "chromium", + "status": "expected", + "duration": 205, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "chromium", + "status": "expected", + "duration": 169, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "chromium", + "status": "expected", + "duration": 138, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "chromium", + "status": "expected", + "duration": 177, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + ] + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "browser": "chromium", + "status": "expected", + "duration": 144, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-emitter": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "firefox", + "status": "expected", + "duration": 1422, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "firefox", + "status": "expected", + "duration": 433, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "firefox", + "status": "expected", + "duration": 475, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "firefox", + "status": "expected", + "duration": 387, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + ] + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "browser": "firefox", + "status": "expected", + "duration": 402, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-emitter": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail cdn-3.5.31: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "webkit", + "status": "expected", + "duration": 635, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "webkit", + "status": "expected", + "duration": 540, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail workspace.legacy.js: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "webkit", + "status": "expected", + "duration": 496, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "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 + } + ] + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail candidate-source: destroy preserves falsy listener exceptions with real DOM cleanup", + "browser": "webkit", + "status": "expected", + "duration": 534, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-cleanup-errors": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + ] + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-emitter.spec.js", + "title": "Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "browser": "webkit", + "status": "expected", + "duration": 502, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-emitter": { + "candidate": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "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 + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-input.spec.js", + "title": "Thumbnail input ownership replaces real DOM listeners and generated inputs", + "browser": "chromium", + "status": "expected", + "duration": 203, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-input-ownership": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-input.spec.js", + "title": "Thumbnail input ownership replaces real DOM listeners and generated inputs", + "browser": "firefox", + "status": "expected", + "duration": 471, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-input-ownership": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-input.spec.js", + "title": "Thumbnail input ownership replaces real DOM listeners and generated inputs", + "browser": "webkit", + "status": "expected", + "duration": 704, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-input-ownership": { + "implementation": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-native.spec.js", + "title": "Thumbnail native HTTP and Blob media controls", + "browser": "chromium", + "status": "expected", + "duration": 170, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-native-media-controls": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + } + ], + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-native.spec.js", + "title": "Thumbnail native HTTP and Blob media controls", + "browser": "firefox", + "status": "expected", + "duration": 580, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-native-media-controls": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + } + ], + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-native.spec.js", + "title": "Thumbnail native HTTP and Blob media controls", + "browser": "webkit", + "status": "expected", + "duration": 1829, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native Blob URL unavailable for /test/pattern.mp4; HTTP comparison loads. This is not successful extraction." + }, + { + "type": "capability-control", + "description": "Native Blob URL unavailable for /test/thumbnail-pattern.mp4; HTTP comparison loads. This is not successful extraction." + }, + { + "type": "capability-control", + "description": "Native Blob URL unavailable for /assets/sample/video.mp4; HTTP comparison loads. This is not successful extraction." + } + ], + "errors": [], + "attachments": { + "thumbnail-native-media-controls": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 3, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 90.094, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + } + ], + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 8033, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 97.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 407.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 495, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 808.5, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1110.699999988079, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1424.199999988079, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1726, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2037.1000000238419, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2350.699999988079, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2661.699999988079, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 2969.699999988079, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3278.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3585.800000011921, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4199.5, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 97.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 407.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 495, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 808.5, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1110.699999988079, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1424.199999988079, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1726, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2037.1000000238419, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2350.699999988079, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2661.699999988079, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 2969.699999988079, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3278.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3585.800000011921, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4199.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4213.300000011921, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 4518.400000035763, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 4829.900000035763, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5144.100000023842, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5457.199999988079, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 5773.5, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6076.600000023842, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6384.600000023842, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 6687.100000023842, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 6994.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7300.5, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 7905.699999988079, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 7906.100000023842, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/ced686de-0229-4253-9296-1c86cb2d7ead", + "second": "blob:http://127.0.0.1:8084/001555b1-557d-469d-9445-9971c8ada479", + "created": [ + "blob:http://127.0.0.1:8084/eb00ea3f-2a72-44b9-8da4-b96edb8d53b2", + "blob:http://127.0.0.1:8084/2b790b8c-2de5-4957-8114-fa1646f4dba5", + "blob:http://127.0.0.1:8084/22df75b7-8ec8-45bc-a8d1-9e8296bdc5e0", + "blob:http://127.0.0.1:8084/70c95a03-92f5-46d5-9b77-61dd9cdfb103", + "blob:http://127.0.0.1:8084/dc69069f-fe85-474a-8aa1-362fc5af1e81", + "blob:http://127.0.0.1:8084/6a868302-ff84-4f83-807e-6ea81d47601f", + "blob:http://127.0.0.1:8084/f154946c-1879-4e86-9276-f2803d72f7bb", + "blob:http://127.0.0.1:8084/ba9fb177-be3b-4c36-9f1f-944995001685", + "blob:http://127.0.0.1:8084/8e3d656e-4677-4eab-b08a-46de7a9eb0bc", + "blob:http://127.0.0.1:8084/a800aaa4-48f5-4a97-be93-113ae8e6fade", + "blob:http://127.0.0.1:8084/ced686de-0229-4253-9296-1c86cb2d7ead", + "blob:http://127.0.0.1:8084/9d6585f4-db2d-43a8-a695-fa1f7b673c04", + "blob:http://127.0.0.1:8084/beb82d6e-ea48-44f5-9366-6704056e9a55", + "blob:http://127.0.0.1:8084/df9db46e-0f7f-40a5-ace3-dda180158587", + "blob:http://127.0.0.1:8084/30bfb05a-5da3-4c7a-a8a1-1585f87a39f0", + "blob:http://127.0.0.1:8084/88ec6c02-6e05-4129-9ec1-7778a491a7ff", + "blob:http://127.0.0.1:8084/5a54ca45-051c-4518-a95c-4b5042efd329", + "blob:http://127.0.0.1:8084/eb8e67a8-7f28-458b-b2f8-8ce87ccda597", + "blob:http://127.0.0.1:8084/1543a66f-9a33-4b4a-a775-08eeac907956", + "blob:http://127.0.0.1:8084/d189ae89-7901-42a0-998d-654b7299fee4", + "blob:http://127.0.0.1:8084/001555b1-557d-469d-9445-9971c8ada479" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2b790b8c-2de5-4957-8114-fa1646f4dba5", + "blob:http://127.0.0.1:8084/22df75b7-8ec8-45bc-a8d1-9e8296bdc5e0", + "blob:http://127.0.0.1:8084/70c95a03-92f5-46d5-9b77-61dd9cdfb103", + "blob:http://127.0.0.1:8084/dc69069f-fe85-474a-8aa1-362fc5af1e81", + "blob:http://127.0.0.1:8084/6a868302-ff84-4f83-807e-6ea81d47601f", + "blob:http://127.0.0.1:8084/f154946c-1879-4e86-9276-f2803d72f7bb", + "blob:http://127.0.0.1:8084/ba9fb177-be3b-4c36-9f1f-944995001685", + "blob:http://127.0.0.1:8084/8e3d656e-4677-4eab-b08a-46de7a9eb0bc", + "blob:http://127.0.0.1:8084/a800aaa4-48f5-4a97-be93-113ae8e6fade", + "blob:http://127.0.0.1:8084/ced686de-0229-4253-9296-1c86cb2d7ead", + "blob:http://127.0.0.1:8084/9d6585f4-db2d-43a8-a695-fa1f7b673c04", + "blob:http://127.0.0.1:8084/beb82d6e-ea48-44f5-9366-6704056e9a55", + "blob:http://127.0.0.1:8084/df9db46e-0f7f-40a5-ace3-dda180158587", + "blob:http://127.0.0.1:8084/30bfb05a-5da3-4c7a-a8a1-1585f87a39f0", + "blob:http://127.0.0.1:8084/88ec6c02-6e05-4129-9ec1-7778a491a7ff", + "blob:http://127.0.0.1:8084/5a54ca45-051c-4518-a95c-4b5042efd329", + "blob:http://127.0.0.1:8084/eb8e67a8-7f28-458b-b2f8-8ce87ccda597", + "blob:http://127.0.0.1:8084/1543a66f-9a33-4b4a-a775-08eeac907956", + "blob:http://127.0.0.1:8084/d189ae89-7901-42a0-998d-654b7299fee4", + "blob:http://127.0.0.1:8084/eb00ea3f-2a72-44b9-8da4-b96edb8d53b2", + "blob:http://127.0.0.1:8084/001555b1-557d-469d-9445-9971c8ada479" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/eb00ea3f-2a72-44b9-8da4-b96edb8d53b2" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 203, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 226, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/0f1d2314-c99d-4597-b4e2-25a2d00aee4e" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/0f1d2314-c99d-4597-b4e2-25a2d00aee4e", + "blob:http://127.0.0.1:8084/ba232684-8f15-4166-a137-3b7ed365b98f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ba232684-8f15-4166-a137-3b7ed365b98f" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "chromium", + "status": "expected", + "duration": 599, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/545d83e3-076a-4a36-95ef-17a013e1c11b", + "blob:http://127.0.0.1:8084/5541ed13-acbe-4298-a6b4-a6298e7f4aab" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/545d83e3-076a-4a36-95ef-17a013e1c11b" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 7540 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 8356, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 202, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 509, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 600, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 913, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1227, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1543, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1851, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2184, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2492, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2800, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3111, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3415, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3717, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4334, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 202, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 509, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 600, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 913, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1227, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1543, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1851, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2184, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2492, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2800, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3111, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3415, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3717, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4334, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4335, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 4638, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 4961, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5275, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5587, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 5904, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6228, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6553, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 6868, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 7187, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7505, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 8112, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 8112, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/811c8749-31a6-4f72-b0bd-75974e523564", + "second": "blob:http://127.0.0.1:8084/8686cd74-dd63-4f14-b26e-9f39f61cb583", + "created": [ + "blob:http://127.0.0.1:8084/af81f6fc-2f6e-4f94-8baa-2557c216d190", + "blob:http://127.0.0.1:8084/437ba0ee-e3a1-41c8-aa92-8f047fa08334", + "blob:http://127.0.0.1:8084/6c72cc42-b227-4397-a72a-05496f032c93", + "blob:http://127.0.0.1:8084/86f0abb8-f10d-41c8-b3f4-46abceacd650", + "blob:http://127.0.0.1:8084/827fe5ca-0b93-4688-8ffe-b31bf0aa7e1c", + "blob:http://127.0.0.1:8084/0a8eb2f4-6971-413f-8000-52efa1fd1b9e", + "blob:http://127.0.0.1:8084/802c8703-0a7a-4dcc-be10-8d81519ed782", + "blob:http://127.0.0.1:8084/28f76268-a3f3-4642-97d1-c99296415de2", + "blob:http://127.0.0.1:8084/1e733530-aa96-4804-8e9e-03387f05256b", + "blob:http://127.0.0.1:8084/c6012087-4bfe-4664-adf0-488c80806506", + "blob:http://127.0.0.1:8084/811c8749-31a6-4f72-b0bd-75974e523564", + "blob:http://127.0.0.1:8084/3c6156c7-b4a8-4b88-91ed-d93c49f7528f", + "blob:http://127.0.0.1:8084/f8f1da3a-bae2-428a-b1fa-64dc8614a0e4", + "blob:http://127.0.0.1:8084/d42427b6-519d-4ff5-807e-48104f69e626", + "blob:http://127.0.0.1:8084/9e16dc2d-42f1-4468-be0a-d584f7d3e608", + "blob:http://127.0.0.1:8084/cbaa1189-55ca-4b09-bf0c-281123697ccb", + "blob:http://127.0.0.1:8084/95e19450-9290-4399-b933-da85290fd752", + "blob:http://127.0.0.1:8084/195a3cbb-b609-4c25-a530-780a134898dc", + "blob:http://127.0.0.1:8084/a4889b36-4c7d-492b-8b33-32360593f88a", + "blob:http://127.0.0.1:8084/2e6d6f85-fcaf-404c-8ee9-ca6dcd5a9482", + "blob:http://127.0.0.1:8084/8686cd74-dd63-4f14-b26e-9f39f61cb583" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/437ba0ee-e3a1-41c8-aa92-8f047fa08334", + "blob:http://127.0.0.1:8084/6c72cc42-b227-4397-a72a-05496f032c93", + "blob:http://127.0.0.1:8084/86f0abb8-f10d-41c8-b3f4-46abceacd650", + "blob:http://127.0.0.1:8084/827fe5ca-0b93-4688-8ffe-b31bf0aa7e1c", + "blob:http://127.0.0.1:8084/0a8eb2f4-6971-413f-8000-52efa1fd1b9e", + "blob:http://127.0.0.1:8084/802c8703-0a7a-4dcc-be10-8d81519ed782", + "blob:http://127.0.0.1:8084/28f76268-a3f3-4642-97d1-c99296415de2", + "blob:http://127.0.0.1:8084/1e733530-aa96-4804-8e9e-03387f05256b", + "blob:http://127.0.0.1:8084/c6012087-4bfe-4664-adf0-488c80806506", + "blob:http://127.0.0.1:8084/811c8749-31a6-4f72-b0bd-75974e523564", + "blob:http://127.0.0.1:8084/3c6156c7-b4a8-4b88-91ed-d93c49f7528f", + "blob:http://127.0.0.1:8084/f8f1da3a-bae2-428a-b1fa-64dc8614a0e4", + "blob:http://127.0.0.1:8084/d42427b6-519d-4ff5-807e-48104f69e626", + "blob:http://127.0.0.1:8084/9e16dc2d-42f1-4468-be0a-d584f7d3e608", + "blob:http://127.0.0.1:8084/cbaa1189-55ca-4b09-bf0c-281123697ccb", + "blob:http://127.0.0.1:8084/95e19450-9290-4399-b933-da85290fd752", + "blob:http://127.0.0.1:8084/195a3cbb-b609-4c25-a530-780a134898dc", + "blob:http://127.0.0.1:8084/a4889b36-4c7d-492b-8b33-32360593f88a", + "blob:http://127.0.0.1:8084/2e6d6f85-fcaf-404c-8ee9-ca6dcd5a9482", + "blob:http://127.0.0.1:8084/af81f6fc-2f6e-4f94-8baa-2557c216d190", + "blob:http://127.0.0.1:8084/8686cd74-dd63-4f14-b26e-9f39f61cb583" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/af81f6fc-2f6e-4f94-8baa-2557c216d190" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 450, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 579, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/3d346b25-b80e-4bec-bacc-53db981c5bc4" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/3d346b25-b80e-4bec-bacc-53db981c5bc4", + "blob:http://127.0.0.1:8084/816b2fef-0710-4111-95af-79d3f493ef71" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/816b2fef-0710-4111-95af-79d3f493ef71" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "firefox", + "status": "expected", + "duration": 974, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/7efc04c2-e7e3-4c62-b427-d2d18383791f", + "blob:http://127.0.0.1:8084/be2674da-6fd2-4310-87b3-823193be5c24" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7efc04c2-e7e3-4c62-b427-d2d18383791f" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 4980 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1159, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1292, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1164, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "webkit", + "status": "expected", + "duration": 1158, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 472, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 95.5, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 95.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 111.80000001192093, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 126, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 132, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 142.10000002384186, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 160.5, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 163.80000001192093, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 169.80000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 193.30000001192093, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 205.69999998807907, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 227.69999998807907, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 234.4000000357628, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 234.5, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 95.5, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 95.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 111.80000001192093, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 126, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 132, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 142.10000002384186, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 160.5, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 163.80000001192093, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 169.80000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 193.30000001192093, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 205.69999998807907, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 227.69999998807907, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 234.4000000357628, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 234.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 238, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 243.80000001192093, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 260, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 268.69999998807907, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 293.30000001192093, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 297.80000001192093, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 306, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 336.80000001192093, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 361.19999998807907, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 364.80000001192093, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 372.19999998807907, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 372.19999998807907, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 372.4000000357628, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/7cb128be-2120-4f9a-bf92-6ffe0d52cf84", + "second": "blob:http://127.0.0.1:8084/1cf411e3-afb7-499e-8abc-9b6dfe1beeda", + "created": [ + "blob:http://127.0.0.1:8084/5a4b80ce-8051-4eb3-a95b-d46925b983c6", + "blob:http://127.0.0.1:8084/a0285fdb-6789-4145-9019-4321bbd4b0b1", + "blob:http://127.0.0.1:8084/9cb064dc-fc52-4bda-a1a8-960d144a1760", + "blob:http://127.0.0.1:8084/cef2206e-570c-412f-89b8-dafb6e44bb1e", + "blob:http://127.0.0.1:8084/4da59bb7-fda6-4210-8aca-651dae7e3e58", + "blob:http://127.0.0.1:8084/71c6c4f9-2605-4e6f-8439-a9eee6172385", + "blob:http://127.0.0.1:8084/b1922293-fc5b-45f1-8e72-ec8bc32b4ca5", + "blob:http://127.0.0.1:8084/8b73e39c-9743-4ee1-88ca-392c252fd3f4", + "blob:http://127.0.0.1:8084/67e7ffcd-8ef8-48c2-8992-64e1a0f4727d", + "blob:http://127.0.0.1:8084/51195f95-39e8-45ca-9c10-8d7e3b27b39c", + "blob:http://127.0.0.1:8084/7cb128be-2120-4f9a-bf92-6ffe0d52cf84", + "blob:http://127.0.0.1:8084/6b34e9a9-d6a0-46a6-8215-df4bf5e89534", + "blob:http://127.0.0.1:8084/4d80020d-361d-4188-af2e-2234f3a6da58", + "blob:http://127.0.0.1:8084/8684ddf0-8e55-4599-b8f3-3f71b3368bc8", + "blob:http://127.0.0.1:8084/525123e5-127b-46ee-a6de-92a43c9b13c9", + "blob:http://127.0.0.1:8084/80e537ef-b087-4e5c-b07e-a46ca77253be", + "blob:http://127.0.0.1:8084/d2190f61-4d0f-4409-8e8f-8e17f168604c", + "blob:http://127.0.0.1:8084/0d889d45-7fce-4fc9-b769-5c6f68557a2a", + "blob:http://127.0.0.1:8084/ed0aa1e8-912f-497b-82cf-3672ff200378", + "blob:http://127.0.0.1:8084/ff738f21-18ef-452b-bc15-505438c8923e", + "blob:http://127.0.0.1:8084/1cf411e3-afb7-499e-8abc-9b6dfe1beeda" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a0285fdb-6789-4145-9019-4321bbd4b0b1", + "blob:http://127.0.0.1:8084/9cb064dc-fc52-4bda-a1a8-960d144a1760", + "blob:http://127.0.0.1:8084/cef2206e-570c-412f-89b8-dafb6e44bb1e", + "blob:http://127.0.0.1:8084/4da59bb7-fda6-4210-8aca-651dae7e3e58", + "blob:http://127.0.0.1:8084/71c6c4f9-2605-4e6f-8439-a9eee6172385", + "blob:http://127.0.0.1:8084/b1922293-fc5b-45f1-8e72-ec8bc32b4ca5", + "blob:http://127.0.0.1:8084/8b73e39c-9743-4ee1-88ca-392c252fd3f4", + "blob:http://127.0.0.1:8084/67e7ffcd-8ef8-48c2-8992-64e1a0f4727d", + "blob:http://127.0.0.1:8084/51195f95-39e8-45ca-9c10-8d7e3b27b39c", + "blob:http://127.0.0.1:8084/7cb128be-2120-4f9a-bf92-6ffe0d52cf84", + "blob:http://127.0.0.1:8084/6b34e9a9-d6a0-46a6-8215-df4bf5e89534", + "blob:http://127.0.0.1:8084/4d80020d-361d-4188-af2e-2234f3a6da58", + "blob:http://127.0.0.1:8084/8684ddf0-8e55-4599-b8f3-3f71b3368bc8", + "blob:http://127.0.0.1:8084/525123e5-127b-46ee-a6de-92a43c9b13c9", + "blob:http://127.0.0.1:8084/80e537ef-b087-4e5c-b07e-a46ca77253be", + "blob:http://127.0.0.1:8084/d2190f61-4d0f-4409-8e8f-8e17f168604c", + "blob:http://127.0.0.1:8084/0d889d45-7fce-4fc9-b769-5c6f68557a2a", + "blob:http://127.0.0.1:8084/ed0aa1e8-912f-497b-82cf-3672ff200378", + "blob:http://127.0.0.1:8084/ff738f21-18ef-452b-bc15-505438c8923e", + "blob:http://127.0.0.1:8084/5a4b80ce-8051-4eb3-a95b-d46925b983c6", + "blob:http://127.0.0.1:8084/1cf411e3-afb7-499e-8abc-9b6dfe1beeda" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/5a4b80ce-8051-4eb3-a95b-d46925b983c6" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 191, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 204, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/8f1c3a87-b858-4571-b00b-13782f2795e6" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/8f1c3a87-b858-4571-b00b-13782f2795e6", + "blob:http://127.0.0.1:8084/535e47a9-7b49-4698-a343-2682b13f80b6" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/535e47a9-7b49-4698-a343-2682b13f80b6" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "chromium", + "status": "expected", + "duration": 338, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/6453a826-9a8d-4faa-a121-c37bdb3eb49d", + "blob:http://127.0.0.1:8084/17275ebe-2ff0-446b-8232-d3218b640a18" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6453a826-9a8d-4faa-a121-c37bdb3eb49d" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 1372, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 189, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 189, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 212, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 229, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 277, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 347, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 433, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 451, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 489, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 551, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 638, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 656, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 699, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 699, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 189, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 189, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 212, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 229, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 277, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 347, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 433, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 451, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 489, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 551, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 638, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 656, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 699, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 699, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 701, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 719, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 757, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 816, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 897, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 913, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 953, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 1012, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 1089, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 1107, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 1147, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 1147, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 1149, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/2b4320fd-f09d-4042-8155-d41b6c55c89b", + "second": "blob:http://127.0.0.1:8084/0eadc376-eb3e-4cb6-9fcf-f0aa0815a7a7", + "created": [ + "blob:http://127.0.0.1:8084/1bd7050c-cdc7-4249-8760-b373265ee76b", + "blob:http://127.0.0.1:8084/bc3f4fae-1a35-4c0d-8f6a-8188dae7c24e", + "blob:http://127.0.0.1:8084/e84184e7-f542-4545-b2e3-9dddb7428aef", + "blob:http://127.0.0.1:8084/119e5d71-c86d-400d-982b-a0d399b989ea", + "blob:http://127.0.0.1:8084/49939fc3-fa86-450e-b68a-456218353fcc", + "blob:http://127.0.0.1:8084/6ff34567-de40-44c6-bbad-433710a9d063", + "blob:http://127.0.0.1:8084/e7c2aaae-5f62-4446-88ac-6a4a23b83803", + "blob:http://127.0.0.1:8084/3166d967-9ba9-4a71-9ee4-44acfe9b501d", + "blob:http://127.0.0.1:8084/ebf9076e-9254-4109-8c31-5fff0b59ed02", + "blob:http://127.0.0.1:8084/5eaf1d47-2929-4b38-a85b-5d77f5b0d2da", + "blob:http://127.0.0.1:8084/2b4320fd-f09d-4042-8155-d41b6c55c89b", + "blob:http://127.0.0.1:8084/1c41a553-4b98-4df5-96fb-557cef50d0f8", + "blob:http://127.0.0.1:8084/ce10291b-f3e3-48f0-b439-38b4e5b34a1d", + "blob:http://127.0.0.1:8084/c7116daf-5836-4cb1-a223-e4b9f55f9124", + "blob:http://127.0.0.1:8084/cd143643-1c8c-46b8-9540-e32a49df68a9", + "blob:http://127.0.0.1:8084/54255cfb-e7b9-4c1e-afc6-de7916cb8699", + "blob:http://127.0.0.1:8084/03de5f8f-58fa-40f3-aed7-103443984dcc", + "blob:http://127.0.0.1:8084/81818ec9-f701-4c86-9938-e03dd52e1768", + "blob:http://127.0.0.1:8084/ca7ba035-9356-401d-b612-5dd45e988ac0", + "blob:http://127.0.0.1:8084/ba3e385b-d2bd-48d8-b06e-abcf67cad32d", + "blob:http://127.0.0.1:8084/0eadc376-eb3e-4cb6-9fcf-f0aa0815a7a7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/bc3f4fae-1a35-4c0d-8f6a-8188dae7c24e", + "blob:http://127.0.0.1:8084/e84184e7-f542-4545-b2e3-9dddb7428aef", + "blob:http://127.0.0.1:8084/119e5d71-c86d-400d-982b-a0d399b989ea", + "blob:http://127.0.0.1:8084/49939fc3-fa86-450e-b68a-456218353fcc", + "blob:http://127.0.0.1:8084/6ff34567-de40-44c6-bbad-433710a9d063", + "blob:http://127.0.0.1:8084/e7c2aaae-5f62-4446-88ac-6a4a23b83803", + "blob:http://127.0.0.1:8084/3166d967-9ba9-4a71-9ee4-44acfe9b501d", + "blob:http://127.0.0.1:8084/ebf9076e-9254-4109-8c31-5fff0b59ed02", + "blob:http://127.0.0.1:8084/5eaf1d47-2929-4b38-a85b-5d77f5b0d2da", + "blob:http://127.0.0.1:8084/2b4320fd-f09d-4042-8155-d41b6c55c89b", + "blob:http://127.0.0.1:8084/1c41a553-4b98-4df5-96fb-557cef50d0f8", + "blob:http://127.0.0.1:8084/ce10291b-f3e3-48f0-b439-38b4e5b34a1d", + "blob:http://127.0.0.1:8084/c7116daf-5836-4cb1-a223-e4b9f55f9124", + "blob:http://127.0.0.1:8084/cd143643-1c8c-46b8-9540-e32a49df68a9", + "blob:http://127.0.0.1:8084/54255cfb-e7b9-4c1e-afc6-de7916cb8699", + "blob:http://127.0.0.1:8084/03de5f8f-58fa-40f3-aed7-103443984dcc", + "blob:http://127.0.0.1:8084/81818ec9-f701-4c86-9938-e03dd52e1768", + "blob:http://127.0.0.1:8084/ca7ba035-9356-401d-b612-5dd45e988ac0", + "blob:http://127.0.0.1:8084/ba3e385b-d2bd-48d8-b06e-abcf67cad32d", + "blob:http://127.0.0.1:8084/1bd7050c-cdc7-4249-8760-b373265ee76b", + "blob:http://127.0.0.1:8084/0eadc376-eb3e-4cb6-9fcf-f0aa0815a7a7" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/1bd7050c-cdc7-4249-8760-b373265ee76b" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 913, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 611, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/78af2217-20b4-4c87-8fda-405167603faa" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/78af2217-20b4-4c87-8fda-405167603faa", + "blob:http://127.0.0.1:8084/114d6e74-d371-40c4-8f71-fdd86fcf66b4" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/114d6e74-d371-40c4-8f71-fdd86fcf66b4" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "firefox", + "status": "expected", + "duration": 655, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/e3d3e454-43f1-49ba-aae5-bd27b1a62c39", + "blob:http://127.0.0.1:8084/d159514f-4591-4924-b82f-4764f277b576" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e3d3e454-43f1-49ba-aae5-bd27b1a62c39" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1238, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1188, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1223, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "webkit", + "status": "expected", + "duration": 1251, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 491, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 93.90000003576279, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 94, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 112.20000004768372, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 123.10000002384186, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 129.4000000357628, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 139.80000001192093, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 156.20000004768372, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 159.5, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 165.70000004768372, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 189.70000004768372, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 202, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 206.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 228.60000002384186, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 228.60000002384186, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 93.90000003576279, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 94, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 112.20000004768372, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 123.10000002384186, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 129.4000000357628, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 139.80000001192093, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 156.20000004768372, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 159.5, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 165.70000004768372, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 189.70000004768372, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 202, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 206.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 228.60000002384186, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 228.60000002384186, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 231.9000000357628, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 234.9000000357628, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 256.80000001192093, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 265.2000000476837, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 289, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 292.4000000357628, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 298.2000000476837, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 323.4000000357628, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 335.9000000357628, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 370.2000000476837, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 393, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 393.10000002384186, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 393.2000000476837, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/75ce86db-e4bf-489e-898a-9f026f3006eb", + "second": "blob:http://127.0.0.1:8084/2ae134b3-21ac-4d2e-9d2b-6c12896966b7", + "created": [ + "blob:http://127.0.0.1:8084/0f38e206-82b7-4b8d-ba7f-f9d8a8246347", + "blob:http://127.0.0.1:8084/36013379-a301-4ae9-a6e8-b1104fc2028b", + "blob:http://127.0.0.1:8084/815d92b1-75f2-4bd1-94b5-e96a66c177f9", + "blob:http://127.0.0.1:8084/3f9cb2ef-d332-4741-9d6c-49c050f5fb35", + "blob:http://127.0.0.1:8084/a5fa0529-ac47-4579-b002-6d188b93d5c9", + "blob:http://127.0.0.1:8084/472ed36e-52d6-4174-952b-d3801e3b4308", + "blob:http://127.0.0.1:8084/8fc41aa8-7c44-462c-bb10-8ebac46ad1a8", + "blob:http://127.0.0.1:8084/cd77b679-6a85-4be9-b6ab-2bac330c881a", + "blob:http://127.0.0.1:8084/3b148618-cf5b-48c1-92f9-8ee93f25fe81", + "blob:http://127.0.0.1:8084/57cdd376-0c31-45a3-acfd-34b4a28ae1b0", + "blob:http://127.0.0.1:8084/75ce86db-e4bf-489e-898a-9f026f3006eb", + "blob:http://127.0.0.1:8084/021ed54d-ff17-43cb-8751-79fff0a261cf", + "blob:http://127.0.0.1:8084/fec3bd7f-9aba-4204-8ffe-de3e1742c352", + "blob:http://127.0.0.1:8084/54b816da-220a-4a5d-8952-4620e4ef2644", + "blob:http://127.0.0.1:8084/93d9ce68-d8c3-43af-9932-5185c744f965", + "blob:http://127.0.0.1:8084/f3f56a7f-e4db-4d51-aafb-5e35f6cf3a2e", + "blob:http://127.0.0.1:8084/7e843374-b1f4-434f-8d39-156b4d65b58a", + "blob:http://127.0.0.1:8084/adf1f39d-f7d8-4bfa-84ce-33accc6e6762", + "blob:http://127.0.0.1:8084/1bb2ec5a-be7e-4cea-82f3-24cd39e5e027", + "blob:http://127.0.0.1:8084/7b1b858a-2663-4772-bf05-7bab4c4b9ad6", + "blob:http://127.0.0.1:8084/2ae134b3-21ac-4d2e-9d2b-6c12896966b7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/36013379-a301-4ae9-a6e8-b1104fc2028b", + "blob:http://127.0.0.1:8084/815d92b1-75f2-4bd1-94b5-e96a66c177f9", + "blob:http://127.0.0.1:8084/3f9cb2ef-d332-4741-9d6c-49c050f5fb35", + "blob:http://127.0.0.1:8084/a5fa0529-ac47-4579-b002-6d188b93d5c9", + "blob:http://127.0.0.1:8084/472ed36e-52d6-4174-952b-d3801e3b4308", + "blob:http://127.0.0.1:8084/8fc41aa8-7c44-462c-bb10-8ebac46ad1a8", + "blob:http://127.0.0.1:8084/cd77b679-6a85-4be9-b6ab-2bac330c881a", + "blob:http://127.0.0.1:8084/3b148618-cf5b-48c1-92f9-8ee93f25fe81", + "blob:http://127.0.0.1:8084/57cdd376-0c31-45a3-acfd-34b4a28ae1b0", + "blob:http://127.0.0.1:8084/75ce86db-e4bf-489e-898a-9f026f3006eb", + "blob:http://127.0.0.1:8084/021ed54d-ff17-43cb-8751-79fff0a261cf", + "blob:http://127.0.0.1:8084/fec3bd7f-9aba-4204-8ffe-de3e1742c352", + "blob:http://127.0.0.1:8084/54b816da-220a-4a5d-8952-4620e4ef2644", + "blob:http://127.0.0.1:8084/93d9ce68-d8c3-43af-9932-5185c744f965", + "blob:http://127.0.0.1:8084/f3f56a7f-e4db-4d51-aafb-5e35f6cf3a2e", + "blob:http://127.0.0.1:8084/7e843374-b1f4-434f-8d39-156b4d65b58a", + "blob:http://127.0.0.1:8084/adf1f39d-f7d8-4bfa-84ce-33accc6e6762", + "blob:http://127.0.0.1:8084/1bb2ec5a-be7e-4cea-82f3-24cd39e5e027", + "blob:http://127.0.0.1:8084/7b1b858a-2663-4772-bf05-7bab4c4b9ad6", + "blob:http://127.0.0.1:8084/0f38e206-82b7-4b8d-ba7f-f9d8a8246347", + "blob:http://127.0.0.1:8084/2ae134b3-21ac-4d2e-9d2b-6c12896966b7" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/0f38e206-82b7-4b8d-ba7f-f9d8a8246347" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 189, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 209, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/92c4de46-ffff-4b5a-aef3-0e62caf24a9b" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/92c4de46-ffff-4b5a-aef3-0e62caf24a9b", + "blob:http://127.0.0.1:8084/9225443d-c5d9-41da-a8dc-402085b69f6d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/9225443d-c5d9-41da-a8dc-402085b69f6d" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "chromium", + "status": "expected", + "duration": 323, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/adc6a2c2-f9da-4b61-b980-8f97a5e30f20", + "blob:http://127.0.0.1:8084/12bc60cb-473b-48dd-a7b6-bff6611d4a7f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/adc6a2c2-f9da-4b61-b980-8f97a5e30f20" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 1468, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 178, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 178, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 316, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 334, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 376, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 438, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 521, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 538, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 577, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 636, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 717, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 737, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 783, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 783, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 178, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 178, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 316, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 334, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 376, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 438, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 521, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 538, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 577, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 636, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 717, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 737, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 783, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 783, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 785, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 803, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 841, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 901, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 979, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 994, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 1034, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 1099, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 1183, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 1202, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 1248, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 1248, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 1249, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/839c75f5-b819-440c-9607-07023ac4f67b", + "second": "blob:http://127.0.0.1:8084/32da4407-c5cb-4595-a584-ce4845ea7334", + "created": [ + "blob:http://127.0.0.1:8084/50d891ef-6d87-48f5-bb3f-1f61b6372e93", + "blob:http://127.0.0.1:8084/a1333482-4ee7-4cb3-a605-3b40a1fb8776", + "blob:http://127.0.0.1:8084/e2937fc0-1da6-45d2-b82a-a829611f9eeb", + "blob:http://127.0.0.1:8084/771ba67e-c0bd-4afd-8cd4-4c40f107b0a7", + "blob:http://127.0.0.1:8084/3b64203a-b2eb-4a75-bbb0-5e0686b368ac", + "blob:http://127.0.0.1:8084/86e37ca2-ed2e-41f2-bab2-1e91031ceb6d", + "blob:http://127.0.0.1:8084/aa4c47d2-4d9a-4720-9dba-1d2acb58e733", + "blob:http://127.0.0.1:8084/80188f61-c35d-4cf5-b757-9cdb8cf91e86", + "blob:http://127.0.0.1:8084/c39779f8-0926-4b53-bce3-ee0ac07dea50", + "blob:http://127.0.0.1:8084/49fc25fb-17eb-4365-ac1e-22dfddba6d28", + "blob:http://127.0.0.1:8084/839c75f5-b819-440c-9607-07023ac4f67b", + "blob:http://127.0.0.1:8084/3e7847fb-2311-4db3-8c3d-a408dcfa6b2b", + "blob:http://127.0.0.1:8084/b97b73ac-4474-42cf-b963-7a8acfbfd985", + "blob:http://127.0.0.1:8084/9bd1853a-2790-45d7-b473-fadd8708e6d2", + "blob:http://127.0.0.1:8084/ad864206-b3e7-470b-9aec-0d6a6c56a274", + "blob:http://127.0.0.1:8084/c234092a-6cd1-4a9f-82be-57ca3763abd7", + "blob:http://127.0.0.1:8084/ec3b8014-d293-406f-a8ed-f414a6f50355", + "blob:http://127.0.0.1:8084/0830a2ad-f8b1-4e1c-bb0c-e35e7a966b4a", + "blob:http://127.0.0.1:8084/d86d040e-f6a0-4aab-aada-fa105d430c6b", + "blob:http://127.0.0.1:8084/846fa13f-e2d2-4ee3-a216-5299579bc923", + "blob:http://127.0.0.1:8084/32da4407-c5cb-4595-a584-ce4845ea7334" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a1333482-4ee7-4cb3-a605-3b40a1fb8776", + "blob:http://127.0.0.1:8084/e2937fc0-1da6-45d2-b82a-a829611f9eeb", + "blob:http://127.0.0.1:8084/771ba67e-c0bd-4afd-8cd4-4c40f107b0a7", + "blob:http://127.0.0.1:8084/3b64203a-b2eb-4a75-bbb0-5e0686b368ac", + "blob:http://127.0.0.1:8084/86e37ca2-ed2e-41f2-bab2-1e91031ceb6d", + "blob:http://127.0.0.1:8084/aa4c47d2-4d9a-4720-9dba-1d2acb58e733", + "blob:http://127.0.0.1:8084/80188f61-c35d-4cf5-b757-9cdb8cf91e86", + "blob:http://127.0.0.1:8084/c39779f8-0926-4b53-bce3-ee0ac07dea50", + "blob:http://127.0.0.1:8084/49fc25fb-17eb-4365-ac1e-22dfddba6d28", + "blob:http://127.0.0.1:8084/839c75f5-b819-440c-9607-07023ac4f67b", + "blob:http://127.0.0.1:8084/3e7847fb-2311-4db3-8c3d-a408dcfa6b2b", + "blob:http://127.0.0.1:8084/b97b73ac-4474-42cf-b963-7a8acfbfd985", + "blob:http://127.0.0.1:8084/9bd1853a-2790-45d7-b473-fadd8708e6d2", + "blob:http://127.0.0.1:8084/ad864206-b3e7-470b-9aec-0d6a6c56a274", + "blob:http://127.0.0.1:8084/c234092a-6cd1-4a9f-82be-57ca3763abd7", + "blob:http://127.0.0.1:8084/ec3b8014-d293-406f-a8ed-f414a6f50355", + "blob:http://127.0.0.1:8084/0830a2ad-f8b1-4e1c-bb0c-e35e7a966b4a", + "blob:http://127.0.0.1:8084/d86d040e-f6a0-4aab-aada-fa105d430c6b", + "blob:http://127.0.0.1:8084/846fa13f-e2d2-4ee3-a216-5299579bc923", + "blob:http://127.0.0.1:8084/50d891ef-6d87-48f5-bb3f-1f61b6372e93", + "blob:http://127.0.0.1:8084/32da4407-c5cb-4595-a584-ce4845ea7334" + ], + "connected": false, + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/50d891ef-6d87-48f5-bb3f-1f61b6372e93" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 406, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 523, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/ad9b8e6b-c7b3-40ab-b900-ec904e49e07c" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/ad9b8e6b-c7b3-40ab-b900-ec904e49e07c", + "blob:http://127.0.0.1:8084/f6f60be5-fdfa-4b80-a16d-81e4b797b699" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f6f60be5-fdfa-4b80-a16d-81e4b797b699" + ], + "repeated": "NotFoundError" + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "firefox", + "status": "expected", + "duration": 582, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/605ca500-3738-4aa7-b57e-9aee83594545", + "blob:http://127.0.0.1:8084/a0dcd4b4-b73e-49bb-8b37-cb29305cdab4" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/605ca500-3738-4aa7-b57e-9aee83594545" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1259, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch exposes missing listener while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1264, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1236, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy still emits update and creates a URL", + "browser": "webkit", + "status": "expected", + "duration": 1217, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 8042, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 85.10000002384186, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 390.60000002384186, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 485.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 794.3000000119209, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1101.1000000238419, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1410.6000000238419, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1714.6000000238419, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2025, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2339.100000023842, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2641.800000011921, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 2958.600000023842, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3265.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3574.199999988079, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4188.399999976158, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 85.10000002384186, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 390.60000002384186, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 485.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 794.3000000119209, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1101.1000000238419, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1410.6000000238419, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1714.6000000238419, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2025, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2339.100000023842, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2641.800000011921, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 2958.600000023842, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3265.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3574.199999988079, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4188.399999976158, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4200.699999988079, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 4507.5, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 4822.5, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5129.199999988079, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5442.899999976158, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 5759.199999988079, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6074, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6377.899999976158, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 6695.100000023842, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 7013.5, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7315.300000011921, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 7924.100000023842, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 7925.199999988079, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/df9f7352-07c1-464f-9e33-627ecc29a7fd", + "second": "blob:http://127.0.0.1:8084/8b1947b0-fca4-4faa-9879-32eace697485", + "created": [ + "blob:http://127.0.0.1:8084/7151421c-ccdd-4b3f-ba94-7cac52808f18", + "blob:http://127.0.0.1:8084/cdb9e5fd-335b-428d-8e31-c7ae7416bdf1", + "blob:http://127.0.0.1:8084/1de7b0f9-e6d5-401c-a8e6-48507818a2ae", + "blob:http://127.0.0.1:8084/5408a91d-b9b2-4202-a18c-cd5f2be94681", + "blob:http://127.0.0.1:8084/cb2016e7-f296-4347-9932-16dd4bed81e2", + "blob:http://127.0.0.1:8084/b54c6aca-e6b2-48ca-9cc0-ef6d77e9b9cf", + "blob:http://127.0.0.1:8084/e4d05b45-9a39-44b8-8299-85a8a9aa6304", + "blob:http://127.0.0.1:8084/fee95ec3-256c-4d67-a71e-5f497e2cbb5b", + "blob:http://127.0.0.1:8084/234501b2-ce83-4525-82d7-022c998d4ec6", + "blob:http://127.0.0.1:8084/eb0e3986-4456-49db-935c-6b55d62592bc", + "blob:http://127.0.0.1:8084/df9f7352-07c1-464f-9e33-627ecc29a7fd", + "blob:http://127.0.0.1:8084/4b4d9cb9-2044-4e1e-95e2-3a967376d063", + "blob:http://127.0.0.1:8084/cb851102-60ba-4add-b222-08a354a38739", + "blob:http://127.0.0.1:8084/418e3fec-6112-4d3a-935a-802b43c9fac9", + "blob:http://127.0.0.1:8084/5a9d6ac8-83d7-4dec-8769-1086a92e9ed8", + "blob:http://127.0.0.1:8084/b9a87f87-0fec-4b5e-92be-0873f4cfe33e", + "blob:http://127.0.0.1:8084/ed8657f8-a152-44f2-b152-389cfd12c1fd", + "blob:http://127.0.0.1:8084/ca198d08-6308-4d1a-b3ce-5b41711e3feb", + "blob:http://127.0.0.1:8084/ab8d4436-8bd2-46f7-9553-4d5f0abc525e", + "blob:http://127.0.0.1:8084/95139b5c-35fa-41ea-9e21-b2f61cbcaff3", + "blob:http://127.0.0.1:8084/8b1947b0-fca4-4faa-9879-32eace697485" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/cdb9e5fd-335b-428d-8e31-c7ae7416bdf1", + "blob:http://127.0.0.1:8084/1de7b0f9-e6d5-401c-a8e6-48507818a2ae", + "blob:http://127.0.0.1:8084/5408a91d-b9b2-4202-a18c-cd5f2be94681", + "blob:http://127.0.0.1:8084/cb2016e7-f296-4347-9932-16dd4bed81e2", + "blob:http://127.0.0.1:8084/b54c6aca-e6b2-48ca-9cc0-ef6d77e9b9cf", + "blob:http://127.0.0.1:8084/e4d05b45-9a39-44b8-8299-85a8a9aa6304", + "blob:http://127.0.0.1:8084/fee95ec3-256c-4d67-a71e-5f497e2cbb5b", + "blob:http://127.0.0.1:8084/234501b2-ce83-4525-82d7-022c998d4ec6", + "blob:http://127.0.0.1:8084/eb0e3986-4456-49db-935c-6b55d62592bc", + "blob:http://127.0.0.1:8084/df9f7352-07c1-464f-9e33-627ecc29a7fd", + "blob:http://127.0.0.1:8084/4b4d9cb9-2044-4e1e-95e2-3a967376d063", + "blob:http://127.0.0.1:8084/cb851102-60ba-4add-b222-08a354a38739", + "blob:http://127.0.0.1:8084/418e3fec-6112-4d3a-935a-802b43c9fac9", + "blob:http://127.0.0.1:8084/5a9d6ac8-83d7-4dec-8769-1086a92e9ed8", + "blob:http://127.0.0.1:8084/b9a87f87-0fec-4b5e-92be-0873f4cfe33e", + "blob:http://127.0.0.1:8084/ed8657f8-a152-44f2-b152-389cfd12c1fd", + "blob:http://127.0.0.1:8084/ca198d08-6308-4d1a-b3ce-5b41711e3feb", + "blob:http://127.0.0.1:8084/ab8d4436-8bd2-46f7-9553-4d5f0abc525e", + "blob:http://127.0.0.1:8084/95139b5c-35fa-41ea-9e21-b2f61cbcaff3", + "blob:http://127.0.0.1:8084/7151421c-ccdd-4b3f-ba94-7cac52808f18", + "blob:http://127.0.0.1:8084/8b1947b0-fca4-4faa-9879-32eace697485" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 184, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 222, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/3437072a-8963-4125-81fd-add9aefe787a" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/3437072a-8963-4125-81fd-add9aefe787a", + "blob:http://127.0.0.1:8084/c17e9109-9ff2-4d67-90ab-bc45cd45d2b0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3437072a-8963-4125-81fd-add9aefe787a", + "blob:http://127.0.0.1:8084/c17e9109-9ff2-4d67-90ab-bc45cd45d2b0" + ] + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "chromium", + "status": "expected", + "duration": 600, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/f817e55f-0052-4608-8817-2d2d5a96aeea" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f817e55f-0052-4608-8817-2d2d5a96aeea" + ], + "pendingState": "rejected", + "events": [ + "file", + "canvas", + "video", + "destroy" + ], + "connected": false, + "bytes": 7540 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "chromium", + "status": "expected", + "duration": 4306, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 7540, + "source": "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "updateSources": [ + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe" + ], + "created": [ + "blob:http://127.0.0.1:8084/3302f4e3-b601-4189-8d91-3d7fd55c1ecb", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/eb283bae-e39c-43ac-bd0f-834e23ff4e93", + "blob:http://127.0.0.1:8084/6a852005-858f-45ba-be24-b0a24b8b92b2", + "blob:http://127.0.0.1:8084/57f52b52-c60e-4449-8576-0faef623ac11", + "blob:http://127.0.0.1:8084/ffdaeaaf-eabc-4ca7-ad0d-6f4bea8f35aa", + "blob:http://127.0.0.1:8084/0777ca9c-ab99-4d10-9437-a9152dfb2071", + "blob:http://127.0.0.1:8084/ff0513a6-2bb1-448c-a17c-f3f4725112b0", + "blob:http://127.0.0.1:8084/79649d60-ab0c-456a-b43f-06f7eded8fe3", + "blob:http://127.0.0.1:8084/ae0afe69-ce42-4b80-9e53-c900f8710fa2", + "blob:http://127.0.0.1:8084/93b87b17-a225-4bd2-b107-7553c4d19888", + "blob:http://127.0.0.1:8084/9887dd10-92c4-4e20-b097-2b5f3330f1c0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3302f4e3-b601-4189-8d91-3d7fd55c1ecb", + "blob:http://127.0.0.1:8084/eb283bae-e39c-43ac-bd0f-834e23ff4e93", + "blob:http://127.0.0.1:8084/6a852005-858f-45ba-be24-b0a24b8b92b2", + "blob:http://127.0.0.1:8084/57f52b52-c60e-4449-8576-0faef623ac11", + "blob:http://127.0.0.1:8084/ffdaeaaf-eabc-4ca7-ad0d-6f4bea8f35aa", + "blob:http://127.0.0.1:8084/0777ca9c-ab99-4d10-9437-a9152dfb2071", + "blob:http://127.0.0.1:8084/ff0513a6-2bb1-448c-a17c-f3f4725112b0", + "blob:http://127.0.0.1:8084/79649d60-ab0c-456a-b43f-06f7eded8fe3", + "blob:http://127.0.0.1:8084/ae0afe69-ce42-4b80-9e53-c900f8710fa2", + "blob:http://127.0.0.1:8084/93b87b17-a225-4bd2-b107-7553c4d19888", + "blob:http://127.0.0.1:8084/79ff53f0-13f6-49dd-abbf-719babc00ebe", + "blob:http://127.0.0.1:8084/9887dd10-92c4-4e20-b097-2b5f3330f1c0" + ], + "events": [ + "file", + "canvas", + "video", + "file", + "canvas", + "video", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 8515, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 206, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 518, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 710, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 1020, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1323, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1643, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1980, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2288, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2595, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2907, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3223, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3535, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3844, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4453, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 206, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 518, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 710, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 1020, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1323, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1643, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1980, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2288, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2595, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2907, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3223, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3535, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3844, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4453, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4456, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 4760, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 5095, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5408, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5729, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 6038, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6352, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6664, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 6994, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 7303, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7605, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 8227, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 8228, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/efab9da2-a4e3-4d47-9837-9620090ca499", + "second": "blob:http://127.0.0.1:8084/06e791dc-b533-4299-8c01-13031b0ffccc", + "created": [ + "blob:http://127.0.0.1:8084/efabba7d-d0b8-4522-a82b-fe71357cd557", + "blob:http://127.0.0.1:8084/b59dab15-c7d8-40ec-885f-923c6a225983", + "blob:http://127.0.0.1:8084/bad7241b-2ecf-40f5-9f4f-8b0367cf110a", + "blob:http://127.0.0.1:8084/c477bf06-232c-4d95-8c47-02547237ade5", + "blob:http://127.0.0.1:8084/4c6dd665-dbf6-425c-9a07-d247da85fdba", + "blob:http://127.0.0.1:8084/1abb99b9-8296-4118-8fda-60863a641d59", + "blob:http://127.0.0.1:8084/a44d87b8-a89a-49b5-80a8-58410cf17c27", + "blob:http://127.0.0.1:8084/0ebd5b2c-b0f2-4c02-9474-361599d48733", + "blob:http://127.0.0.1:8084/2b9776de-4612-4e6c-9022-c4c8375af0ed", + "blob:http://127.0.0.1:8084/ccabc632-3ddf-4668-ab8c-3168c4464957", + "blob:http://127.0.0.1:8084/efab9da2-a4e3-4d47-9837-9620090ca499", + "blob:http://127.0.0.1:8084/edd6bdc8-be44-4ab7-944c-1091bfde3bdc", + "blob:http://127.0.0.1:8084/9491d54e-490f-495c-9606-e30e52fa3a51", + "blob:http://127.0.0.1:8084/568db428-358f-490a-8857-84a154151e1c", + "blob:http://127.0.0.1:8084/6a81cf01-44de-42f8-a695-5cdafd4356e1", + "blob:http://127.0.0.1:8084/8203e389-31dd-4d02-8dc6-c7c779983ec2", + "blob:http://127.0.0.1:8084/dca4f2a9-50fb-4e91-b85b-35729bbc49fc", + "blob:http://127.0.0.1:8084/a4ebdd1d-7b74-4897-9584-9b44343c4166", + "blob:http://127.0.0.1:8084/42b00972-7bc2-4a10-8645-117ab6cbcc81", + "blob:http://127.0.0.1:8084/95cec45d-4abf-4ee4-9e72-18fc14a1ef76", + "blob:http://127.0.0.1:8084/06e791dc-b533-4299-8c01-13031b0ffccc" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/b59dab15-c7d8-40ec-885f-923c6a225983", + "blob:http://127.0.0.1:8084/bad7241b-2ecf-40f5-9f4f-8b0367cf110a", + "blob:http://127.0.0.1:8084/c477bf06-232c-4d95-8c47-02547237ade5", + "blob:http://127.0.0.1:8084/4c6dd665-dbf6-425c-9a07-d247da85fdba", + "blob:http://127.0.0.1:8084/1abb99b9-8296-4118-8fda-60863a641d59", + "blob:http://127.0.0.1:8084/a44d87b8-a89a-49b5-80a8-58410cf17c27", + "blob:http://127.0.0.1:8084/0ebd5b2c-b0f2-4c02-9474-361599d48733", + "blob:http://127.0.0.1:8084/2b9776de-4612-4e6c-9022-c4c8375af0ed", + "blob:http://127.0.0.1:8084/ccabc632-3ddf-4668-ab8c-3168c4464957", + "blob:http://127.0.0.1:8084/efab9da2-a4e3-4d47-9837-9620090ca499", + "blob:http://127.0.0.1:8084/edd6bdc8-be44-4ab7-944c-1091bfde3bdc", + "blob:http://127.0.0.1:8084/9491d54e-490f-495c-9606-e30e52fa3a51", + "blob:http://127.0.0.1:8084/568db428-358f-490a-8857-84a154151e1c", + "blob:http://127.0.0.1:8084/6a81cf01-44de-42f8-a695-5cdafd4356e1", + "blob:http://127.0.0.1:8084/8203e389-31dd-4d02-8dc6-c7c779983ec2", + "blob:http://127.0.0.1:8084/dca4f2a9-50fb-4e91-b85b-35729bbc49fc", + "blob:http://127.0.0.1:8084/a4ebdd1d-7b74-4897-9584-9b44343c4166", + "blob:http://127.0.0.1:8084/42b00972-7bc2-4a10-8645-117ab6cbcc81", + "blob:http://127.0.0.1:8084/95cec45d-4abf-4ee4-9e72-18fc14a1ef76", + "blob:http://127.0.0.1:8084/efabba7d-d0b8-4522-a82b-fe71357cd557", + "blob:http://127.0.0.1:8084/06e791dc-b533-4299-8c01-13031b0ffccc" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 401, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 446, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/1e733bbd-0751-4b70-9344-b45af036aad5" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/1e733bbd-0751-4b70-9344-b45af036aad5", + "blob:http://127.0.0.1:8084/c4270f53-d4b1-491b-ad51-5b27117edc94" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1e733bbd-0751-4b70-9344-b45af036aad5", + "blob:http://127.0.0.1:8084/c4270f53-d4b1-491b-ad51-5b27117edc94" + ] + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "firefox", + "status": "expected", + "duration": 803, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/535e6df3-34d9-4dfa-a5ab-fd598b39082d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/535e6df3-34d9-4dfa-a5ab-fd598b39082d" + ], + "pendingState": "rejected", + "events": [ + "file", + "canvas", + "video", + "destroy" + ], + "connected": false, + "bytes": 4980 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "firefox", + "status": "expected", + "duration": 5022, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 4980, + "source": "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "updateSources": [ + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd" + ], + "created": [ + "blob:http://127.0.0.1:8084/900182f9-65ae-481e-b64e-9fd223fd7d1a", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/45bc1f6c-3798-4fcd-9f56-2a356f26025d", + "blob:http://127.0.0.1:8084/c9c13796-514a-4d25-8b24-fbb4ec14f418", + "blob:http://127.0.0.1:8084/9138baf9-a848-4826-9746-f167ff25d20b", + "blob:http://127.0.0.1:8084/b56d5f88-6dc7-4ab7-86df-af5ed7090362", + "blob:http://127.0.0.1:8084/abbfcc9a-1eff-4c0e-a1fd-37c4b9a6db61", + "blob:http://127.0.0.1:8084/a2ae04c2-e472-43ce-9a93-0ddb284a804f", + "blob:http://127.0.0.1:8084/f2d42928-d9a6-48b1-a33f-588f4728e525", + "blob:http://127.0.0.1:8084/93d854f8-0dc2-4862-9ac8-4ba5a7e4d6e5", + "blob:http://127.0.0.1:8084/e666e7ae-7294-49db-9896-f246d0f7682e", + "blob:http://127.0.0.1:8084/be18039c-86e4-4dc5-a0f5-ae898eb18ec1" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/900182f9-65ae-481e-b64e-9fd223fd7d1a", + "blob:http://127.0.0.1:8084/45bc1f6c-3798-4fcd-9f56-2a356f26025d", + "blob:http://127.0.0.1:8084/c9c13796-514a-4d25-8b24-fbb4ec14f418", + "blob:http://127.0.0.1:8084/9138baf9-a848-4826-9746-f167ff25d20b", + "blob:http://127.0.0.1:8084/b56d5f88-6dc7-4ab7-86df-af5ed7090362", + "blob:http://127.0.0.1:8084/abbfcc9a-1eff-4c0e-a1fd-37c4b9a6db61", + "blob:http://127.0.0.1:8084/a2ae04c2-e472-43ce-9a93-0ddb284a804f", + "blob:http://127.0.0.1:8084/f2d42928-d9a6-48b1-a33f-588f4728e525", + "blob:http://127.0.0.1:8084/93d854f8-0dc2-4862-9ac8-4ba5a7e4d6e5", + "blob:http://127.0.0.1:8084/e666e7ae-7294-49db-9896-f246d0f7682e", + "blob:http://127.0.0.1:8084/1a8be6aa-2302-4780-ba8d-500db015cabd", + "blob:http://127.0.0.1:8084/be18039c-86e4-4dc5-a0f5-ae898eb18ec1" + ], + "events": [ + "file", + "canvas", + "video", + "file", + "canvas", + "video", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1225, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1187, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1193, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "webkit", + "status": "expected", + "duration": 1185, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "webkit", + "status": "expected", + "duration": 1151, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 527, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 107, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 107.19999998807907, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 124.60000002384186, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 145.30000001192093, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 153.10000002384186, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 178.30000001192093, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 191.30000001192093, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 195.39999997615814, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 202.30000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 226.39999997615814, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 246.60000002384186, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 250.19999998807907, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 257.39999997615814, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 257.39999997615814, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 107, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 107.19999998807907, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 124.60000002384186, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 145.30000001192093, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 153.10000002384186, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 178.30000001192093, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 191.30000001192093, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 195.39999997615814, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 202.30000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 226.39999997615814, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 246.60000002384186, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 250.19999998807907, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 257.39999997615814, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 257.39999997615814, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 261.10000002384186, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 278.69999998807907, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 286, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 311.80000001192093, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 324.60000002384186, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 350.5, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 378.80000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 390.39999997615814, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 412.19999998807907, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 415.80000001192093, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 425.89999997615814, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 426, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 427.30000001192093, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/0dd323b8-c3d8-4662-8b48-7afdc34539b6", + "second": "blob:http://127.0.0.1:8084/07ae78c5-804f-4683-8e47-d81ab2787043", + "created": [ + "blob:http://127.0.0.1:8084/cce32b62-8e91-43c7-9061-00033a648a48", + "blob:http://127.0.0.1:8084/80ba3c34-9c74-4c19-812e-e2313fa913e3", + "blob:http://127.0.0.1:8084/adc9d494-ef47-4392-8ecc-202c6fbc0af3", + "blob:http://127.0.0.1:8084/9c2d2bef-94b8-4463-8454-2a9096628138", + "blob:http://127.0.0.1:8084/de9c35f4-ed73-496c-9d36-b96cc8f734d8", + "blob:http://127.0.0.1:8084/27d18b79-aeee-497c-85e0-c4003f6e9b65", + "blob:http://127.0.0.1:8084/5bf84033-ac7c-4b42-9f10-6955a77b3748", + "blob:http://127.0.0.1:8084/a5e7ade9-54a1-4867-b799-d2ff4f76fb3b", + "blob:http://127.0.0.1:8084/ea9bcb1d-ea45-42f7-aa86-d2325f408ca5", + "blob:http://127.0.0.1:8084/821a0f91-9e05-46a1-bf55-d196ece45c3f", + "blob:http://127.0.0.1:8084/0dd323b8-c3d8-4662-8b48-7afdc34539b6", + "blob:http://127.0.0.1:8084/6a01032d-1188-47a4-ae56-3f021445b413", + "blob:http://127.0.0.1:8084/d14acfba-926a-49dc-9947-e68f4a6a61c7", + "blob:http://127.0.0.1:8084/8837d993-a98a-471b-bdd3-58baa3b132d8", + "blob:http://127.0.0.1:8084/11a18ec2-bb86-448f-9440-1d59433ca09c", + "blob:http://127.0.0.1:8084/3ba0c329-24fb-49ab-aa3f-86039230d690", + "blob:http://127.0.0.1:8084/2a5fabea-62bc-4aeb-8a4f-a4ec7567b257", + "blob:http://127.0.0.1:8084/f45d8dd4-51cf-4720-8682-a1a9e975653e", + "blob:http://127.0.0.1:8084/6f785b6a-6af3-4660-8650-27628b9afeec", + "blob:http://127.0.0.1:8084/ff80534d-1c96-4fee-ac50-8392c9c3ac40", + "blob:http://127.0.0.1:8084/07ae78c5-804f-4683-8e47-d81ab2787043" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/80ba3c34-9c74-4c19-812e-e2313fa913e3", + "blob:http://127.0.0.1:8084/adc9d494-ef47-4392-8ecc-202c6fbc0af3", + "blob:http://127.0.0.1:8084/9c2d2bef-94b8-4463-8454-2a9096628138", + "blob:http://127.0.0.1:8084/de9c35f4-ed73-496c-9d36-b96cc8f734d8", + "blob:http://127.0.0.1:8084/27d18b79-aeee-497c-85e0-c4003f6e9b65", + "blob:http://127.0.0.1:8084/5bf84033-ac7c-4b42-9f10-6955a77b3748", + "blob:http://127.0.0.1:8084/a5e7ade9-54a1-4867-b799-d2ff4f76fb3b", + "blob:http://127.0.0.1:8084/ea9bcb1d-ea45-42f7-aa86-d2325f408ca5", + "blob:http://127.0.0.1:8084/821a0f91-9e05-46a1-bf55-d196ece45c3f", + "blob:http://127.0.0.1:8084/0dd323b8-c3d8-4662-8b48-7afdc34539b6", + "blob:http://127.0.0.1:8084/6a01032d-1188-47a4-ae56-3f021445b413", + "blob:http://127.0.0.1:8084/d14acfba-926a-49dc-9947-e68f4a6a61c7", + "blob:http://127.0.0.1:8084/8837d993-a98a-471b-bdd3-58baa3b132d8", + "blob:http://127.0.0.1:8084/11a18ec2-bb86-448f-9440-1d59433ca09c", + "blob:http://127.0.0.1:8084/3ba0c329-24fb-49ab-aa3f-86039230d690", + "blob:http://127.0.0.1:8084/2a5fabea-62bc-4aeb-8a4f-a4ec7567b257", + "blob:http://127.0.0.1:8084/f45d8dd4-51cf-4720-8682-a1a9e975653e", + "blob:http://127.0.0.1:8084/6f785b6a-6af3-4660-8650-27628b9afeec", + "blob:http://127.0.0.1:8084/ff80534d-1c96-4fee-ac50-8392c9c3ac40", + "blob:http://127.0.0.1:8084/cce32b62-8e91-43c7-9061-00033a648a48", + "blob:http://127.0.0.1:8084/07ae78c5-804f-4683-8e47-d81ab2787043" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 199, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 223, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/d01c2684-f3f2-4fe7-8131-7fe2e3d055e3" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/d01c2684-f3f2-4fe7-8131-7fe2e3d055e3", + "blob:http://127.0.0.1:8084/cfddee95-1864-469d-8e56-b694066e5c65" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d01c2684-f3f2-4fe7-8131-7fe2e3d055e3", + "blob:http://127.0.0.1:8084/cfddee95-1864-469d-8e56-b694066e5c65" + ] + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "chromium", + "status": "expected", + "duration": 337, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/98ce7999-7c53-4f07-af16-6c9cb6ef43c2" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/98ce7999-7c53-4f07-af16-6c9cb6ef43c2" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "chromium", + "status": "expected", + "duration": 474, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "updateSources": [ + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76" + ], + "created": [ + "blob:http://127.0.0.1:8084/edfabe91-83f4-4929-8fbe-645bf2e3276f", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/7fb92311-15b5-4a44-aa37-a58bc125b248", + "blob:http://127.0.0.1:8084/04ff8ece-d82a-44e8-a680-357fcc7d54ec", + "blob:http://127.0.0.1:8084/41e9a58a-09b1-47f2-8b49-a14796d6d041", + "blob:http://127.0.0.1:8084/bf752387-e533-4e66-907a-c02b78def57e", + "blob:http://127.0.0.1:8084/4cc9001c-4aa3-4195-9675-a665ec1f099b", + "blob:http://127.0.0.1:8084/87f4c9b4-5025-4ed2-8167-c287a8186f4e", + "blob:http://127.0.0.1:8084/08bce62b-909a-4ee7-8e97-42fbaa143e58", + "blob:http://127.0.0.1:8084/7519d6e5-964a-4e5b-b0d0-5a9e77cefd96", + "blob:http://127.0.0.1:8084/4346a2e0-e3b4-4fb7-9da3-7a5d78a49fc4", + "blob:http://127.0.0.1:8084/1782aedf-8044-4652-8bbf-8913b1afcc9e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/edfabe91-83f4-4929-8fbe-645bf2e3276f", + "blob:http://127.0.0.1:8084/7fb92311-15b5-4a44-aa37-a58bc125b248", + "blob:http://127.0.0.1:8084/04ff8ece-d82a-44e8-a680-357fcc7d54ec", + "blob:http://127.0.0.1:8084/41e9a58a-09b1-47f2-8b49-a14796d6d041", + "blob:http://127.0.0.1:8084/bf752387-e533-4e66-907a-c02b78def57e", + "blob:http://127.0.0.1:8084/4cc9001c-4aa3-4195-9675-a665ec1f099b", + "blob:http://127.0.0.1:8084/87f4c9b4-5025-4ed2-8167-c287a8186f4e", + "blob:http://127.0.0.1:8084/08bce62b-909a-4ee7-8e97-42fbaa143e58", + "blob:http://127.0.0.1:8084/7519d6e5-964a-4e5b-b0d0-5a9e77cefd96", + "blob:http://127.0.0.1:8084/4346a2e0-e3b4-4fb7-9da3-7a5d78a49fc4", + "blob:http://127.0.0.1:8084/14733b5d-c2dc-4f6f-9dc2-dad33788bb76", + "blob:http://127.0.0.1:8084/1782aedf-8044-4652-8bbf-8913b1afcc9e" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 1500, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 175, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 176, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 298, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 317, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 363, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 426, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 509, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 527, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 566, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 625, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 709, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 729, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 771, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 771, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 175, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 176, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 298, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 317, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 363, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 426, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 509, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 527, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 566, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 625, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 709, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 729, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 771, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 771, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 773, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 791, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 830, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 892, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 975, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 991, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 1028, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 1093, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 1181, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 1201, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 1247, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 1247, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 1247, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/277f9b33-66f3-4241-8083-95f06e792425", + "second": "blob:http://127.0.0.1:8084/f8ce24fd-cc1b-42a2-937c-2c7574f25c7c", + "created": [ + "blob:http://127.0.0.1:8084/ad6b3d6c-f995-4e55-9c0f-aac58bd95c49", + "blob:http://127.0.0.1:8084/1a05307e-7c98-42fe-8b52-b1fe1f6e47e6", + "blob:http://127.0.0.1:8084/ad565504-1b5c-4e48-b1f6-29492b76e959", + "blob:http://127.0.0.1:8084/9308e450-23ef-4548-8606-4197b75fac22", + "blob:http://127.0.0.1:8084/f18a9966-9ddb-4f0c-aa30-f55dea184189", + "blob:http://127.0.0.1:8084/d6c1804a-bad7-4b5a-b9cf-e4ba2c2070c1", + "blob:http://127.0.0.1:8084/c427d126-2d99-4614-bd77-5d013f566d02", + "blob:http://127.0.0.1:8084/19832468-4416-44fd-b496-d51b01f082bb", + "blob:http://127.0.0.1:8084/e0203ff9-a00d-4e26-8f63-73d1e65f5dea", + "blob:http://127.0.0.1:8084/9bb9d3e4-7b79-4a42-b85d-ac67b49be8d0", + "blob:http://127.0.0.1:8084/277f9b33-66f3-4241-8083-95f06e792425", + "blob:http://127.0.0.1:8084/eace7c26-7a01-4867-8a1a-ee7e75c5f79e", + "blob:http://127.0.0.1:8084/a08c5a98-4966-4161-aade-934493f2ac04", + "blob:http://127.0.0.1:8084/07ce4c53-d582-421c-a1eb-860213ef4ef7", + "blob:http://127.0.0.1:8084/b634fdc2-7292-4af8-b21c-dc0072f7e10c", + "blob:http://127.0.0.1:8084/658ef1af-6fb1-46fd-aae0-f92f4f1c58a4", + "blob:http://127.0.0.1:8084/cab2724c-f577-48fc-bc04-c76381ebb290", + "blob:http://127.0.0.1:8084/b2a5a6f0-b524-4054-a02c-a10555a80990", + "blob:http://127.0.0.1:8084/3c5f923b-9696-4eb7-a5e1-0a9263216983", + "blob:http://127.0.0.1:8084/b09cfdd1-a250-47d0-a61d-d35b617171f1", + "blob:http://127.0.0.1:8084/f8ce24fd-cc1b-42a2-937c-2c7574f25c7c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1a05307e-7c98-42fe-8b52-b1fe1f6e47e6", + "blob:http://127.0.0.1:8084/ad565504-1b5c-4e48-b1f6-29492b76e959", + "blob:http://127.0.0.1:8084/9308e450-23ef-4548-8606-4197b75fac22", + "blob:http://127.0.0.1:8084/f18a9966-9ddb-4f0c-aa30-f55dea184189", + "blob:http://127.0.0.1:8084/d6c1804a-bad7-4b5a-b9cf-e4ba2c2070c1", + "blob:http://127.0.0.1:8084/c427d126-2d99-4614-bd77-5d013f566d02", + "blob:http://127.0.0.1:8084/19832468-4416-44fd-b496-d51b01f082bb", + "blob:http://127.0.0.1:8084/e0203ff9-a00d-4e26-8f63-73d1e65f5dea", + "blob:http://127.0.0.1:8084/9bb9d3e4-7b79-4a42-b85d-ac67b49be8d0", + "blob:http://127.0.0.1:8084/277f9b33-66f3-4241-8083-95f06e792425", + "blob:http://127.0.0.1:8084/eace7c26-7a01-4867-8a1a-ee7e75c5f79e", + "blob:http://127.0.0.1:8084/a08c5a98-4966-4161-aade-934493f2ac04", + "blob:http://127.0.0.1:8084/07ce4c53-d582-421c-a1eb-860213ef4ef7", + "blob:http://127.0.0.1:8084/b634fdc2-7292-4af8-b21c-dc0072f7e10c", + "blob:http://127.0.0.1:8084/658ef1af-6fb1-46fd-aae0-f92f4f1c58a4", + "blob:http://127.0.0.1:8084/cab2724c-f577-48fc-bc04-c76381ebb290", + "blob:http://127.0.0.1:8084/b2a5a6f0-b524-4054-a02c-a10555a80990", + "blob:http://127.0.0.1:8084/3c5f923b-9696-4eb7-a5e1-0a9263216983", + "blob:http://127.0.0.1:8084/b09cfdd1-a250-47d0-a61d-d35b617171f1", + "blob:http://127.0.0.1:8084/ad6b3d6c-f995-4e55-9c0f-aac58bd95c49", + "blob:http://127.0.0.1:8084/f8ce24fd-cc1b-42a2-937c-2c7574f25c7c" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 461, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 484, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/028439cd-5c3a-4e5d-bd6c-48329682b27d" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/028439cd-5c3a-4e5d-bd6c-48329682b27d", + "blob:http://127.0.0.1:8084/7dde9d12-9555-4d6d-9851-c0f5f614e2cf" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/028439cd-5c3a-4e5d-bd6c-48329682b27d", + "blob:http://127.0.0.1:8084/7dde9d12-9555-4d6d-9851-c0f5f614e2cf" + ] + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "firefox", + "status": "expected", + "duration": 588, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/aa84aa42-4ce0-4bfa-a73b-b30204271c73" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/aa84aa42-4ce0-4bfa-a73b-b30204271c73" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "firefox", + "status": "expected", + "duration": 1072, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "updateSources": [ + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687" + ], + "created": [ + "blob:http://127.0.0.1:8084/69247ea6-d59c-4175-b18d-d8c492685cdc", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/7442cb41-8845-470f-bc71-12b6f82465b1", + "blob:http://127.0.0.1:8084/bbb2849c-71c1-4378-bbdd-6dd66aacf9e6", + "blob:http://127.0.0.1:8084/58b24305-715b-4c63-8922-6d1eabfd69fa", + "blob:http://127.0.0.1:8084/5a9fbc6e-1d4e-41ec-b705-7e4aed3d2403", + "blob:http://127.0.0.1:8084/bcbf4d8f-1018-467b-9502-c7ad9951a4b1", + "blob:http://127.0.0.1:8084/d7111b31-512e-48a4-aba3-712f143b2b9b", + "blob:http://127.0.0.1:8084/1e01c810-bd4b-4d12-a5df-9ae376565c15", + "blob:http://127.0.0.1:8084/027d60b5-b111-43ad-aede-4e2d53efb6dd", + "blob:http://127.0.0.1:8084/5dfc8c18-c09a-41c2-841d-060a743c89a2", + "blob:http://127.0.0.1:8084/5bd11a7d-f7fa-4b0b-b931-e98a8eb5e9e6" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/69247ea6-d59c-4175-b18d-d8c492685cdc", + "blob:http://127.0.0.1:8084/7442cb41-8845-470f-bc71-12b6f82465b1", + "blob:http://127.0.0.1:8084/bbb2849c-71c1-4378-bbdd-6dd66aacf9e6", + "blob:http://127.0.0.1:8084/58b24305-715b-4c63-8922-6d1eabfd69fa", + "blob:http://127.0.0.1:8084/5a9fbc6e-1d4e-41ec-b705-7e4aed3d2403", + "blob:http://127.0.0.1:8084/bcbf4d8f-1018-467b-9502-c7ad9951a4b1", + "blob:http://127.0.0.1:8084/d7111b31-512e-48a4-aba3-712f143b2b9b", + "blob:http://127.0.0.1:8084/1e01c810-bd4b-4d12-a5df-9ae376565c15", + "blob:http://127.0.0.1:8084/027d60b5-b111-43ad-aede-4e2d53efb6dd", + "blob:http://127.0.0.1:8084/5dfc8c18-c09a-41c2-841d-060a743c89a2", + "blob:http://127.0.0.1:8084/e74b93be-a0bf-4743-900f-6ef550bd9687", + "blob:http://127.0.0.1:8084/5bd11a7d-f7fa-4b0b-b931-e98a8eb5e9e6" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1160, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1203, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1196, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "webkit", + "status": "expected", + "duration": 1298, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "webkit", + "status": "expected", + "duration": 1159, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-source-workspace-policy", + "sha256": "79bbb3b36e9b1842f825c3ce29d0e113a99a2c788942d29859d65a93132c1b0f", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + } + ] + }, + "installed": { + "report": "refactor/.cache/thumbnail-policy-browser-installed-final/report.json", + "sha256": "3378f8f4985b63b13a851bfbd6565336d5f5af9efe93059f24d7bdcfac50a3fb", + "stats": { + "startTime": "2026-09-15T15:50:16.177Z", + "duration": 33414.154, + "expected": 30, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "capabilityControlCases": 10, + "cases": [ + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 8146, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 141.89999997615814, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 444.2999999523163, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 552.3999999761581, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 867.6999999880791, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1171.8999999761581, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1481.199999988079, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1786, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2101.7999999523163, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2420.399999976158, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2723.5, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3037.5, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3351.099999964237, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3660.2999999523163, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4263.599999964237, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 141.89999997615814, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 444.2999999523163, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 552.3999999761581, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 867.6999999880791, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1171.8999999761581, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1481.199999988079, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1786, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2101.7999999523163, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2420.399999976158, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 2723.5, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3037.5, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3351.099999964237, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 3660.2999999523163, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4263.599999964237, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4273, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 4589.199999988079, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 4896.399999976158, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5202.899999976158, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5514, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 5824.399999976158, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6140.299999952316, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6442.199999988079, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 6750.899999976158, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 7065.299999952316, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7379, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 7985.399999976158, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 7986.199999988079, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/5cc2af48-5290-4d84-9ff0-69074a89f9c7", + "second": "blob:http://127.0.0.1:8084/fbe964ec-56c0-4600-bd65-6ebad4e48ba5", + "created": [ + "blob:http://127.0.0.1:8084/45917405-e4cb-469e-b603-95b3641d7053", + "blob:http://127.0.0.1:8084/67b4ebfe-9bb5-4e00-ae98-30f96eb68db8", + "blob:http://127.0.0.1:8084/88ba0e99-7cb2-404c-b1ec-1367a9ab891c", + "blob:http://127.0.0.1:8084/f518dd2b-2328-44ff-bd8e-a7477464a5c8", + "blob:http://127.0.0.1:8084/235bf285-2b72-4a5c-955b-db50ba9a6023", + "blob:http://127.0.0.1:8084/a52c0725-a96a-4b40-9baf-41be83529e19", + "blob:http://127.0.0.1:8084/b0b2e6ca-33a7-41e5-8b53-ae0ab427e73d", + "blob:http://127.0.0.1:8084/110c337f-0558-4d01-ad30-494f947dcf1f", + "blob:http://127.0.0.1:8084/f5c4758d-0220-4e43-8daa-6591a2558ebf", + "blob:http://127.0.0.1:8084/74e5a47c-f6b5-470c-bdf0-6aad91203438", + "blob:http://127.0.0.1:8084/5cc2af48-5290-4d84-9ff0-69074a89f9c7", + "blob:http://127.0.0.1:8084/5afc272f-9bca-41be-bc1b-af10d10585d4", + "blob:http://127.0.0.1:8084/d2f8dc84-f067-4c1e-b4fb-a13235fd2c6c", + "blob:http://127.0.0.1:8084/59007dbc-e69e-4c74-9402-6cd6aa789c7f", + "blob:http://127.0.0.1:8084/3d8440c9-833d-43d0-a64c-34a43dfd6b04", + "blob:http://127.0.0.1:8084/63e85470-cd44-4ed9-93ef-b9461a590472", + "blob:http://127.0.0.1:8084/aa37beeb-eab3-4917-818a-dff9a5de5c65", + "blob:http://127.0.0.1:8084/d8038b67-6f95-4489-b4d9-079f74f08fae", + "blob:http://127.0.0.1:8084/6b83aedd-779e-4c9d-a0dc-2c4c47620ce4", + "blob:http://127.0.0.1:8084/8c03b058-a072-474d-8927-e7ff80ce9f8b", + "blob:http://127.0.0.1:8084/fbe964ec-56c0-4600-bd65-6ebad4e48ba5" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/67b4ebfe-9bb5-4e00-ae98-30f96eb68db8", + "blob:http://127.0.0.1:8084/88ba0e99-7cb2-404c-b1ec-1367a9ab891c", + "blob:http://127.0.0.1:8084/f518dd2b-2328-44ff-bd8e-a7477464a5c8", + "blob:http://127.0.0.1:8084/235bf285-2b72-4a5c-955b-db50ba9a6023", + "blob:http://127.0.0.1:8084/a52c0725-a96a-4b40-9baf-41be83529e19", + "blob:http://127.0.0.1:8084/b0b2e6ca-33a7-41e5-8b53-ae0ab427e73d", + "blob:http://127.0.0.1:8084/110c337f-0558-4d01-ad30-494f947dcf1f", + "blob:http://127.0.0.1:8084/f5c4758d-0220-4e43-8daa-6591a2558ebf", + "blob:http://127.0.0.1:8084/74e5a47c-f6b5-470c-bdf0-6aad91203438", + "blob:http://127.0.0.1:8084/5cc2af48-5290-4d84-9ff0-69074a89f9c7", + "blob:http://127.0.0.1:8084/5afc272f-9bca-41be-bc1b-af10d10585d4", + "blob:http://127.0.0.1:8084/d2f8dc84-f067-4c1e-b4fb-a13235fd2c6c", + "blob:http://127.0.0.1:8084/59007dbc-e69e-4c74-9402-6cd6aa789c7f", + "blob:http://127.0.0.1:8084/3d8440c9-833d-43d0-a64c-34a43dfd6b04", + "blob:http://127.0.0.1:8084/63e85470-cd44-4ed9-93ef-b9461a590472", + "blob:http://127.0.0.1:8084/aa37beeb-eab3-4917-818a-dff9a5de5c65", + "blob:http://127.0.0.1:8084/d8038b67-6f95-4489-b4d9-079f74f08fae", + "blob:http://127.0.0.1:8084/6b83aedd-779e-4c9d-a0dc-2c4c47620ce4", + "blob:http://127.0.0.1:8084/8c03b058-a072-474d-8927-e7ff80ce9f8b", + "blob:http://127.0.0.1:8084/45917405-e4cb-469e-b603-95b3641d7053", + "blob:http://127.0.0.1:8084/fbe964ec-56c0-4600-bd65-6ebad4e48ba5" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 286, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 229, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/c2bd55a3-c52f-40cb-85d4-98fa12c0fe96" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/c2bd55a3-c52f-40cb-85d4-98fa12c0fe96", + "blob:http://127.0.0.1:8084/0ab4229d-a7a3-4840-a22c-8809816b2205" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c2bd55a3-c52f-40cb-85d4-98fa12c0fe96", + "blob:http://127.0.0.1:8084/0ab4229d-a7a3-4840-a22c-8809816b2205" + ] + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "chromium", + "status": "expected", + "duration": 587, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/1ad68518-95f9-4f9f-b269-e40a09cffa1f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1ad68518-95f9-4f9f-b269-e40a09cffa1f" + ], + "pendingState": "rejected", + "events": [ + "file", + "canvas", + "video", + "destroy" + ], + "connected": false, + "bytes": 7540 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "chromium", + "status": "expected", + "duration": 4375, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 7540, + "source": "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "updateSources": [ + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231" + ], + "created": [ + "blob:http://127.0.0.1:8084/ee3da0da-bcb8-4982-8b59-63c38ccbb9af", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/21e44c6e-0c42-444d-9b39-399e9f648987", + "blob:http://127.0.0.1:8084/7091347a-93dd-46cf-94f2-df0496c55b71", + "blob:http://127.0.0.1:8084/831ad60b-2cfd-49a2-978e-0b1c5baa2b56", + "blob:http://127.0.0.1:8084/48a8231d-2515-4398-9781-caf8abc5fcd4", + "blob:http://127.0.0.1:8084/584fab4b-dc42-4958-8f2b-e855d7cec63c", + "blob:http://127.0.0.1:8084/7e091e82-6265-4813-aa78-897329180a40", + "blob:http://127.0.0.1:8084/9c89d334-53b4-4d10-99da-687ded070d1c", + "blob:http://127.0.0.1:8084/dc310949-a83d-4cbb-95f9-49086cffd3f5", + "blob:http://127.0.0.1:8084/45f2185e-916b-4260-addb-4774aed0f4ae", + "blob:http://127.0.0.1:8084/5deed14b-1db7-4337-a00b-1a97ebd5f37c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ee3da0da-bcb8-4982-8b59-63c38ccbb9af", + "blob:http://127.0.0.1:8084/21e44c6e-0c42-444d-9b39-399e9f648987", + "blob:http://127.0.0.1:8084/7091347a-93dd-46cf-94f2-df0496c55b71", + "blob:http://127.0.0.1:8084/831ad60b-2cfd-49a2-978e-0b1c5baa2b56", + "blob:http://127.0.0.1:8084/48a8231d-2515-4398-9781-caf8abc5fcd4", + "blob:http://127.0.0.1:8084/584fab4b-dc42-4958-8f2b-e855d7cec63c", + "blob:http://127.0.0.1:8084/7e091e82-6265-4813-aa78-897329180a40", + "blob:http://127.0.0.1:8084/9c89d334-53b4-4d10-99da-687ded070d1c", + "blob:http://127.0.0.1:8084/dc310949-a83d-4cbb-95f9-49086cffd3f5", + "blob:http://127.0.0.1:8084/45f2185e-916b-4260-addb-4774aed0f4ae", + "blob:http://127.0.0.1:8084/b2014180-bb0c-4cb1-85b0-48a5789a4231", + "blob:http://127.0.0.1:8084/5deed14b-1db7-4337-a00b-1a97ebd5f37c" + ], + "events": [ + "file", + "canvas", + "video", + "file", + "canvas", + "video", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 9312, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "initialEvents": [ + { + "event": "file", + "at": 590, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 904, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 997, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 1304, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1607, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1918, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 2220, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2527, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2842, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 3155, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3471, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3778, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 4085, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4739, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 590, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 904, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 997, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 1304, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 1607, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 1918, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 2220, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 2527, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 2842, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 3155, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 3471, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 3778, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 4085, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 4739, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 4744, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 5059, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 5370, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 5676, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 5991, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 6305, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 6618, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 6920, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 7226, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 7543, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 7858, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 8476, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 8476, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/618429e2-eac9-4ed4-9bfa-acbb0cc1f85a", + "second": "blob:http://127.0.0.1:8084/96580b03-768f-4010-829f-b62657cf10d3", + "created": [ + "blob:http://127.0.0.1:8084/94327d4b-88fd-4520-b30a-61663d41ecfd", + "blob:http://127.0.0.1:8084/73bd721f-2e93-47cf-876d-7f77cc259d6a", + "blob:http://127.0.0.1:8084/00102aae-0a69-4052-a282-c2f95537a5ec", + "blob:http://127.0.0.1:8084/ce30d502-f4de-4f4a-93f6-330aab18f85e", + "blob:http://127.0.0.1:8084/77b9be5e-ba55-4bc0-bcd9-b8a370f2f53c", + "blob:http://127.0.0.1:8084/f2a0b9ff-2098-435c-82ba-4da8b5969f48", + "blob:http://127.0.0.1:8084/6eefd149-31c8-4590-a6fa-06278855939b", + "blob:http://127.0.0.1:8084/9444d0fd-cbfe-456d-b687-27f87996d80a", + "blob:http://127.0.0.1:8084/16a09532-2c53-4dd7-800e-1d92fedce780", + "blob:http://127.0.0.1:8084/e9414e55-056e-47bd-8927-dac9aaa8dd27", + "blob:http://127.0.0.1:8084/618429e2-eac9-4ed4-9bfa-acbb0cc1f85a", + "blob:http://127.0.0.1:8084/9580c585-2777-416c-a32b-7d4d5ddba9bf", + "blob:http://127.0.0.1:8084/23bd92bb-4aeb-46c5-8e00-6afa66800ab3", + "blob:http://127.0.0.1:8084/bf9dae50-d342-4377-96e4-734ea05d74e9", + "blob:http://127.0.0.1:8084/64916acf-0e03-4a8a-a4d5-56fc6e9ba923", + "blob:http://127.0.0.1:8084/98139c6f-3a47-46c7-92bc-bbd7125ee433", + "blob:http://127.0.0.1:8084/9fdfc361-1afd-4214-8b26-a99a81c87827", + "blob:http://127.0.0.1:8084/27fb3bd0-d4d5-47d1-ac62-7c0669d5acfd", + "blob:http://127.0.0.1:8084/e6ba84c1-6f56-4dd8-b73a-362d5788852c", + "blob:http://127.0.0.1:8084/9e96e16c-23b8-46ce-bfb1-c6050ac7a082", + "blob:http://127.0.0.1:8084/96580b03-768f-4010-829f-b62657cf10d3" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/73bd721f-2e93-47cf-876d-7f77cc259d6a", + "blob:http://127.0.0.1:8084/00102aae-0a69-4052-a282-c2f95537a5ec", + "blob:http://127.0.0.1:8084/ce30d502-f4de-4f4a-93f6-330aab18f85e", + "blob:http://127.0.0.1:8084/77b9be5e-ba55-4bc0-bcd9-b8a370f2f53c", + "blob:http://127.0.0.1:8084/f2a0b9ff-2098-435c-82ba-4da8b5969f48", + "blob:http://127.0.0.1:8084/6eefd149-31c8-4590-a6fa-06278855939b", + "blob:http://127.0.0.1:8084/9444d0fd-cbfe-456d-b687-27f87996d80a", + "blob:http://127.0.0.1:8084/16a09532-2c53-4dd7-800e-1d92fedce780", + "blob:http://127.0.0.1:8084/e9414e55-056e-47bd-8927-dac9aaa8dd27", + "blob:http://127.0.0.1:8084/618429e2-eac9-4ed4-9bfa-acbb0cc1f85a", + "blob:http://127.0.0.1:8084/9580c585-2777-416c-a32b-7d4d5ddba9bf", + "blob:http://127.0.0.1:8084/23bd92bb-4aeb-46c5-8e00-6afa66800ab3", + "blob:http://127.0.0.1:8084/bf9dae50-d342-4377-96e4-734ea05d74e9", + "blob:http://127.0.0.1:8084/64916acf-0e03-4a8a-a4d5-56fc6e9ba923", + "blob:http://127.0.0.1:8084/98139c6f-3a47-46c7-92bc-bbd7125ee433", + "blob:http://127.0.0.1:8084/9fdfc361-1afd-4214-8b26-a99a81c87827", + "blob:http://127.0.0.1:8084/27fb3bd0-d4d5-47d1-ac62-7c0669d5acfd", + "blob:http://127.0.0.1:8084/e6ba84c1-6f56-4dd8-b73a-362d5788852c", + "blob:http://127.0.0.1:8084/9e96e16c-23b8-46ce-bfb1-c6050ac7a082", + "blob:http://127.0.0.1:8084/94327d4b-88fd-4520-b30a-61663d41ecfd", + "blob:http://127.0.0.1:8084/96580b03-768f-4010-829f-b62657cf10d3" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 1634, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 551, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/49062676-eee8-4941-989e-dd6d136fd4fb" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/49062676-eee8-4941-989e-dd6d136fd4fb", + "blob:http://127.0.0.1:8084/18a6d4aa-1fe8-4e27-a19e-575c8a0885bd" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/49062676-eee8-4941-989e-dd6d136fd4fb", + "blob:http://127.0.0.1:8084/18a6d4aa-1fe8-4e27-a19e-575c8a0885bd" + ] + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "firefox", + "status": "expected", + "duration": 923, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/41c0b2ca-75fb-4c04-83d2-ffee56deacf7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/41c0b2ca-75fb-4c04-83d2-ffee56deacf7" + ], + "pendingState": "rejected", + "events": [ + "file", + "canvas", + "video", + "destroy" + ], + "connected": false, + "bytes": 4980 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "firefox", + "status": "expected", + "duration": 4650, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 4980, + "source": "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "updateSources": [ + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef" + ], + "created": [ + "blob:http://127.0.0.1:8084/8d35ff50-fb8f-445e-b9e2-47a842baf086", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/8c526591-81f6-4a9e-aca7-76c27294909b", + "blob:http://127.0.0.1:8084/e137e9a6-2dc4-4c53-92bc-0217dbd56533", + "blob:http://127.0.0.1:8084/c41808fd-7780-4822-81bf-197672670c19", + "blob:http://127.0.0.1:8084/f2a44068-e33c-4692-b5d7-ac137ffcb56d", + "blob:http://127.0.0.1:8084/9dfd8c28-723a-43d3-9774-4ebcd9196e95", + "blob:http://127.0.0.1:8084/7695686d-98d7-4e44-a979-6806cf02fa43", + "blob:http://127.0.0.1:8084/098f8a50-17a4-49d5-8994-9c28230a8d24", + "blob:http://127.0.0.1:8084/35e66c49-f13d-410c-9152-e7d89b674d3d", + "blob:http://127.0.0.1:8084/8860bbe5-e3ed-4c61-8712-fa0a3b00cfbb", + "blob:http://127.0.0.1:8084/eeb6a16a-4c9e-47dd-a90b-97565be46f12" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/8d35ff50-fb8f-445e-b9e2-47a842baf086", + "blob:http://127.0.0.1:8084/8c526591-81f6-4a9e-aca7-76c27294909b", + "blob:http://127.0.0.1:8084/e137e9a6-2dc4-4c53-92bc-0217dbd56533", + "blob:http://127.0.0.1:8084/c41808fd-7780-4822-81bf-197672670c19", + "blob:http://127.0.0.1:8084/f2a44068-e33c-4692-b5d7-ac137ffcb56d", + "blob:http://127.0.0.1:8084/9dfd8c28-723a-43d3-9774-4ebcd9196e95", + "blob:http://127.0.0.1:8084/7695686d-98d7-4e44-a979-6806cf02fa43", + "blob:http://127.0.0.1:8084/098f8a50-17a4-49d5-8994-9c28230a8d24", + "blob:http://127.0.0.1:8084/35e66c49-f13d-410c-9152-e7d89b674d3d", + "blob:http://127.0.0.1:8084/8860bbe5-e3ed-4c61-8712-fa0a3b00cfbb", + "blob:http://127.0.0.1:8084/99010661-e317-4da6-954f-ed2c3ea2bcef", + "blob:http://127.0.0.1:8084/eeb6a16a-4c9e-47dd-a90b-97565be46f12" + ], + "events": [ + "file", + "canvas", + "video", + "file", + "canvas", + "video", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1324, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1245, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1226, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "webkit", + "status": "expected", + "duration": 1175, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "webkit", + "status": "expected", + "duration": 1180, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "chromium", + "status": "expected", + "duration": 544, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 98.5, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 98.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 117, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 136.30000001192093, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 144.4000000357628, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 170.69999998807907, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 183.10000002384186, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 202.60000002384186, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 210.80000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 236.19999998807907, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 249.80000001192093, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 272, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 281.19999998807907, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 281.30000001192093, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 98.5, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 98.5, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 117, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 136.30000001192093, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 144.4000000357628, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 170.69999998807907, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 183.10000002384186, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 202.60000002384186, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 210.80000001192093, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 236.19999998807907, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 249.80000001192093, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 272, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 281.19999998807907, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 281.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 284.69999998807907, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 302.69999998807907, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 309.5, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 318.69999998807907, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 335.69999998807907, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 341.60000002384186, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 347.9000000357628, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 370.4000000357628, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 383.19999998807907, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 413.60000002384186, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 436.30000001192093, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 436.30000001192093, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 437.4000000357628, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/4f461443-6b36-4d2e-ad9a-99fa96301091", + "second": "blob:http://127.0.0.1:8084/fc1404fb-0f5a-4568-b855-16dcc8373d85", + "created": [ + "blob:http://127.0.0.1:8084/cd2feda2-5ae0-406b-945e-79ad554535c3", + "blob:http://127.0.0.1:8084/1d287060-39ee-4a42-9b88-92fd12db56d1", + "blob:http://127.0.0.1:8084/5a86d249-7aa8-4d57-8e4d-e16c54fcd899", + "blob:http://127.0.0.1:8084/db8394ae-cff6-4477-a008-efb960c4cbd7", + "blob:http://127.0.0.1:8084/571a92d5-f726-41fc-ad11-91358283125c", + "blob:http://127.0.0.1:8084/83b9a382-da6d-4d71-b815-1a6ab2816aee", + "blob:http://127.0.0.1:8084/c2fd164a-4843-4475-b888-18c429d9087a", + "blob:http://127.0.0.1:8084/cdae350a-461c-40c3-9854-a8508bf4e58a", + "blob:http://127.0.0.1:8084/03bb0efa-528c-4ade-8da4-1510a9592e15", + "blob:http://127.0.0.1:8084/3684601f-e3d8-43de-9eb7-a85becfe5d40", + "blob:http://127.0.0.1:8084/4f461443-6b36-4d2e-ad9a-99fa96301091", + "blob:http://127.0.0.1:8084/2320f7de-2fcc-4f7d-b2f2-45a73542ccf7", + "blob:http://127.0.0.1:8084/49e0b6f6-7abf-4b06-98fc-cb091ec58f6c", + "blob:http://127.0.0.1:8084/5c0e2064-d1fe-48a5-bb57-24ccfb042084", + "blob:http://127.0.0.1:8084/d70e889a-726d-40d0-ba36-0531a4769030", + "blob:http://127.0.0.1:8084/59a4a67a-984f-4a40-98c7-33e35389bdd0", + "blob:http://127.0.0.1:8084/5c9fa5c1-86a0-44a5-9f35-785037bce90b", + "blob:http://127.0.0.1:8084/6575d34b-8e0c-495e-9fd4-32703a3d1090", + "blob:http://127.0.0.1:8084/3f990c7f-70eb-459c-a964-b4521085eb55", + "blob:http://127.0.0.1:8084/c4b6cba7-348f-422e-8ca6-75a179813d3e", + "blob:http://127.0.0.1:8084/fc1404fb-0f5a-4568-b855-16dcc8373d85" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1d287060-39ee-4a42-9b88-92fd12db56d1", + "blob:http://127.0.0.1:8084/5a86d249-7aa8-4d57-8e4d-e16c54fcd899", + "blob:http://127.0.0.1:8084/db8394ae-cff6-4477-a008-efb960c4cbd7", + "blob:http://127.0.0.1:8084/571a92d5-f726-41fc-ad11-91358283125c", + "blob:http://127.0.0.1:8084/83b9a382-da6d-4d71-b815-1a6ab2816aee", + "blob:http://127.0.0.1:8084/c2fd164a-4843-4475-b888-18c429d9087a", + "blob:http://127.0.0.1:8084/cdae350a-461c-40c3-9854-a8508bf4e58a", + "blob:http://127.0.0.1:8084/03bb0efa-528c-4ade-8da4-1510a9592e15", + "blob:http://127.0.0.1:8084/3684601f-e3d8-43de-9eb7-a85becfe5d40", + "blob:http://127.0.0.1:8084/4f461443-6b36-4d2e-ad9a-99fa96301091", + "blob:http://127.0.0.1:8084/2320f7de-2fcc-4f7d-b2f2-45a73542ccf7", + "blob:http://127.0.0.1:8084/49e0b6f6-7abf-4b06-98fc-cb091ec58f6c", + "blob:http://127.0.0.1:8084/5c0e2064-d1fe-48a5-bb57-24ccfb042084", + "blob:http://127.0.0.1:8084/d70e889a-726d-40d0-ba36-0531a4769030", + "blob:http://127.0.0.1:8084/59a4a67a-984f-4a40-98c7-33e35389bdd0", + "blob:http://127.0.0.1:8084/5c9fa5c1-86a0-44a5-9f35-785037bce90b", + "blob:http://127.0.0.1:8084/6575d34b-8e0c-495e-9fd4-32703a3d1090", + "blob:http://127.0.0.1:8084/3f990c7f-70eb-459c-a964-b4521085eb55", + "blob:http://127.0.0.1:8084/c4b6cba7-348f-422e-8ca6-75a179813d3e", + "blob:http://127.0.0.1:8084/cd2feda2-5ae0-406b-945e-79ad554535c3", + "blob:http://127.0.0.1:8084/fc1404fb-0f5a-4568-b855-16dcc8373d85" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "chromium", + "status": "expected", + "duration": 181, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "chromium", + "status": "expected", + "duration": 238, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/3cd52838-e9a5-42d1-be78-b9c21a6205e6" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/3cd52838-e9a5-42d1-be78-b9c21a6205e6", + "blob:http://127.0.0.1:8084/03a09608-8d7d-4332-adba-73409b0f0e6d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3cd52838-e9a5-42d1-be78-b9c21a6205e6", + "blob:http://127.0.0.1:8084/03a09608-8d7d-4332-adba-73409b0f0e6d" + ] + } + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "chromium", + "status": "expected", + "duration": 307, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/e0bcebc3-dd8a-4e19-8d48-aa6f2323930e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e0bcebc3-dd8a-4e19-8d48-aa6f2323930e" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "chromium", + "status": "expected", + "duration": 508, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "updateSources": [ + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2" + ], + "created": [ + "blob:http://127.0.0.1:8084/31d90497-5ed6-4b98-b705-16783a4487ad", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/7690ee86-2338-47c7-bb54-1559582053db", + "blob:http://127.0.0.1:8084/97a2ecff-8cad-4fe8-8c52-3b35245197d0", + "blob:http://127.0.0.1:8084/3c93c487-3d1e-464c-bd5d-113b995e8297", + "blob:http://127.0.0.1:8084/43e9d1b0-666d-4a5e-834c-3ec05b6b86c6", + "blob:http://127.0.0.1:8084/a27e42e3-7bb3-4cd0-97d8-50c3e36881ed", + "blob:http://127.0.0.1:8084/c334b79d-f3f6-4d63-bf23-bf7959594a58", + "blob:http://127.0.0.1:8084/a0faa12c-4229-4875-9f04-ef7f3a2f65a3", + "blob:http://127.0.0.1:8084/797f0f62-9a73-4733-b032-af61fcabd74e", + "blob:http://127.0.0.1:8084/c6708742-ffbd-40d1-a2ce-35dd94c054e2", + "blob:http://127.0.0.1:8084/b29ead6a-1aad-4bb7-8653-98767888bce0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/31d90497-5ed6-4b98-b705-16783a4487ad", + "blob:http://127.0.0.1:8084/7690ee86-2338-47c7-bb54-1559582053db", + "blob:http://127.0.0.1:8084/97a2ecff-8cad-4fe8-8c52-3b35245197d0", + "blob:http://127.0.0.1:8084/3c93c487-3d1e-464c-bd5d-113b995e8297", + "blob:http://127.0.0.1:8084/43e9d1b0-666d-4a5e-834c-3ec05b6b86c6", + "blob:http://127.0.0.1:8084/a27e42e3-7bb3-4cd0-97d8-50c3e36881ed", + "blob:http://127.0.0.1:8084/c334b79d-f3f6-4d63-bf23-bf7959594a58", + "blob:http://127.0.0.1:8084/a0faa12c-4229-4875-9f04-ef7f3a2f65a3", + "blob:http://127.0.0.1:8084/797f0f62-9a73-4733-b032-af61fcabd74e", + "blob:http://127.0.0.1:8084/c6708742-ffbd-40d1-a2ce-35dd94c054e2", + "blob:http://127.0.0.1:8084/429ca8dd-b6e7-4271-8e99-fde4d9b621e2", + "blob:http://127.0.0.1:8084/b29ead6a-1aad-4bb7-8653-98767888bce0" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "153.0.8010.12", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.8010.12 Safari/537.36" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "firefox", + "status": "expected", + "duration": 1577, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-extraction": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "initialEvents": [ + { + "event": "file", + "at": 173, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 173, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 303, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 321, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 365, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 432, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 525, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 543, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 583, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 648, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 743, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 768, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 817, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 817, + "progress": null, + "processing": false + } + ], + "events": [ + { + "event": "file", + "at": 173, + "progress": null, + "processing": false + }, + { + "event": "video", + "at": 173, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 303, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 321, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 365, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 432, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 525, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 543, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 583, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 648, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 743, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 768, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 817, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 817, + "progress": null, + "processing": false + }, + { + "event": "canvas", + "at": 820, + "progress": null, + "processing": false + }, + { + "event": "update", + "at": 841, + "progress": 0.1, + "processing": true + }, + { + "event": "update", + "at": 884, + "progress": 0.2, + "processing": true + }, + { + "event": "update", + "at": 951, + "progress": 0.3, + "processing": true + }, + { + "event": "update", + "at": 1039, + "progress": 0.4, + "processing": true + }, + { + "event": "update", + "at": 1058, + "progress": 0.5, + "processing": true + }, + { + "event": "update", + "at": 1099, + "progress": 0.6, + "processing": true + }, + { + "event": "update", + "at": 1166, + "progress": 0.7, + "processing": true + }, + { + "event": "update", + "at": 1258, + "progress": 0.8, + "processing": true + }, + { + "event": "update", + "at": 1280, + "progress": 0.9, + "processing": true + }, + { + "event": "update", + "at": 1327, + "progress": 1, + "processing": true + }, + { + "event": "done", + "at": 1327, + "progress": null, + "processing": false + }, + { + "event": "destroy", + "at": 1329, + "progress": null, + "processing": false + } + ], + "first": "blob:http://127.0.0.1:8084/b6eae97c-12cc-4fa6-9c43-000cd3812a3d", + "second": "blob:http://127.0.0.1:8084/4c7bd6c8-a546-4bc2-a41c-553e10d73bc7", + "created": [ + "blob:http://127.0.0.1:8084/ddc8b630-257d-40c6-86fa-d72cd63db93e", + "blob:http://127.0.0.1:8084/888b9046-7a91-4411-ab25-83170383cfc7", + "blob:http://127.0.0.1:8084/1d4e3076-8c9c-4a9e-9335-c7a83970f0c6", + "blob:http://127.0.0.1:8084/7e1ac383-58b0-4a45-b9b5-08cdace0d9c9", + "blob:http://127.0.0.1:8084/55cd7f36-d4a2-4347-8fc5-63c080d83f52", + "blob:http://127.0.0.1:8084/33be8b62-f2f0-47ae-ac7f-dde75c552dd3", + "blob:http://127.0.0.1:8084/6e542b7e-9e19-4c03-955a-9eb88e20df2d", + "blob:http://127.0.0.1:8084/a76acf5f-c127-4eeb-916a-300564d74b22", + "blob:http://127.0.0.1:8084/b47b746d-62cd-4512-81de-92188bb8cc92", + "blob:http://127.0.0.1:8084/c75c92d1-e384-4dfa-b327-b567e54d2c5d", + "blob:http://127.0.0.1:8084/b6eae97c-12cc-4fa6-9c43-000cd3812a3d", + "blob:http://127.0.0.1:8084/6a3ad79f-480f-43eb-aa9f-c49e30bb9f8f", + "blob:http://127.0.0.1:8084/63e467bf-75af-4f81-af83-ca0aa5157b10", + "blob:http://127.0.0.1:8084/9df74887-ff4c-4b7a-bf85-e02c04d99e90", + "blob:http://127.0.0.1:8084/66e326b3-5fd1-4a2d-b23c-468b4e32316f", + "blob:http://127.0.0.1:8084/16c2be5c-f170-460b-b8cc-b56016277223", + "blob:http://127.0.0.1:8084/2915a477-3a93-4dda-b3e5-1fa6f4712bb8", + "blob:http://127.0.0.1:8084/f35b08fd-1383-4301-a42d-c09f7183cd25", + "blob:http://127.0.0.1:8084/a2093551-862e-4a57-846d-0235cb8a354c", + "blob:http://127.0.0.1:8084/c937f2cd-be9f-432e-9c4c-0a4c7fd31465", + "blob:http://127.0.0.1:8084/4c7bd6c8-a546-4bc2-a41c-553e10d73bc7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/888b9046-7a91-4411-ab25-83170383cfc7", + "blob:http://127.0.0.1:8084/1d4e3076-8c9c-4a9e-9335-c7a83970f0c6", + "blob:http://127.0.0.1:8084/7e1ac383-58b0-4a45-b9b5-08cdace0d9c9", + "blob:http://127.0.0.1:8084/55cd7f36-d4a2-4347-8fc5-63c080d83f52", + "blob:http://127.0.0.1:8084/33be8b62-f2f0-47ae-ac7f-dde75c552dd3", + "blob:http://127.0.0.1:8084/6e542b7e-9e19-4c03-955a-9eb88e20df2d", + "blob:http://127.0.0.1:8084/a76acf5f-c127-4eeb-916a-300564d74b22", + "blob:http://127.0.0.1:8084/b47b746d-62cd-4512-81de-92188bb8cc92", + "blob:http://127.0.0.1:8084/c75c92d1-e384-4dfa-b327-b567e54d2c5d", + "blob:http://127.0.0.1:8084/b6eae97c-12cc-4fa6-9c43-000cd3812a3d", + "blob:http://127.0.0.1:8084/6a3ad79f-480f-43eb-aa9f-c49e30bb9f8f", + "blob:http://127.0.0.1:8084/63e467bf-75af-4f81-af83-ca0aa5157b10", + "blob:http://127.0.0.1:8084/9df74887-ff4c-4b7a-bf85-e02c04d99e90", + "blob:http://127.0.0.1:8084/66e326b3-5fd1-4a2d-b23c-468b4e32316f", + "blob:http://127.0.0.1:8084/16c2be5c-f170-460b-b8cc-b56016277223", + "blob:http://127.0.0.1:8084/2915a477-3a93-4dda-b3e5-1fa6f4712bb8", + "blob:http://127.0.0.1:8084/f35b08fd-1383-4301-a42d-c09f7183cd25", + "blob:http://127.0.0.1:8084/a2093551-862e-4a57-846d-0235cb8a354c", + "blob:http://127.0.0.1:8084/c937f2cd-be9f-432e-9c4c-0a4c7fd31465", + "blob:http://127.0.0.1:8084/ddc8b630-257d-40c6-86fa-d72cd63db93e", + "blob:http://127.0.0.1:8084/4c7bd6c8-a546-4bc2-a41c-553e10d73bc7" + ], + "connected": false, + "retiredReadyState": 0, + "retiredSrc": null + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "firefox", + "status": "expected", + "duration": 450, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "firefox", + "status": "expected", + "duration": 588, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-media-failure": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/64a3e71c-14dc-4e8d-bfdc-077ae5413528" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/64a3e71c-14dc-4e8d-bfdc-077ae5413528", + "blob:http://127.0.0.1:8084/15b487db-877b-44e0-a66a-e87e957faf31" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/64a3e71c-14dc-4e8d-bfdc-077ae5413528", + "blob:http://127.0.0.1:8084/15b487db-877b-44e0-a66a-e87e957faf31" + ] + } + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "firefox", + "status": "expected", + "duration": 554, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-late-native-blob": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/b2fcea95-5c9e-4c05-8cef-819e5f4e6aa0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/b2fcea95-5c9e-4c05-8cef-819e5f4e6aa0" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "firefox", + "status": "expected", + "duration": 1261, + "retry": 0, + "annotations": [], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-native-source-cancellation": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "updateSources": [ + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8" + ], + "created": [ + "blob:http://127.0.0.1:8084/2b3fbf3e-4ca2-44b8-b36a-5b538cd2cc30", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/a4df713e-2c82-44e8-8549-87081a2ae759", + "blob:http://127.0.0.1:8084/53f13e27-ef99-449b-ab74-97662eb83f07", + "blob:http://127.0.0.1:8084/970f2a92-f27d-4d28-be53-e7a2a58eb9a0", + "blob:http://127.0.0.1:8084/01c396e2-303e-4ccd-a897-58a9c424cb6b", + "blob:http://127.0.0.1:8084/10228736-a3e0-443b-8698-866db0f07dea", + "blob:http://127.0.0.1:8084/bb872617-b20b-4fc6-860e-a0e67c19347d", + "blob:http://127.0.0.1:8084/355f064a-2382-4f39-a6a7-b6b48976927b", + "blob:http://127.0.0.1:8084/bc95a38b-600e-4ee1-afb0-654a53c5f515", + "blob:http://127.0.0.1:8084/a2fb019f-7f00-47d1-a24d-dfde5eede328", + "blob:http://127.0.0.1:8084/2c5e7431-b789-4672-aa48-61af30d2d5f3" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2b3fbf3e-4ca2-44b8-b36a-5b538cd2cc30", + "blob:http://127.0.0.1:8084/a4df713e-2c82-44e8-8549-87081a2ae759", + "blob:http://127.0.0.1:8084/53f13e27-ef99-449b-ab74-97662eb83f07", + "blob:http://127.0.0.1:8084/970f2a92-f27d-4d28-be53-e7a2a58eb9a0", + "blob:http://127.0.0.1:8084/01c396e2-303e-4ccd-a897-58a9c424cb6b", + "blob:http://127.0.0.1:8084/10228736-a3e0-443b-8698-866db0f07dea", + "blob:http://127.0.0.1:8084/bb872617-b20b-4fc6-860e-a0e67c19347d", + "blob:http://127.0.0.1:8084/355f064a-2382-4f39-a6a7-b6b48976927b", + "blob:http://127.0.0.1:8084/bc95a38b-600e-4ee1-afb0-654a53c5f515", + "blob:http://127.0.0.1:8084/a2fb019f-7f00-47d1-a24d-dfde5eede328", + "blob:http://127.0.0.1:8084/089f64ec-9446-49f0-b2a4-c9e63774aac8", + "blob:http://127.0.0.1:8084/2c5e7431-b789-4672-aa48-61af30d2d5f3" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + }, + "browser-evidence": { + "browser": "155.0", + "platform": "win32", + "errors": [], + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native file selection, seek extraction, PNG pixels and repeat generation", + "browser": "webkit", + "status": "expected", + "duration": 1163, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "DOM drop dispatch uses the registered handler while callable ondrop works", + "browser": "webkit", + "status": "expected", + "duration": 1221, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "browser": "webkit", + "status": "expected", + "duration": 1177, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "native PNG callback delivered after destroy is ignored after cancellation", + "browser": "webkit", + "status": "expected", + "duration": 1182, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + }, + { + "file": "thumbnail-tool.spec.js", + "title": "source replacement cancels held native encoding and completes only the latest job", + "browser": "webkit", + "status": "expected", + "duration": 1246, + "retry": 0, + "annotations": [ + { + "type": "capability-control", + "description": "Native HTTP succeeds but native Blob and tool Blob return media error 4. Extraction scenario unavailable; no successful extraction claimed." + } + ], + "errors": [], + "attachments": { + "thumbnail-runtime": { + "name": "candidate-artifact-workspace-policy", + "sha256": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + }, + "thumbnail-blob-unavailable": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + }, + "browser-evidence": { + "browser": "26.6", + "platform": "win32", + "errors": [], + "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" + } + } + } + ] + } + }, + "fingerprints": { + "packages/artplayer-tool-thumbnail/ARCHITECTURE.md": "44534a91510f99ff1d64a013953c77cfb6c00ece9f4dfad86a12e66b1999a3ec", + "packages/artplayer-tool-thumbnail/README.md": "959562ac29bd0162e22c30041da9c3c9bfc9039e20cd6f210fb26b597d7f1c74", + "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES": "7bea0832808c3d37b5a79f2ed6c65ef2ebdf7dbcbba124c469ebb90e2ec7c013", + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js": "255791d9c0cb6af1d00262a9723b3f191ff2c75fc59ce669c6fe72245f7d7b48", + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js": "3a3dd905d48c89de05fe232b1fe7aca522510238959c6170a7003b6df2f3df2c", + "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs": "39a3c48eca45c1c97e976d864f11d8b758d64cbae98654f88de3e26f1f36b60b", + "packages/artplayer-tool-thumbnail/package.json": "dafbe13cddee908b2932ab2f529b0eef86ad9d9de8f7ab7585ed073c221cdbe1", + "packages/artplayer-tool-thumbnail/types/README.md": "dd0bf1f872feac29b4c10424bec22c66174848d30724f067e463763de2ccea62", + "packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.cts": "f0573326178b57a6e22a08b53247eb15ef4d20a1652e40d0cae638ac7aaf8092", + "packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.mts": "f7a2fea001883706400b250588363533605d3f3a075cef1e31ae973c22258cb3", + "packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts": "3da1a6a68db16c173dfb688ccc49ed130de2f7fb4790f1888230f2fd4c5847ac", + "packages/artplayer-tool-thumbnail/src/emitter.ts": "a083e029dd1104912d3e9a0ba23a6dcc160d9ccde6a55be40db74367a647a3cb", + "packages/artplayer-tool-thumbnail/src/extraction.ts": "8705d51519f71edbd58b21cd30bcd2665a48f121f0ae9d96da68d3f8b0c41a5e", + "packages/artplayer-tool-thumbnail/src/index.ts": "a7784a2e5036b3b29c6a2481532869e4b6569ad6b7e0a8081a0a85d29e65b8f3", + "packages/artplayer-tool-thumbnail/src/input.ts": "29024c3e799dd51735fc09196c12480a7b271f9e4c76f661f43f3643e1c1e020", + "packages/artplayer-tool-thumbnail/src/lifecycle.ts": "54d6a2a78906c79ebdba4a12441138cb95f55b85c09c4a0213f12f5a4afa89b7", + "packages/artplayer-tool-thumbnail/src/policy.ts": "2207c25e17060d28aa18a83130a3c7086fadcfe6236c5c5eba759e1cdec9c0ff", + "packages/artplayer-tool-thumbnail/src/sheet.ts": "af423e53fbd6aec2c54ae74410cf84f99eb8c125605b064aaf6fa1f0bb8f0d45", + "packages/artplayer-tool-thumbnail/src/source.ts": "e74ab976a47bb81deeba91eb84ab5794791f5c9ec1b1a59a8700b28e1b0985c1", + "packages/artplayer-tool-thumbnail/src/types.ts": "029d8c6f61cc97d7a9216bcf76d5009fcf3b9773ee03cda278caa8fcd26227b9", + "packages/artplayer-tool-thumbnail/src/utils.ts": "90d2e1195f7de6c4a5930172baf23b95c2de1cc729375b0d29e30fe43e4dfd13", + "test/thumbnail-policy.test.js": "8769f361d0374919540c7a8a44b517538840108086762027c0187a30b3ba3ffe", + "test/browser/thumbnail-tool.spec.js": "6e2d71c23c09f38fde65fe7baf5d653a14bf7bed2a75b64f36763371dfb3eb6a", + "test/types/thumbnail.ts": "b8be26c3662b3dd834433f437984de61ee5bab1b9a1049f82ea0c5851257b590", + "docs/assets/ts/artplayer-tool-thumbnail.d.ts": "f2845ec0a890d001829dbca089ccc450f8e77817e497c5738f964c2920772f3f" + }, + "initialFailures": [ + { + "log": "refactor/.cache/thumbnail-policy-before.log", + "tests": 7, + "pass": 1, + "fail": 6, + "description": "New default policy assertions reproduce the missing implementation." + }, + { + "log": "refactor/.cache/thumbnail-policy-regression-initial.log", + "description": "Existing no-delay lifecycle fixtures did not advance restored timers; corrected to explicitly exercise workspace mode. Hung runtime fixture worker was stopped, not counted as a completed passing run." + }, + { + "log": "refactor/.cache/thumbnail-policy-final-unit.log", + "description": "Before final rerun strict checking rejected optional delay at normalization. Numeric normalization now follows runtime numeric validation; final log contains the passing rerun." + }, + { + "report": "refactor/.cache/thumbnail-policy-browser-initial/report.json", + "expected": 89, + "unexpected": 1, + "description": "Chromium same-file replacement did not fire change when published policy retained input value, leaving the original job active. Test now clears selection then chooses the same file; production input retention is preserved." + } + ], + "limitations": [ + "Windows WebKit native HTTP loads but Blob returns media error 4; annotated checks are capability controls, not extraction passes or Safari evidence.", + "Historical defect-control passes do not imply historical cleanup correctness.", + "No complete original npm 3.5.31 archive or historical declaration recovered.", + "PKG-TOOL-THUMB-05/06 integration and packaging disposition remain separate gates.", + "No physical device, remote CI, publication or formal review completed by this change." + ] +} diff --git a/refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md b/refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md new file mode 100644 index 000000000..94b22aa6b --- /dev/null +++ b/refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md @@ -0,0 +1,91 @@ +# PKG-TOOL-THUMB-04 approved Thumbnail policy and migration completion + +The user approved published 3.5.31 behavior as the default, with the unpublished +workspace behavior explicitly selected through `compatibility: 'workspace-4.4'`. +This completes the source/type migration task, including its earlier TypeScript, +declaration, installed-entry and emitter checkpoints. It does not complete 05/06, +physical-device evidence, formal reviews or publication readiness. + +## Implementation + +`src/policy.ts` selects and validates the policy. The public constructor restores +`DEFAULTS.delay: 300` and published key order. Published mode validates/clamps +height and delay to 10-1000, retains configured height and the selected input +value, delays the video event and each frame, and waits twice the delay before +done. Workspace mode keeps aspect-ratio height, synchronous video notification, +no fixed extraction delays and input reset. The class DEFAULTS always represents +the published policy; this boundary was explicitly approved. + +Source and extraction continue owning their resources separately. Source changes, +native errors and destroy cancel pending notification, frame and completion +timers. Frame readiness and seek completion are required in addition to the +published delay. Cancellation settles the Promise without late update/done or +new URLs. NaN retains the old asynchronous timer boundary rather than being +mistaken for the workspace no-timer mode. Existing public spellings, descriptors, +custom fields, event payloads, synchronous validation and return values remain. + +The authored declarations add optional compatibility/delay and exported +Compatibility/DefaultOptions types. SheetOptions still accepts old literals +without a delay field. Source and public constructors remain mutually assignable. +The generated local Monaco declarations are regenerated; its real worker compiles +the new mode/default types and executes constructor/event/cleanup code. + +README, architecture and type ownership guides describe both policies. No new +dependency, lockfile or version change is needed. `test:thumbnail` and `test:unit` +now include the new policy regression suite. Bundles and docs copies were built +through the repository build script; source remains authoritative. + +## Verification + +Node 24.21.0, Yarn Classic 1.22.22, Windows. Evidence: +[thumbnail-policy-validation.json](../baselines/thumbnail-policy-validation.json). + +- The initial new policy tests failed 6/7 against the previous implementation. + Final `yarn test:thumbnail`: 136 passed, no failure/skip/cancellation, 14.968 s. + This includes frozen historical tests and current/old TypeScript consumers. +- Strict source tsc and scoped source/type/test/script lint pass. Five public + compiler/resolution modes reject all 12 invalid calls. Native CommonJS/no-interop + fixtures and both-direction source/declaration assignment pass. +- Main/legacy/ESM build succeeds. Final external Yarn pack/install evidence is + `.cache/thumbnail-package-types-IH4Ouy`: offline frozen reinstall, installed-file + hashes, native require/import and 12 compiler cases pass. Five cases characterize + the frozen workspace missing-entry defect; seven exercise candidate consumers. + Installed legacy runtime/policy/lifecycle subset: 63/63, 2.770 s; the final + legacy bytes are identical to that tested snapshot. The final packed files are + checked against the working tree in the evidence recorder. +- Three-engine source browser run: 90 passed, 0 failed/skipped/flaky, 66.423 s. + Installed main browser run: 30 passed, 0 failed/skipped/flaky, 33.414 s. + Chromium 153.0.8010.12 and Firefox 155.0 verify actual file selection, configured + versus aspect height, PNG pixels, repeated extraction, URL release, held native + encoding cancellation and latest-source-only updates. Published video/frame/done + delays have lower-bound assertions; owned-timer tests verify exact scheduling. +- Windows WebKit 26.6 cannot decode the tested native Blob URLs (error 4), while + HTTP controls load. There are 23 annotated capability-control source cases and + 10 installed cases. These are not successful extraction or physical Safari + evidence. Other passing historical tests also include exact old-defect controls. + +The first broad runtime attempt exposed fixtures that assumed no fixed delays; +those now explicitly select workspace mode. A waiting worker was stopped and its +unfinished run is not counted as passing. An initial strict error on optional +delay was fixed through numeric normalization after runtime validation. + +The first browser run had 89 passes and one Chromium failure: selecting an +identical file while published mode retains input.value did not trigger change, +so starting a second job correctly rejected it. The replacement test now clears +selection before choosing the file again. Both modes retain the original strict +AbortError/latest-source assertions. Final reports and the initial trace are +archived separately; no retry, weaker assertion or production input reset hides +that failure. + +## Status and continuation + +PKG-TOOL-THUMB-04 is done; THUMB-COMPAT-01 is resolved for the approved default +policy and migration boundary. No complete original npm archive or historical +declaration has been recovered, and 05/06 retain integration/distribution work. +Next implementation work includes the recorded installed Danmuku CPU-gap failure +and remaining package/release preparation. Finish implementation and required +tests, then await user direction for reviews; do not push or publish. + +Rollback requires reverting this task's source, declarations, generated outputs, +tests and documentation together. Returning to the prior workspace default would +also reverse the approved compatibility decision and must not be done silently. diff --git a/refactor/plan.md b/refactor/plan.md index ab6d3fa2a..8047c9cbe 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 282 项,范围 22 个包及工作区/示例。 -状态:todo 43 / doing 21 / blocked 0 / done 218 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 43 / doing 20 / blocked 0 / done 219 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -396,7 +396,7 @@ | 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, PKG-TOOL-THUMB-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 | done | | 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 | @@ -673,7 +673,7 @@ - PKG-TOOL-THUMB-01: [记录](baselines/thumbnail-release.json) [记录](baselines/thumbnail-contract.md) [记录](changes/2026-09-13-PKG-TOOL-THUMB-01-contract.md) [记录](baselines/thumbnail-contract-validation.json) - PKG-TOOL-THUMB-02: [记录](baselines/thumbnail-behavior-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-02-behavior.md) - PKG-TOOL-THUMB-03: [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-input-checkpoint.md) [记录](baselines/thumbnail-input-checkpoint.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md) [记录](baselines/thumbnail-lifecycle-validation.json) -- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md) [记录](baselines/thumbnail-emitter-validation.json) [记录](thumbnail-compatibility-decision.md) [记录](changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md) +- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md) [记录](baselines/thumbnail-emitter-validation.json) [记录](thumbnail-compatibility-decision.md) [记录](changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md) [记录](changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md) [记录](baselines/thumbnail-policy-validation.json) - SITE-01: [记录](site-inventory.md) [记录](baselines/site-inventory.json) [记录](baselines/site-provenance.json) [记录](baselines/demo-additions.json) [记录](changes/2026-09-14-SITE-01-site-inventory.md) [记录](baselines/site-inventory-validation.json) - SITE-SMOKE-01: [记录](changes/2026-09-14-SITE-SMOKE-01-documentation-smoke.md) [记录](baselines/docs-smoke-validation.json) [记录](scripts/docs-smoke.test.mjs) - SITE-02: [记录](changes/2026-09-14-SITE-02-editor-declarations.md) [记录](baselines/editor-declarations-validation.json) [记录](changes/2026-09-14-SITE-SMOKE-01-documentation-smoke.md) diff --git a/refactor/progress.md b/refactor/progress.md index ba4272aa4..ddceaf1fc 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,18 @@ # 进度与证据 +## PKG-TOOL-THUMB-04 Thumbnail 策略与源码迁移完成 + +用户批准已落实:默认保留 npm 3.5.31,显式 workspace-4.4 保留未发布工作区行为。 +策略集中到 policy.ts,延迟由 source/job 各自管理;类型、迁移说明与构建产物同步。 +136 项 Node 测试、严格类型、隔离安装 12 编译器案例通过;安装 legacy 63 项通过。 +三引擎源码浏览器 90 项、安装 main 30 项通过,其中分别 23/10 项为 WebKit +能力对照,不能算截图提取或真实 Safari 通过。首次同文件重选夹具失败已修正并归档。 +见[完成记录](changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md)、 +[证据](baselines/thumbnail-policy-validation.json)。04 done,THUMB-COMPAT-01 resolved; +05/06、设备与整体发布门槛保留。当前 219 done、20 doing、43 todo,共282项。 +下一步修复已捕获的 DANMUKU-INSTALLED-GAP-01,并继续剩余实施/版本准备。 +实施完成后等待用户指导复盘;未启动复盘、推送或发布。 + ## CI-01 完整 Windows WebKit 安装包回归 固定bb2c12df3,复用5e0447fc5打包的20包run-cRduve;77文件/845项:694通过、 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index a9e06a030..8ecb2412d 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -177,7 +177,7 @@ | IFRAME-LIFE-01 | open / 已复现 | Iframe timestamp IDs and unowned pending requests/timers lack terminal cleanup | PKG-IFRAME-02, PKG-IFRAME-03, PKG-IFRAME-05 | | IFRAME-TRUST-01 | open / 已复现 | Iframe executable commit protocol accepts messages without source/origin validation | PKG-IFRAME-03, PKG-IFRAME-05 | | IFRAME-DIST-01 | open / 已复现 | Iframe tool name is unavailable at registry observation and old archive includes distinct helper exports | PKG-IFRAME-04, PKG-IFRAME-06 | -| THUMB-COMPAT-01 | open / 已复现 | Thumbnail recovered 3.5.31 and workspace 4.4.0 have conflicting height and event timing defaults | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05 | +| THUMB-COMPAT-01 | resolved / 已复现 | Thumbnail recovered 3.5.31 and workspace 4.4.0 have conflicting height and event timing defaults | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05 | | THUMB-LIFE-01 | open / 已复现 | Thumbnail drop binding and repeated destroy fail; pending work and replacement resource ownership need repair | PKG-TOOL-THUMB-02, PKG-TOOL-THUMB-03, PKG-TOOL-THUMB-05 | | THUMB-PROVENANCE-01 | resolved / 源码/产物事实 | Thumbnail local emitter attribution and historical distribution completeness remain unverified | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 | | THUMB-MEDIA-01 | open / 已复现 | Windows WebKit native Blob URLs cannot load tested MP4 media despite successful HTTP controls | PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06 | diff --git a/refactor/risks.json b/refactor/risks.json index ce8912f92..7605a95e3 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4125,9 +4125,9 @@ "PKG-TOOL-THUMB-04", "PKG-TOOL-THUMB-05" ], - "compatibleResolution": "Freeze both implementations and derive a concrete compatibility policy before changing defaults; no silent claim that identical calls have identical historical semantics.", + "compatibleResolution": "User approved published 3.5.31 default behavior and explicit workspace-4.4 mode, including class DEFAULTS. Implemented as an owned policy without restoring historical resource defects.", "closureCriteria": "Explicit documented compatibility decision, old/candidate tests and migration examples covering delay, height, input reset and readiness.", - "status": "open", + "status": "resolved", "evidence": [ "refactor/baselines/thumbnail-contract.md", "refactor/baselines/thumbnail-release.json", @@ -4139,9 +4139,17 @@ "refactor/changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md", "refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md", "refactor/thumbnail-compatibility-decision.md", - "refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md" + "refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md", + "refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md", + "refactor/baselines/thumbnail-policy-validation.json" ], - "workspaceState": "Concrete proposal prepared: recovered 3.5.31 runtime defaults and optional workspace-4.4 mode, explicitly including the class-level DEFAULTS difference, delay/height validation, event/frame timing and input reset. Frozen contract/behavior tests rerun 62/62. User choice remains pending; no mode/default change implemented or inferred from other package approvals." + "workspaceState": "Default fixed/clamped height and delay, delayed video/frame/done and retained input value implemented; explicit workspace mode keeps aspect height, no policy delays and input reset. Source and installed browser checks plus strict current/old types pass within documented Windows WebKit Blob capability limits. Package integration/distribution and physical devices remain separate gates.", + "resolutionEvidence": [ + "refactor/changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md", + "refactor/baselines/thumbnail-policy-validation.json", + "refactor/thumbnail-compatibility-decision.md" + ], + "resolutionRationale": "Explicit user approval and migration examples, frozen/default/workspace tests, source and installed real PNG checks on Chromium/Firefox, owned cancellation timing and declaration consumers close the conflicting-default policy. Windows WebKit native Blob limitation is recorded, not treated as successful extraction." }, { "id": "THUMB-LIFE-01", diff --git a/refactor/scripts/thumbnail-package-types.mjs b/refactor/scripts/thumbnail-package-types.mjs index 8a611da8d..0e6e17fa1 100644 --- a/refactor/scripts/thumbnail-package-types.mjs +++ b/refactor/scripts/thumbnail-package-types.mjs @@ -6,8 +6,8 @@ import ts from 'typescript' import compat from 'typescript-compat' import { checkFiles, packedFiles } from '../../scripts/package-check.mjs' import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs' -import { verifyThumbnailContract } from './thumbnail-contract.mjs' import { hash, readMember } from './releases.mjs' +import { verifyThumbnailContract } from './thumbnail-contract.mjs' async function main() { assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:thumbnail-types-package') @@ -20,7 +20,8 @@ async function main() { fs.mkdirSync(frozen) const prefix = 'packages/artplayer-tool-thumbnail/' for (const [file, text] of contract.source) { - if (!file.startsWith(prefix)) continue + if (!file.startsWith(prefix)) + continue const target = path.join(frozen, file.slice(prefix.length)) fs.mkdirSync(path.dirname(target), { recursive: true }) fs.writeFileSync(target, text) @@ -74,7 +75,8 @@ ${label === 'candidate' ? `const legacy = require('${pkg.name}/legacy'); assert. fs.writeFileSync(path.join(output, `${label}-runtime.log`), observation) runtime.push({ package: label, ...JSON.parse(observation.trim()) }) const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] - if (label === 'candidate') modes.push([ts, 'nodenext-cjs-no-interop'], [compat, 'node10-commonjs-no-interop']) + if (label === 'candidate') + modes.push([ts, 'nodenext-cjs-no-interop'], [compat, 'node10-commonjs-no-interop']) for (const [compiler, mode] of modes) { const next = mode.startsWith('nodenext') const noInterop = mode.endsWith('no-interop') @@ -92,15 +94,19 @@ ${label === 'candidate' ? `const legacy = require('${pkg.name}/legacy'); assert. } const diagnostics = compile(source) writeJson(path.join(output, `${label}-${compiler.version}-${mode}.json`), diagnostics) - if (label === 'candidate') assert.deepEqual(diagnostics, [], `${compiler.version} ${mode}`) + if (label === 'candidate') { + assert.deepEqual(diagnostics, [], `${compiler.version} ${mode}`) + } else { assert.deepEqual(diagnostics.map(item => item.code), [mode === 'node10-commonjs' ? 2307 : 7016], 'Only the exact frozen missing entry/declaration diagnostic is expected') } const negative = label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : [] - if (label === 'candidate') assert.equal(negative.length, noInterop ? 1 : 10) + if (label === 'candidate') + assert.equal(negative.length, noInterop ? 1 : 12) matrix.push({ package: label, compiler: compiler.version, mode, diagnostics, negative }) } - if (label === 'candidate') fs.cpSync(installed, path.join(output, 'installed-artifacts'), { recursive: true }) + if (label === 'candidate') + fs.cpSync(installed, path.join(output, 'installed-artifacts'), { recursive: true }) } finally { removeConsumer(consumer) } } diff --git a/refactor/scripts/thumbnail-types.test.mjs b/refactor/scripts/thumbnail-types.test.mjs index bfe35d894..00593b7ef 100644 --- a/refactor/scripts/thumbnail-types.test.mjs +++ b/refactor/scripts/thumbnail-types.test.mjs @@ -2,11 +2,11 @@ import assert from 'node:assert/strict' import fs from 'node:fs' // eslint-disable-next-line test/no-import-node-test -- Public type and editor contract runner. import test from 'node:test' +import { ESLint } from 'eslint' import ts from 'typescript' import compat from 'typescript-compat' -import { ESLint } from 'eslint' -import { checkConsumer } from '../../scripts/typecheck.mjs' import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs' +import { checkConsumer } from '../../scripts/typecheck.mjs' const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] const invalid = source => source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '') @@ -15,7 +15,7 @@ test('Thumbnail public declarations check actual callbacks, custom events and in const source = fs.readFileSync('test/types/thumbnail.ts', 'utf8') for (const [compiler, mode] of modes) { assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`) - assert.equal(checkConsumer(compiler, mode, invalid(source)).length, 10, `${compiler.version} ${mode} negatives`) + assert.equal(checkConsumer(compiler, mode, invalid(source)).length, 12, `${compiler.version} ${mode} negatives`) } const commonjs = fs.readFileSync('test/types/thumbnail-commonjs.cts', 'utf8') for (const [compiler, mode] of [[ts, 'nodenext-cjs'], [compat, 'node10-commonjs']]) { @@ -67,7 +67,7 @@ const bad = player.mediabunny?.levels;` assert.equal(checkPluginEditorDeclaration(code, core, invalid(consumer), compiler).length, 1) } assert.throws(() => generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny'), /Unsupported editor type re-export/) - assert.throws(() => generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny', { './media': media + '\nexport interface Extra {}' }), /explicitly cover/) + assert.throws(() => generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny', { './media': `${media}\nexport interface Extra {}` }), /explicitly cover/) const common = fs.readFileSync('docs/assets/js/common.js', 'utf8') assert(common.includes('./assets/ts/artplayer-tool-thumbnail.d.ts')) assert(!common.includes('./assets/ts/media.d.ts')) diff --git a/refactor/tasks.json b/refactor/tasks.json index 407a7c58d..e8636f08e 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -4238,7 +4238,7 @@ "CORE-07", "PKG-TOOL-THUMB-07" ], - "status": "doing", + "status": "done", "risk": "H", "deliverable": "补齐真实 API 声明,自有 emitter/utils TS 化", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;为 BASE-DIST-01 提供兼容入口/声明及消费者验证", @@ -4250,7 +4250,9 @@ "changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md", "baselines/thumbnail-emitter-validation.json", "thumbnail-compatibility-decision.md", - "changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md" + "changes/2026-09-15-PKG-TOOL-THUMB-04-policy-proposal.md", + "changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md", + "baselines/thumbnail-policy-validation.json" ] }, { diff --git a/refactor/thumbnail-compatibility-decision.md b/refactor/thumbnail-compatibility-decision.md index 618092ed7..9dcd734ac 100644 --- a/refactor/thumbnail-compatibility-decision.md +++ b/refactor/thumbnail-compatibility-decision.md @@ -1,7 +1,10 @@ -# Thumbnail 默认行为兼容决策(待用户确认) +# Thumbnail 默认行为兼容决策(已批准,已实施) -状态:proposal,尚未批准或实施。责任任务 PKG-TOOL-THUMB-04,风险 THUMB-COMPAT-01。 -本文件把已有待答问题具体化;不把其他包的兼容授权推广到本工具。 +状态:approved,源码、类型及本机验证完成。责任任务 PKG-TOOL-THUMB-04,风险 THUMB-COMPAT-01。 +2026-09-15 用户明确批准:“接受:npm 行为默认,工作区行为显式选择(推荐)”。 +下列方案现在可直接实施,无需再次询问;批准不代替源码、类型和浏览器验证。 +实施结果见 [完成记录](changes/2026-09-15-PKG-TOOL-THUMB-04-approved-policy.md); +05/06、设备和发布门槛仍独立保留,Windows WebKit 能力对照不计截图提取通过。 ## 已复现的冲突 @@ -23,7 +26,7 @@ 变成 90 高;loadVideo 后同步注册 video 监听器在两版也会得到不同结果。 不能通过 TS 重命名或内部拆文件同时满足这两种默认值和时序。 -## 推荐方案:恢复已发布行为为默认,显式选择工作区模式 +## 已批准方案:恢复已发布行为为默认,显式选择工作区模式 1. 根构造器默认选择恢复的 3.5.31 行为,支持旧 delay 参数,DEFAULTS 恢复 delay: 300;配置 height 保留原钳制,video 延迟、每帧/完成延迟及 input @@ -33,7 +36,7 @@ 显式 compatibility: 'published-3.5' 可表达默认选择。 3. 类级 DEFAULTS 没有实例上下文,统一表示根默认的 3.5.31 配置。依赖工作区 DEFAULTS 精确字段集合的代码需要明确选择/保存自己的配置;不能承诺它同时 - 返回两套对象。这个差异也属于本次待批准范围。 + 返回两套对象。这个差异也属于本次已批准范围。 4. 保留已实现的 URL/监听器/定时器所有权、销毁与切源取消、错误结算、输入 清理和 emitter 修复。不会恢复旧版错绑 this、陈旧回调、资源泄漏等缺陷。 定时等待不取代真实帧就绪;修复慢解码导致错误截图的路径单独记录和验证。 @@ -51,8 +54,8 @@ const workspaceTool = new ArtplayerToolThumbnail({ }) ``` -另一种可选决策是保留工作区 4.4.0 为默认,并接受已发布 3.5.31 用户需要选择 -兼容模式。这会把迁移成本放到已发布旧用户,不作为当前推荐。 +未选择的替代方案是保留工作区 4.4.0 为默认,并要求已发布 3.5.31 用户选择 +兼容模式;用户已经选择上面的 npm 默认方案。 ## 批准后实施与验收 diff --git a/test/browser/thumbnail-editor-types.spec.js b/test/browser/thumbnail-editor-types.spec.js index 784269263..15807f38f 100644 --- a/test/browser/thumbnail-editor-types.spec.js +++ b/test/browser/thumbnail-editor-types.spec.js @@ -22,7 +22,9 @@ test('Monaco consumes Thumbnail global declarations and runs the actual typed co })) const model = api.editor.createModel(` const input = document.createElement('input'); input.type = 'file'; document.body.appendChild(input); -const options: ArtplayerToolThumbnail.Option = { fileInput: input, number: 10 }; +const mode: ArtplayerToolThumbnail.Compatibility = 'workspace-4.4'; +const defaults: ArtplayerToolThumbnail.DefaultOptions = ArtplayerToolThumbnail.DEFAULTS; +const options: ArtplayerToolThumbnail.Option = { fileInput: input, number: 10, compatibility: mode, delay: defaults.delay }; const tool: ArtplayerToolThumbnail = new ArtplayerToolThumbnail(options); let last = 0; tool.on('custom', (value: number) => { last = value; }); @@ -32,7 +34,7 @@ const video: HTMLVideoElement = tool.video; tool.destroy(); tool.destroy(); input.remove(); function mediaTypes(player: artplayerProxyMediabunny.MediaBunnyPlayer): number | undefined { return player.mediabunny?.currentTime; } `, 'typescript', api.Uri.parse('file:///thumbnail-consumer.ts')) - const invalid = api.editor.createModel("new ArtplayerToolThumbnail({ fileInput: '#input' });", 'typescript', api.Uri.parse('file:///thumbnail-invalid.ts')) + const invalid = api.editor.createModel('new ArtplayerToolThumbnail({ fileInput: \'#input\' });', 'typescript', api.Uri.parse('file:///thumbnail-invalid.ts')) try { const worker = await (await types.getTypeScriptWorker())(model.uri, invalid.uri) const syntax = await worker.getSyntacticDiagnostics(model.uri.toString()) diff --git a/test/browser/thumbnail-input.spec.js b/test/browser/thumbnail-input.spec.js index 57dba23b7..c374418b0 100644 --- a/test/browser/thumbnail-input.spec.js +++ b/test/browser/thumbnail-input.spec.js @@ -12,7 +12,7 @@ test('Thumbnail input ownership replaces real DOM listeners and generated inputs await page.addScriptTag({ content: implementation.code }) await page.evaluate(() => { window.inputEvents = [] - window.inputTool = new window.ArtplayerToolThumbnail({ fileInput: document.querySelector('#first') }) + window.inputTool = new window.ArtplayerToolThumbnail({ fileInput: document.querySelector('#first'), compatibility: 'workspace-4.4' }) window.firstInput = window.inputTool.option.fileInput window.inputTool.on('file', file => window.inputEvents.push(file.name)) window.inputTool.setup({ fileInput: document.querySelector('#first') }) diff --git a/test/browser/thumbnail-tool.spec.js b/test/browser/thumbnail-tool.spec.js index ce93f3d62..2e92d9e07 100644 --- a/test/browser/thumbnail-tool.spec.js +++ b/test/browser/thumbnail-tool.spec.js @@ -27,7 +27,12 @@ async function capabilityControl(page, testInfo, lifecycle) { return true } -const implementations = [...thumbnailHistorical(), { ...await thumbnailCandidate(), inputOwnership: true, lifecycle: true }] +const candidate = await thumbnailCandidate() +const implementations = [ + ...thumbnailHistorical(), + { ...candidate, inputOwnership: true, lifecycle: true, publishedPolicy: true }, + { ...candidate, name: `${candidate.name}-workspace-policy`, inputOwnership: true, lifecycle: true, compatibility: 'workspace-4.4' }, +] for (const implementation of implementations) { test.describe(`Thumbnail tool ${implementation.name}`, () => { test.beforeEach(async ({ page }, testInfo) => { @@ -61,7 +66,7 @@ for (const implementation of implementations) { } URL.revokeObjectURL(blobUrl) }) - await page.evaluate(() => { + await page.evaluate((compatibility) => { window.created = [] window.revoked = [] const create = URL.createObjectURL.bind(URL) @@ -75,11 +80,11 @@ for (const implementation of implementations) { revoked.push(url) revoke(url) } - window.tool = new ArtplayerToolThumbnail({ fileInput: document.querySelector('#input'), number: 10, width: 80, height: 30, column: 3, begin: 0, end: 20 }) + window.tool = new ArtplayerToolThumbnail({ fileInput: document.querySelector('#input'), number: 10, width: 80, height: 30, column: 3, begin: 0, end: 20, compatibility }) window.observed = [] for (const event of ['file', 'video', 'canvas', 'update', 'done', 'error', 'destroy']) - tool.on(event, (...args) => observed.push({ event, progress: event === 'update' ? args[1] : null, processing: tool.processing })) - }) + tool.on(event, (...args) => observed.push({ event, at: performance.now(), progress: event === 'update' ? args[1] : null, processing: tool.processing })) + }, implementation.compatibility) await testInfo.attach('thumbnail-runtime', { contentType: 'application/json', body: JSON.stringify({ name: implementation.name, sha256: hash(implementation.code), recoveredIndividualFile: implementation.legacy, originalNpmArchive: false, media: path.relative(process.cwd(), sample) }) }) }) @@ -117,12 +122,20 @@ for (const implementation of implementations) { return { width: image.width, height: image.height, sourceWidth, sourceHeight, cellHeight: tool.option.height, mime: blob.type, bytes: blob.size, nonBlack, initialEvents, events: observed, first, second, created, revoked, connected: tool.video.isConnected, retiredReadyState: tool.video.readyState, retiredSrc: tool.video.getAttribute('src') } }) expect(result.width).toBe(240) - expect(result.cellHeight).toBe(implementation.legacy ? 30 : result.sourceHeight / result.sourceWidth * 80) + expect(result.cellHeight).toBe(implementation.legacy || implementation.publishedPolicy ? 30 : result.sourceHeight / result.sourceWidth * 80) expect(result.height).toBe(Math.trunc(result.cellHeight * 4 + 30)) expect(result.mime).toBe('image/png') expect(result.bytes).toBeGreaterThan(100) expect(result.nonBlack).toBeGreaterThan(100) expect(result.initialEvents.filter(item => item.event === 'update').map(item => item.progress)).toEqual(Array.from({ length: 10 }, (_, i) => (i + 1) / 10)) + if (implementation.publishedPolicy) { + const events = result.initialEvents + expect(events.find(item => item.event === 'video').at - events.find(item => item.event === 'file').at).toBeGreaterThanOrEqual(298) + const frames = events.filter(item => item.event === 'canvas' || item.event === 'update') + for (let index = 1; index < frames.length; index++) + expect(frames[index].at - frames[index - 1].at).toBeGreaterThanOrEqual(298) + expect(events.find(item => item.event === 'done').at - frames.at(-1).at).toBeGreaterThanOrEqual(598) + } expect(result.events.filter(item => item.event === 'done')).toHaveLength(2) expect(result.first).not.toBe(result.second) expect([...result.created].sort()).toEqual([...result.revoked].sort()) @@ -177,7 +190,7 @@ for (const implementation of implementations) { expect(result.revoked).toEqual(implementation.lifecycle ? result.created : [result.created[1]]) expect(result.revoked.includes(failed.first)).toBe(!!implementation.lifecycle) expect(result.repeated).toBe(implementation.inputOwnership ? undefined : 'NotFoundError') - expect(result.value === '').toBe(!implementation.legacy) + expect(result.value === '').toBe(!(implementation.legacy || implementation.publishedPolicy)) await testInfo.attach('thumbnail-media-failure', { contentType: 'application/json', body: JSON.stringify({ failed, result }) }) }) @@ -255,6 +268,8 @@ for (const implementation of implementations) { }) }) await expect.poll(() => page.evaluate(() => typeof window.releaseFirst)).toBe('function') + // Published policy retains the value; selecting the same file need not fire change. + await page.locator('#input').setInputFiles([]) await page.locator('#input').setInputFiles(sample) const result = await page.evaluate(async () => { const source = tool.videoUrl diff --git a/test/thumbnail-input.test.js b/test/thumbnail-input.test.js index 6e52768bc..7c741a5fa 100644 --- a/test/thumbnail-input.test.js +++ b/test/thumbnail-input.test.js @@ -7,7 +7,7 @@ const implementation = await thumbnailCandidate() function setup(wrapper = false) { const env = thumbnailEnvironment(implementation) const target = new env.Element(wrapper ? 'div' : 'input') - const instance = new env.Factory({ fileInput: target, number: 10, width: 20, height: 30, column: 3 }) + const instance = new env.Factory({ fileInput: target, number: 10, width: 20, height: 30, column: 3, compatibility: 'workspace-4.4' }) return { ...env, target, instance } } @@ -17,7 +17,7 @@ test('Thumbnail input ownership preserves class methods, constructor fields and assert.equal(exported.default, undefined) assert.deepEqual(Object.keys(instance), ['processing', 'option', 'video', 'duration', 'inputChange', 'ondrop']) assert.deepEqual(Object.getOwnPropertyNames(Factory.prototype), ['constructor', 'ondrop', 'setup', 'inputChange', 'loadVideo', 'start', 'creatScreenshotDate', 'creatCanvas', 'download', 'errorHandle', 'destroy']) - assert.deepEqual(Object.keys(Factory.DEFAULTS), ['number', 'width', 'height', 'column', 'begin', 'end']) + assert.deepEqual(Object.keys(Factory.DEFAULTS), ['delay', 'number', 'width', 'height', 'column', 'begin', 'end']) assert.equal(instance.setup({ custom: 7 }), instance) assert.equal(instance.option.custom, 7) instance.destroy() diff --git a/test/thumbnail-lifecycle.test.js b/test/thumbnail-lifecycle.test.js index 97567f405..037c511e1 100644 --- a/test/thumbnail-lifecycle.test.js +++ b/test/thumbnail-lifecycle.test.js @@ -16,7 +16,8 @@ function observe(promise) { } function setup(ready = true) { const env = thumbnailEnvironment(implementation) - env.instance = new env.Factory({ fileInput: new env.Element('input'), number: 10, width: 40, column: 3 }) + // These lifecycle fixtures retain the explicitly selected workspace timing. + env.instance = new env.Factory({ fileInput: new env.Element('input'), number: 10, width: 40, column: 3, compatibility: 'workspace-4.4' }) env.instance.loadVideo({ name: 'first.mp4', type: 'video/mp4' }) if (ready) { env.instance.video.duration = 100 @@ -409,7 +410,7 @@ test('Thumbnail URL creation reentrant load preserves the newer source and revok test('Thumbnail start before the first file retains its legacy wait-for-selection behavior', async () => { const env = thumbnailEnvironment(implementation) - env.instance = new env.Factory({ fileInput: new env.Element('input'), number: 10 }) + env.instance = new env.Factory({ fileInput: new env.Element('input'), number: 10, compatibility: 'workspace-4.4' }) const state = observe(env.instance.start()) env.instance.loadVideo({ name: 'first.mp4', type: 'video/mp4' }) env.instance.video.duration = 100 diff --git a/test/thumbnail-policy.test.js b/test/thumbnail-policy.test.js new file mode 100644 index 000000000..07fce4d3b --- /dev/null +++ b/test/thumbnail-policy.test.js @@ -0,0 +1,188 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Approved runtime policy compatibility. +import test from 'node:test' +import { thumbnailCandidate, thumbnailEnvironment, thumbnailHistorical } from './helpers/thumbnail.js' + +const candidate = await thumbnailCandidate() +const flush = () => new Promise(resolve => setImmediate(resolve)) +function create(option = {}) { + const env = thumbnailEnvironment(candidate) + env.input = new env.Element('input') + env.tool = new env.Factory({ fileInput: env.input, number: 10, width: 40, height: 25, ...option }) + return env +} +function tick(env, delay) { + const selected = [...env.timers].find(([, timer]) => Object.is(timer.delay, delay)) + assert(selected, `Expected owned timer for ${delay} ms`) + env.timers.delete(selected[0]) + selected[1].callback() +} +function ready(env) { + env.tool.video.duration = 100 + env.tool.video.readyState = 2 + env.tool.video.videoWidth = 160 + env.tool.video.videoHeight = 90 + env.tool.loadVideo({ name: 'sample.mp4', type: 'video/mp4' }) + for (const fn of env.tool.video.handlers.get('loadedmetadata')) + fn() +} + +test('Thumbnail root defaults match the recovered published profile, including key order', () => { + const actual = create() + const old = thumbnailEnvironment(thumbnailHistorical().find(item => item.legacy)) + assert.deepEqual(Object.keys(actual.Factory.DEFAULTS), Object.keys(old.Factory.DEFAULTS)) + for (const key of Object.keys(old.Factory.DEFAULTS)) + assert.equal(actual.Factory.DEFAULTS[key], old.Factory.DEFAULTS[key]) + actual.tool.destroy() +}) + +test('Thumbnail published policy validates and clamps delay/height while workspace preserves its inputs', () => { + const published = create({ height: 2, delay: 4000 }) + assert.equal(published.tool.option.height, 10) + assert.equal(published.tool.option.delay, 1000) + assert.throws(() => published.tool.setup({ delay: 'bad' }), /delay.*number/) + assert.throws(() => published.tool.setup({ height: 'bad' }), /height.*number/) + assert.throws(() => published.tool.setup({ compatibility: 'unknown' }), /compatibility/) + const workspace = create({ compatibility: 'workspace-4.4', height: 2, delay: 4000 }) + assert.equal(workspace.tool.option.height, 2) + assert.equal(workspace.tool.option.delay, 4000) + published.tool.destroy() + workspace.tool.destroy() +}) + +for (const compatibility of [undefined, 'published-3.5', 'workspace-4.4']) { + test(`Thumbnail ${compatibility || 'default'} preserves file/video timing and input reset policy`, () => { + const env = create({ compatibility, delay: 25 }) + const seen = [] + env.input.files = [{ name: 'sample.mp4', type: 'video/mp4' }] + env.input.value = 'selected' + env.tool.on('file', () => seen.push(['file', env.tool.video.src])) + env.tool.on('video', video => seen.push(['video', video.src])) + env.tool.inputChange({ target: env.input }) + const workspace = compatibility === 'workspace-4.4' + assert.deepEqual(seen, workspace ? [['file', undefined], ['video', env.tool.videoUrl]] : [['file', undefined]]) + assert.equal(env.input.value, workspace ? '' : 'selected') + if (!workspace) { + tick(env, 25) + assert.deepEqual(seen[1], ['video', env.tool.videoUrl]) + } + env.tool.destroy() + assert.equal(env.timers.size, 0) + }) +} + +test('Thumbnail default extraction keeps configured height and requires delay plus frame readiness', async () => { + const env = create({ delay: 25 }) + ready(env) + tick(env, 25) + env.tool.video.seeking = true + const work = env.tool.start() + await flush() + assert.equal(env.tool.option.height, 25) + tick(env, 25) + assert.equal(env.operations.filter(op => op.name === 'drawImage').length, 0) + env.tool.video.seeking = false + env.tool.video.oncanplay() + await flush() + assert.equal(env.operations.filter(op => op.name === 'drawImage').length, 1) + env.tool.destroy() + await assert.rejects(work, { name: 'AbortError' }) + assert.equal(env.timers.size, 0) +}) + +test('Thumbnail delayed video notifications are cancelled by replacement, failure and destroy', () => { + for (const action of ['replace', 'error', 'destroy']) { + const env = create({ delay: 25 }) + const seen = [] + env.tool.on('video', () => seen.push(env.tool.file.name)).on('error', () => {}) + env.tool.loadVideo({ name: 'old.mp4', type: 'video/mp4' }) + const stale = [...env.timers.values()].find(timer => timer.delay === 25) + assert(stale) + if (action === 'replace') { + env.tool.loadVideo({ name: 'new.mp4', type: 'video/mp4' }) + } + else if (action === 'error') { + for (const fn of env.tool.video.handlers.get('error')) + fn() + } + else { + env.tool.destroy() + } + stale.callback() + assert.deepEqual(seen, []) + if (action === 'replace') { + tick(env, 25) + assert.deepEqual(seen, ['new.mp4']) + } + env.tool.destroy() + assert.equal(env.timers.size, 0) + } +}) + +test('Thumbnail numeric NaN delay keeps the historical asynchronous timer boundary', () => { + const env = create({ delay: Number.NaN }) + let events = 0 + env.tool.on('video', () => events++) + env.tool.loadVideo({ name: 'sample.mp4', type: 'video/mp4' }) + assert.equal(events, 0) + tick(env, Number.NaN) + assert.equal(events, 1) + env.tool.destroy() +}) + +for (const finish of ['complete', 'destroy', 'replace', 'error']) { + test(`Thumbnail default final delay owns completion and ${finish} settlement`, async () => { + const env = create({ delay: 25 }) + ready(env) + tick(env, 25) + let done = 0 + const updates = [] + env.tool.on('done', () => done++).on('update', (_, progress) => updates.push(progress)) + const work = env.tool.start() + await flush() + for (let frame = 0; frame < 10; frame++) { + tick(env, 25) + await flush() + } + assert.deepEqual(updates, Array.from({ length: 10 }, (_, index) => (index + 1) / 10)) + assert.equal(done, 0) + assert.equal(env.tool.processing, true) + const final = [...env.timers.values()].find(timer => timer.delay === 50) + assert(final) + if (finish === 'complete') { + tick(env, 50) + await work + assert.equal(done, 1) + } + else { + if (finish === 'destroy') { + env.tool.destroy() + } + else if (finish === 'replace') { + env.tool.loadVideo({ name: 'next.mp4', type: 'video/mp4' }) + } + else { + env.tool.on('error', () => {}) + for (const fn of [...env.tool.video.handlers.get('error')]) + fn() + } + await assert.rejects(work, finish === 'error' ? /Unable to load video/ : { name: 'AbortError' }) + final.callback() + assert.equal(done, 0) + } + assert.equal(env.tool.processing, false) + env.tool.destroy() + assert.equal(env.timers.size, 0) + }) +} + +test('Thumbnail explicit workspace extraction retains aspect height with no policy timers', async () => { + const env = create({ compatibility: 'workspace-4.4', delay: 25 }) + ready(env) + const work = env.tool.start() + await work + assert.equal(env.tool.option.height, 22.5) + assert.equal(env.operations.filter(op => op.name === 'drawImage').length, 10) + assert.equal(env.timers.size, 0) + env.tool.destroy() +}) diff --git a/test/thumbnail-runtime.test.js b/test/thumbnail-runtime.test.js index e34d9ccd4..741c03011 100644 --- a/test/thumbnail-runtime.test.js +++ b/test/thumbnail-runtime.test.js @@ -6,9 +6,9 @@ import vm from 'node:vm' import { thumbnailCandidate, thumbnailEnvironment, thumbnailHistorical } from './helpers/thumbnail.js' const candidate = await thumbnailCandidate() -function create(implementation) { +function create(implementation, option = {}) { const env = thumbnailEnvironment(implementation) - return { ...env, tool: new env.Factory({ fileInput: new env.Element('input') }) } + return { ...env, tool: new env.Factory({ fileInput: new env.Element('input'), ...option }) } } const referenceModule = { exports: {} } vm.runInNewContext(fs.readFileSync(new URL('../refactor/baselines/thumbnail-vendor/tiny-emitter-2.1.0.txt', import.meta.url), 'utf8'), { module: referenceModule }) @@ -39,7 +39,8 @@ test('Thumbnail typed facade preserves descriptors, lazy fields, default class a assert.equal(tool.option.custom, metadata) assert.equal(tool.option.width, 21.5) assert.notEqual(Factory.DEFAULTS, Factory.DEFAULTS) - assert.deepEqual(Object.keys(Factory.DEFAULTS), Object.keys(baseline.Factory.DEFAULTS)) + const published = thumbnailEnvironment(thumbnailHistorical().find(item => item.legacy)) + assert.deepEqual(Object.keys(Factory.DEFAULTS), Object.keys(published.Factory.DEFAULTS)) assert(Number.isNaN(Factory.DEFAULTS.end)) tool.destroy() }) @@ -132,7 +133,7 @@ test('Thumbnail typed facade retains synchronous validation, option mutation and test('Thumbnail error event preserves arbitrary thrown message values and Promise rejection identity', async () => { for (const failure of [{ message: 7 }, { message: { detail: 'failure' } }, 3, null]) { - const { tool } = create(candidate) + const { tool } = create(candidate, { compatibility: 'workspace-4.4' }) tool.setup({ number: 10 }) tool.file = { name: 'sample.mp4' } tool.video.duration = 100 diff --git a/test/types/thumbnail.ts b/test/types/thumbnail.ts index f8bdfc9ce..c50399283 100644 --- a/test/types/thumbnail.ts +++ b/test/types/thumbnail.ts @@ -1,4 +1,4 @@ -import type { EventArgs, Option, ScreenshotPoint } from 'artplayer-tool-thumbnail' +import type { Compatibility, DefaultOptions, EventArgs, Option, ScreenshotPoint } from 'artplayer-tool-thumbnail' import Thumbnail from 'artplayer-tool-thumbnail' import Legacy from 'artplayer-tool-thumbnail/legacy' @@ -32,8 +32,14 @@ tool.off('file').setup({ width: 100 }).download() tool.loadVideo(input.files?.[0]) tool.loadVideo(null) const defaults: Thumbnail.SheetOptions = Thumbnail.DEFAULTS +const publishedDefaults: DefaultOptions = Thumbnail.DEFAULTS +const delay: number = publishedDefaults.delay +const mode: Compatibility = 'workspace-4.4' +tool.setup({ compatibility: mode, delay: undefined }) +legacy.setup({ compatibility: 'published-3.5', delay: 300, height: 25 }) +const sheet: Thumbnail.SheetOptions = { number: 10, width: 80, height: 25, column: 3, begin: 0, end: 20 } const end: void = tool.destroy() -void [legacy, duration, file, url, pending, canvas, points, extension, defaults, end] +void [legacy, duration, file, url, pending, canvas, points, extension, defaults, delay, sheet, end] // @ts-expect-error Numeric width is required. tool.setup({ width: '80' }) @@ -55,4 +61,8 @@ const earlyDensity: number = tool.density tool.on('error', (message: string) => message.length) // @ts-expect-error Built-in done does not carry a file. tool.emit('done', input.files?.[0]) +// @ts-expect-error Compatibility accepts only the two supported policies. +tool.setup({ compatibility: 'latest' }) +// @ts-expect-error Published delay is a numeric option. +tool.setup({ delay: '300' }) void [wrongInput, wrongResult, WrongAlias, earlyDensity]