diff --git a/docs/compiled/artplayer-plugin-auto-thumbnail.js b/docs/compiled/artplayer-plugin-auto-thumbnail.js index 093637ba6..6d6107829 100644 --- a/docs/compiled/artplayer-plugin-auto-thumbnail.js +++ b/docs/compiled/artplayer-plugin-auto-thumbnail.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";function e(e){let t,n=!1;for(const r of e)try{r()}catch(i){n||(n=!0,t=i)}return{failed:n,failure:t}}function t(t,n){const i="function"===typeof n.requestVideoFrameCallback&&"function"===typeof n.cancelVideoFrameCallback;let r;function o(){const t=r;if(r=void 0,n.onloadeddata=null,n.onseeked=null,!t)return;const i=e([()=>clearTimeout(t.timer),()=>{null!==t.frame&&n.cancelVideoFrameCallback(t.frame)}]);if(i.failed)throw i.failure}return t.own(o),(e,a)=>{if(o(),!t.active())return;const u={frame:null,timer:null,sought:!1,presented:!i,retries:0,sequence:0,started:!1};r=u;const d=()=>t.active()&&r===u,s=t.guard(()=>{d()&&u.sought&&u.presented&&(o(),t.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!i)return;const e=++u.sequence;u.presented=!1;const r=u.frame;if(u.frame=null,null!==r&&n.cancelVideoFrameCallback(r),!d())return;let o=!1;const a=n.requestVideoFrameCallback(t.guard(()=>{d()&&e===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,s())}));d()&&e===u.sequence&&!o?u.frame=a:n.cancelVideoFrameCallback(a)})(),d()&&(n.currentTime=e)},l=t.guard(()=>{d()&&!u.started&&(u.started=!0,n.onloadeddata=null,n.onseeked=t.guard(()=>{if(!d()||n.seeking)return;const t=n.currentTime;if(d())if(!Number.isFinite(t)||Math.abs(t-e)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,s()}),c())});if(u.timer=setTimeout(t.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!i||n.readyState>=2;d()&&(f?l():n.onloadeddata=l)}}function n(e,n){const i=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(i.onerror=e.guard(()=>{throw i.error||new Error("Auto-thumbnail media failed to load")}),i.onloadedmetadata=e.guard(()=>{i.onloadedmetadata=null;const r=i.duration,o=i.videoHeight,a=i.videoWidth,{height:u,canvasWidth:d,canvasHeight:s}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),i=Math.trunc(10*e.width),r=Math.trunc(n*Math.ceil(e.number/10));if(![n,i,r].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:i,canvasHeight:r}}(n,{duration:r,videoHeight:o,videoWidth:a});if(!e.active())return;i.width=a,i.height=o,i.style.width=`${a}px`,i.style.height=`${o}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=s;const l=c.getContext("2d");if(!l)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=t(e,i);let h=0;const m=e.guard(()=>{h>=n.number?e.dispose():f(r*h/n.number,e.guard(()=>{if(l.drawImage(i,h%10*n.width,Math.floor(h/10)*u,n.width,u),!e.active())return;let t=!1;c.toBlob(e.guard(i=>{t||(t=!0,e.publish(i,{height:u,column:10,number:n.number,width:n.width,scale:n.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(i.src=n.url))}return function(t){return async i=>{const r=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),o=function(t,n){let i,r,o=!1;const a=new Set;function u(e){a.delete(e)&&URL.revokeObjectURL(e)}function d(){const e=i;i=void 0,e?.dispose()}return{start:function(){if(o)return;const d=i,s=[];let c=!1;const l={active:()=>!o&&!c&&i===l,own(t){if(c){const i=e([t]);i.failed&&n(i.failure)}else s.push(t)},dispose(){if(c)return;c=!0,i===l&&(i=void 0);const t=e(s.splice(0).reverse());t.failed&&n(t.failure)},fail(e){l.active()&&(l.dispose(),n(e))},guard:e=>(...t)=>{if(l.active())try{return e(...t)}catch(n){l.fail(n)}},publish(i,o){if(!l.active())return;if(!i)throw new Error("Auto-thumbnail encoding returned no Blob");const d=URL.createObjectURL(i);if(a.add(d),!l.active())return void u(d);const s=r;r=d;try{t({url:d,...o})}catch(c){r===d&&(r=a.has(s)?s:void 0);const t=e([()=>u(d)]);throw t.failed&&n(t.failure),c}u(s)}};return i=l,d?.dispose(),l},cancel:d,destroy:function(){if(o)return;o=!0;const t=e([d,...[...a].map(e=>()=>u(e))]);r=void 0,t.failed&&n(t.failure)},get closed(){return o}}}(e=>{i.thumbnails=e},r),a=[],u=()=>{const e=o.start();e&&e.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(t,()=>i.option.url);e.active()&&n(e,r)})()},d=()=>{const t=a.splice(0),n=e([o.destroy,...t]);n.failed&&r(n.failure)};try{for(const[e,t]of[["destroy",d],["restart",o.cancel],["video:loadedmetadata",u]]){if(o.closed)break;a.push(()=>i.off(e,t)),i.on(e,t),o.closed&&i.off(e,t)}}catch(s){throw d(),s}return{name:"artplayerPluginAutoThumbnail"}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";function e(e){let t,n=!1;for(const r of e)try{r()}catch(i){n||(n=!0,t=i)}return{failed:n,failure:t}}function t(t,n){const i="function"===typeof n.requestVideoFrameCallback&&"function"===typeof n.cancelVideoFrameCallback;let r;function o(){const t=r;if(r=void 0,n.onloadeddata=null,n.onseeked=null,!t)return;const i=e([()=>{null!==t.timer&&clearTimeout(t.timer)},()=>{null!==t.frame&&n.cancelVideoFrameCallback(t.frame)}]);if(i.failed)throw i.failure}return t.own(o),(e,a)=>{if(o(),!t.active())return;const u={frame:null,timer:null,sought:!1,presented:!i,retries:0,sequence:0,started:!1};r=u;const d=()=>t.active()&&r===u,s=t.guard(()=>{d()&&u.sought&&u.presented&&(o(),t.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!i)return;const e=++u.sequence;u.presented=!1;const r=u.frame;if(u.frame=null,null!==r&&n.cancelVideoFrameCallback(r),!d())return;let o=!1;const a=n.requestVideoFrameCallback(t.guard(()=>{d()&&e===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,s())}));d()&&e===u.sequence&&!o?u.frame=a:n.cancelVideoFrameCallback(a)})(),d()&&(n.currentTime=e)},l=t.guard(()=>{d()&&!u.started&&(u.started=!0,n.onloadeddata=null,n.onseeked=t.guard(()=>{if(!d()||n.seeking)return;const t=n.currentTime;if(d())if(!Number.isFinite(t)||Math.abs(t-e)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,s()}),c())});if(u.timer=setTimeout(t.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!i||n.readyState>=2;d()&&(f?l():n.onloadeddata=l)}}function n(e,n){const i=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(i.onerror=e.guard(()=>{throw i.error||new Error("Auto-thumbnail media failed to load")}),i.onloadedmetadata=e.guard(()=>{i.onloadedmetadata=null;const r=i.duration,o=i.videoHeight,a=i.videoWidth,{height:u,canvasWidth:d,canvasHeight:s}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),i=Math.trunc(10*e.width),r=Math.trunc(n*Math.ceil(e.number/10));if(![n,i,r].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:i,canvasHeight:r}}(n,{duration:r,videoHeight:o,videoWidth:a});if(!e.active())return;i.width=a,i.height=o,i.style.width=`${a}px`,i.style.height=`${o}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=s;const l=c.getContext("2d");if(!l)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=t(e,i);let h=0;const m=e.guard(()=>{h>=n.number?e.dispose():f(r*h/n.number,e.guard(()=>{if(l.drawImage(i,h%10*n.width,Math.floor(h/10)*u,n.width,u),!e.active())return;let t=!1;c.toBlob(e.guard(i=>{t||(t=!0,e.publish(i,{height:u,column:10,number:n.number,width:n.width,scale:n.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(i.src=n.url))}return function(t){return async i=>{const r=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),o=function(t,n){let i,r,o=!1;const a=new Set;function u(e){void 0!==e&&a.delete(e)&&URL.revokeObjectURL(e)}function d(){const e=i;i=void 0,e?.dispose()}return{start:function(){if(o)return;const d=i,s=[];let c=!1;const l={active:()=>!o&&!c&&i===l,own(t){if(c){const i=e([t]);i.failed&&n(i.failure)}else s.push(t)},dispose(){if(c)return;c=!0,i===l&&(i=void 0);const t=e(s.splice(0).reverse());t.failed&&n(t.failure)},fail(e){l.active()&&(l.dispose(),n(e))},guard:e=>(...t)=>{if(l.active())try{return e(...t)}catch(n){l.fail(n)}},publish(i,o){if(!l.active())return;if(!i)throw new Error("Auto-thumbnail encoding returned no Blob");const d=URL.createObjectURL(i);if(a.add(d),!l.active())return void u(d);const s=r;r=d;try{t({url:d,...o})}catch(c){r===d&&(r=void 0!==s&&a.has(s)?s:void 0);const t=e([()=>u(d)]);throw t.failed&&n(t.failure),c}u(s)}};return i=l,d?.dispose(),l},cancel:d,destroy:function(){if(o)return;o=!0;const t=e([d,...[...a].map(e=>()=>u(e))]);r=void 0,t.failed&&n(t.failure)},get closed(){return o}}}(e=>{i.thumbnails=e},r),a=[],u=()=>{const e=o.start();e&&e.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(t,()=>i.option.url);e.active()&&n(e,r)})()},d=()=>{const t=a.splice(0),n=e([o.destroy,...t]);n.failed&&r(n.failure)};try{const e=[["destroy",d],["restart",o.cancel],["video:loadedmetadata",u]];for(const[t,n]of e){if(o.closed)break;a.push(()=>i.off(t,n)),i.on(t,n),o.closed&&i.off(t,n)}}catch(s){throw d(),s}return{name:"artplayerPluginAutoThumbnail"}}}}); diff --git a/docs/compiled/artplayer-plugin-auto-thumbnail.legacy.js b/docs/compiled/artplayer-plugin-auto-thumbnail.legacy.js index 148680572..e024473a3 100644 --- a/docs/compiled/artplayer-plugin-auto-thumbnail.legacy.js +++ b/docs/compiled/artplayer-plugin-auto-thumbnail.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,i=(t,n,r)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[n]=r;function o(e){let t,n=!1;for(const i of e)try{i()}catch(r){n||(n=!0,t=r)}return{failed:n,failure:t}}function a(e,a){let u,d,l=!1;const c=new Set;function s(e){c.delete(e)&&URL.revokeObjectURL(e)}function f(){const e=u;u=void 0,null==e||e.dispose()}return{start:function(){if(l)return;const f=u,h=[];let m=!1;const b={active:()=>!l&&!m&&u===b,own(e){if(m){const t=o([e]);t.failed&&a(t.failure)}else h.push(e)},dispose(){if(m)return;m=!0,u===b&&(u=void 0);const e=o(h.splice(0).reverse());e.failed&&a(e.failure)},fail(e){b.active()&&(b.dispose(),a(e))},guard:e=>(...t)=>{if(b.active())try{return e(...t)}catch(n){b.fail(n)}},publish(u,l){if(!b.active())return;if(!u)throw new Error("Auto-thumbnail encoding returned no Blob");const f=URL.createObjectURL(u);if(c.add(f),!b.active())return void s(f);const h=d;d=f;try{e(((e,o)=>{for(var a in o||(o={}))n.call(o,a)&&i(e,a,o[a]);if(t)for(var a of t(o))r.call(o,a)&&i(e,a,o[a]);return e})({url:f},l))}catch(m){d===f&&(d=c.has(h)?h:void 0);const e=o([()=>s(f)]);throw e.failed&&a(e.failure),m}s(h)}};return u=b,null==f||f.dispose(),b},cancel:f,destroy:function(){if(l)return;l=!0;const e=o([f,...[...c].map(e=>()=>s(e))]);d=void 0,e.failed&&a(e.failure)},get closed(){return l}}}function u(e,t){const n="function"===typeof t.requestVideoFrameCallback&&"function"===typeof t.cancelVideoFrameCallback;let r;function i(){const e=r;if(r=void 0,t.onloadeddata=null,t.onseeked=null,!e)return;const n=o([()=>clearTimeout(e.timer),()=>{null!==e.frame&&t.cancelVideoFrameCallback(e.frame)}]);if(n.failed)throw n.failure}return e.own(i),(o,a)=>{if(i(),!e.active())return;const u={frame:null,timer:null,sought:!1,presented:!n,retries:0,sequence:0,started:!1};r=u;const d=()=>e.active()&&r===u,l=e.guard(()=>{d()&&u.sought&&u.presented&&(i(),e.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!n)return;const r=++u.sequence;u.presented=!1;const i=u.frame;if(u.frame=null,null!==i&&t.cancelVideoFrameCallback(i),!d())return;let o=!1;const a=t.requestVideoFrameCallback(e.guard(()=>{d()&&r===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,l())}));d()&&r===u.sequence&&!o?u.frame=a:t.cancelVideoFrameCallback(a)})(),d()&&(t.currentTime=o)},s=e.guard(()=>{d()&&!u.started&&(u.started=!0,t.onloadeddata=null,t.onseeked=e.guard(()=>{if(!d()||t.seeking)return;const e=t.currentTime;if(d())if(!Number.isFinite(e)||Math.abs(e-o)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,l()}),c())});if(u.timer=setTimeout(e.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!n||t.readyState>=2;d()&&(f?s():t.onloadeddata=s)}}function d(e,t){const n=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(n.onerror=e.guard(()=>{throw n.error||new Error("Auto-thumbnail media failed to load")}),n.onloadedmetadata=e.guard(()=>{n.onloadedmetadata=null;const r=n.duration,i=n.videoHeight,o=n.videoWidth,{height:a,canvasWidth:d,canvasHeight:l}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),r=Math.trunc(10*e.width),i=Math.trunc(n*Math.ceil(e.number/10));if(![n,r,i].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:r,canvasHeight:i}}(t,{duration:r,videoHeight:i,videoWidth:o});if(!e.active())return;n.width=o,n.height=i,n.style.width=`${o}px`,n.style.height=`${i}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=l;const s=c.getContext("2d");if(!s)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=u(e,n);let h=0;const m=e.guard(()=>{h>=t.number?e.dispose():f(r*h/t.number,e.guard(()=>{if(s.drawImage(n,h%10*t.width,Math.floor(h/10)*a,t.width,a),!e.active())return;let r=!1;c.toBlob(e.guard(n=>{r||(r=!0,e.publish(n,{height:a,column:10,number:t.number,width:t.width,scale:t.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(n.src=t.url))}return function(e){return t=>{return n=null,r=null,i=function*(){const n=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),r=a(e=>{t.thumbnails=e},n),i=[],u=()=>{const n=r.start();n&&n.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(e,()=>t.option.url);n.active()&&d(n,r)})()},l=()=>{const e=i.splice(0),t=o([r.destroy,...e]);t.failed&&n(t.failure)};try{for(const[e,n]of[["destroy",l],["restart",r.cancel],["video:loadedmetadata",u]]){if(r.closed)break;i.push(()=>t.off(e,n)),t.on(e,n),r.closed&&t.off(e,n)}}catch(c){throw l(),c}return{name:"artplayerPluginAutoThumbnail"}},new Promise((e,t)=>{var o=e=>{try{u(i.next(e))}catch(n){t(n)}},a=e=>{try{u(i.throw(e))}catch(n){t(n)}},u=t=>t.done?e(t.value):Promise.resolve(t.value).then(o,a);u((i=i.apply(n,r)).next())});var n,r,i}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,i=(t,n,r)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[n]=r;function o(e){let t,n=!1;for(const i of e)try{i()}catch(r){n||(n=!0,t=r)}return{failed:n,failure:t}}function a(e,a){let u,d,l=!1;const c=new Set;function s(e){void 0!==e&&c.delete(e)&&URL.revokeObjectURL(e)}function f(){const e=u;u=void 0,null==e||e.dispose()}return{start:function(){if(l)return;const f=u,h=[];let m=!1;const v={active:()=>!l&&!m&&u===v,own(e){if(m){const t=o([e]);t.failed&&a(t.failure)}else h.push(e)},dispose(){if(m)return;m=!0,u===v&&(u=void 0);const e=o(h.splice(0).reverse());e.failed&&a(e.failure)},fail(e){v.active()&&(v.dispose(),a(e))},guard:e=>(...t)=>{if(v.active())try{return e(...t)}catch(n){v.fail(n)}},publish(u,l){if(!v.active())return;if(!u)throw new Error("Auto-thumbnail encoding returned no Blob");const f=URL.createObjectURL(u);if(c.add(f),!v.active())return void s(f);const h=d;d=f;try{e(((e,o)=>{for(var a in o||(o={}))n.call(o,a)&&i(e,a,o[a]);if(t)for(var a of t(o))r.call(o,a)&&i(e,a,o[a]);return e})({url:f},l))}catch(m){d===f&&(d=void 0!==h&&c.has(h)?h:void 0);const e=o([()=>s(f)]);throw e.failed&&a(e.failure),m}s(h)}};return u=v,null==f||f.dispose(),v},cancel:f,destroy:function(){if(l)return;l=!0;const e=o([f,...[...c].map(e=>()=>s(e))]);d=void 0,e.failed&&a(e.failure)},get closed(){return l}}}function u(e,t){const n="function"===typeof t.requestVideoFrameCallback&&"function"===typeof t.cancelVideoFrameCallback;let r;function i(){const e=r;if(r=void 0,t.onloadeddata=null,t.onseeked=null,!e)return;const n=o([()=>{null!==e.timer&&clearTimeout(e.timer)},()=>{null!==e.frame&&t.cancelVideoFrameCallback(e.frame)}]);if(n.failed)throw n.failure}return e.own(i),(o,a)=>{if(i(),!e.active())return;const u={frame:null,timer:null,sought:!1,presented:!n,retries:0,sequence:0,started:!1};r=u;const d=()=>e.active()&&r===u,l=e.guard(()=>{d()&&u.sought&&u.presented&&(i(),e.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!n)return;const r=++u.sequence;u.presented=!1;const i=u.frame;if(u.frame=null,null!==i&&t.cancelVideoFrameCallback(i),!d())return;let o=!1;const a=t.requestVideoFrameCallback(e.guard(()=>{d()&&r===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,l())}));d()&&r===u.sequence&&!o?u.frame=a:t.cancelVideoFrameCallback(a)})(),d()&&(t.currentTime=o)},s=e.guard(()=>{d()&&!u.started&&(u.started=!0,t.onloadeddata=null,t.onseeked=e.guard(()=>{if(!d()||t.seeking)return;const e=t.currentTime;if(d())if(!Number.isFinite(e)||Math.abs(e-o)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,l()}),c())});if(u.timer=setTimeout(e.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!n||t.readyState>=2;d()&&(f?s():t.onloadeddata=s)}}function d(e,t){const n=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(n.onerror=e.guard(()=>{throw n.error||new Error("Auto-thumbnail media failed to load")}),n.onloadedmetadata=e.guard(()=>{n.onloadedmetadata=null;const r=n.duration,i=n.videoHeight,o=n.videoWidth,{height:a,canvasWidth:d,canvasHeight:l}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),r=Math.trunc(10*e.width),i=Math.trunc(n*Math.ceil(e.number/10));if(![n,r,i].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:r,canvasHeight:i}}(t,{duration:r,videoHeight:i,videoWidth:o});if(!e.active())return;n.width=o,n.height=i,n.style.width=`${o}px`,n.style.height=`${i}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=l;const s=c.getContext("2d");if(!s)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=u(e,n);let h=0;const m=e.guard(()=>{h>=t.number?e.dispose():f(r*h/t.number,e.guard(()=>{if(s.drawImage(n,h%10*t.width,Math.floor(h/10)*a,t.width,a),!e.active())return;let r=!1;c.toBlob(e.guard(n=>{r||(r=!0,e.publish(n,{height:a,column:10,number:t.number,width:t.width,scale:t.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(n.src=t.url))}return function(e){return t=>{return n=null,r=null,i=function*(){const n=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),r=a(e=>{t.thumbnails=e},n),i=[],u=()=>{const n=r.start();n&&n.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(e,()=>t.option.url);n.active()&&d(n,r)})()},l=()=>{const e=i.splice(0),t=o([r.destroy,...e]);t.failed&&n(t.failure)};try{const e=[["destroy",l],["restart",r.cancel],["video:loadedmetadata",u]];for(const[n,o]of e){if(r.closed)break;i.push(()=>t.off(n,o)),t.on(n,o),r.closed&&t.off(n,o)}}catch(c){throw l(),c}return{name:"artplayerPluginAutoThumbnail"}},new Promise((e,t)=>{var o=e=>{try{u(i.next(e))}catch(n){t(n)}},a=e=>{try{u(i.throw(e))}catch(n){t(n)}},u=t=>t.done?e(t.value):Promise.resolve(t.value).then(o,a);u((i=i.apply(n,r)).next())});var n,r,i}}}); diff --git a/docs/compiled/artplayer-plugin-auto-thumbnail.mjs b/docs/compiled/artplayer-plugin-auto-thumbnail.mjs index 77f24658f..948ba1870 100644 --- a/docs/compiled/artplayer-plugin-auto-thumbnail.mjs +++ b/docs/compiled/artplayer-plugin-auto-thumbnail.mjs @@ -25,7 +25,7 @@ function createSession(publish, report) { let lastUrl; const urls = /* @__PURE__ */ new Set(); function release(url) { - if (urls.delete(url)) + if (url !== void 0 && urls.delete(url)) URL.revokeObjectURL(url); } function cancel() { @@ -103,7 +103,7 @@ function createSession(publish, report) { publish({ url, ...config }); } catch (error) { if (lastUrl === url) - lastUrl = urls.has(previousUrl) ? previousUrl : void 0; + lastUrl = previousUrl !== void 0 && urls.has(previousUrl) ? previousUrl : void 0; const result = cleanupAll([() => release(url)]); if (result.failed) report(result.failure); @@ -136,7 +136,10 @@ function createFrameReader(job, video) { if (!previous) return; const result = cleanupAll([ - () => clearTimeout(previous.timer), + () => { + if (previous.timer !== null) + clearTimeout(previous.timer); + }, () => { if (previous.frame !== null) video.cancelVideoFrameCallback(previous.frame); @@ -360,7 +363,8 @@ function artplayerPluginAutoThumbnail(option) { report(result.failure); }; try { - for (const [name, callback] of [["destroy", onDestroy], ["restart", session.cancel], ["video:loadedmetadata", onMetadata]]) { + const events = [["destroy", onDestroy], ["restart", session.cancel], ["video:loadedmetadata", onMetadata]]; + for (const [name, callback] of events) { if (session.closed) break; subscriptions.push(() => art.off(name, callback)); diff --git a/packages/artplayer-plugin-auto-thumbnail/.npmignore b/packages/artplayer-plugin-auto-thumbnail/.npmignore index 4439c6874..2b272d565 100644 --- a/packages/artplayer-plugin-auto-thumbnail/.npmignore +++ b/packages/artplayer-plugin-auto-thumbnail/.npmignore @@ -2,4 +2,5 @@ node_modules .DS_Store yarn.lock package-lock.json -src \ No newline at end of file +src +tsconfig.json diff --git a/packages/artplayer-plugin-auto-thumbnail/ARCHITECTURE.md b/packages/artplayer-plugin-auto-thumbnail/ARCHITECTURE.md index 82d30e9d3..787a233b5 100644 --- a/packages/artplayer-plugin-auto-thumbnail/ARCHITECTURE.md +++ b/packages/artplayer-plugin-auto-thumbnail/ARCHITECTURE.md @@ -7,39 +7,42 @@ for video processing. ## Modules and ownership -- `src/index.js` owns ArtPlayer subscriptions and the public factory. Options are +- `src/index.ts` owns ArtPlayer subscriptions and the public factory. Options are read at each `video:loadedmetadata` event, including subsequent changes to the original options object. `restart` cancels the old decoder immediately. `destroy` closes the session and removes subscriptions. Partial registration is rolled back. -- `src/options.js` preserves truthy defaults and computes the ten-column layout. +- `src/options.ts` preserves truthy defaults and computes the ten-column layout. Coercible numeric JS inputs and fractional frame counts retain their previous behavior; raw option values are forwarded to the thumbnail configuration. Invalid/nonfinite dimensions fail before canvas allocation. Canvas dimensions are checked against integer/IDL bounds, not a universal memory budget. -- `src/session.js` owns one active extraction job and the generated object URLs. +- `src/session.ts` owns one active extraction job and the generated object URLs. Guards prevent obsolete callbacks from publishing or scheduling another frame. Cleanup runs all registered actions even if one fails. Installing a replacement before cleaning up its predecessor makes synchronous host reentry observable. -- `src/extraction.js` owns the private video and canvas, metadata/seek/error +- `src/extraction.ts` owns the private video and canvas, metadata/seek/error handlers, and the frame/encode sequence. Each valid draw is encoded before the next seek. The final decoder is paused, cleared and reset; the final sheet URL remains owned by the session until another usable sheet replaces it or destroy. -- `src/video.js` creates and owns the hidden media element. It is attached to the +- `src/video.ts` creates and owns the hidden media element. It is attached to the document root because detached media loses drawable pixels on Windows WebKit. `visibility:hidden` preserves its rendered box; `display:none` and a 1px box do not. Metadata locks the box to intrinsic dimensions, overriding ordinary global video sizing rules. It is silent, excluded from focus/accessibility, and never played by this implementation. Cleanup also removes the element if insertion, cancellation, pause or decoder reset fails. -- `src/frames.js` owns one pending sample, its seek/data handlers, deadline and +- `src/frames.ts` owns one pending sample, its seek/data handlers, deadline and native presentation callback. When request/cancel frame callbacks are both available it waits for loaded data, then requires both seek completion and frame presentation before drawing. Callback identities are invalidated on retry and replacement; late/duplicate delivery cannot complete a newer sample. One job cleanup handles every frame without accumulating per-frame cleanup closures. +- `src/types.ts` defines internal configuration, sheet data, the minimal host and + guarded extraction-job callbacks. `guard` retains argument/result types and the + inactive undefined result. No public declarations are generated from this file. Only the entry module receives ArtPlayer. Extraction receives a guarded job and a - configuration snapshot. These internal modules do not add public player fields, +configuration snapshot. These internal modules do not add public player fields, plugin methods, or a dependency on a particular core implementation. ## Compatibility and intentional fixes @@ -80,6 +83,7 @@ Use the root Yarn toolchain: ```sh yarn test:auto-thumbnail +yarn exec tsc -p packages/artplayer-plugin-auto-thumbnail/tsconfig.json --noEmit yarn test:browser test/browser/auto-thumbnail-lifecycle.spec.js yarn test:browser test/browser/auto-thumbnail-pixels.spec.js yarn build artplayer-plugin-auto-thumbnail @@ -109,9 +113,18 @@ red, black, blue and yellow sections. Its command and fingerprint are in directory with `ARTPLAYER_FFMPEG` and `node scripts/generate-auto-thumbnail-fixture.mjs `; never silently replace the committed fixture. -Task04 converts these modules to strict TypeScript and resolves historical public -declaration/CommonJS differences. Task05 verifies old/final cores and actual +Task03's internal-type checkpoint has converted the six runtime modules to strict +TypeScript. Numeric annotations describe the nominal options; no runtime casts or +normalization were added, so historical coercible JS inputs retain the same +validation/arithmetic and raw values. Missing options retain the existing behavior. +Task04 still resolves public declarations, implementation-to-consumer contracts and +historical CommonJS differences. Task05 verifies old/final cores and actual devices; task06 covers installed package entries and the real demo/editor. The types and package version are unchanged at this checkpoint. Use `refactor/baselines/auto-thumbnail-contract.md` and `auto-thumbnail-failures.md` as the historical evidence map; do not regenerate those baselines from this source. + +The internal type checkpoint and its core-host assignment fixture are recorded in +`refactor/changes/2026-09-13-PKG-AUTO-THUMB-03-internal-types.md`. A core host can +hold partial user thumbnail options; extraction publishes a complete sheet. Do not +require the host's existing getter to have every generated-sheet field. diff --git a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js index 093637ba6..6d6107829 100644 --- a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js +++ b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";function e(e){let t,n=!1;for(const r of e)try{r()}catch(i){n||(n=!0,t=i)}return{failed:n,failure:t}}function t(t,n){const i="function"===typeof n.requestVideoFrameCallback&&"function"===typeof n.cancelVideoFrameCallback;let r;function o(){const t=r;if(r=void 0,n.onloadeddata=null,n.onseeked=null,!t)return;const i=e([()=>clearTimeout(t.timer),()=>{null!==t.frame&&n.cancelVideoFrameCallback(t.frame)}]);if(i.failed)throw i.failure}return t.own(o),(e,a)=>{if(o(),!t.active())return;const u={frame:null,timer:null,sought:!1,presented:!i,retries:0,sequence:0,started:!1};r=u;const d=()=>t.active()&&r===u,s=t.guard(()=>{d()&&u.sought&&u.presented&&(o(),t.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!i)return;const e=++u.sequence;u.presented=!1;const r=u.frame;if(u.frame=null,null!==r&&n.cancelVideoFrameCallback(r),!d())return;let o=!1;const a=n.requestVideoFrameCallback(t.guard(()=>{d()&&e===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,s())}));d()&&e===u.sequence&&!o?u.frame=a:n.cancelVideoFrameCallback(a)})(),d()&&(n.currentTime=e)},l=t.guard(()=>{d()&&!u.started&&(u.started=!0,n.onloadeddata=null,n.onseeked=t.guard(()=>{if(!d()||n.seeking)return;const t=n.currentTime;if(d())if(!Number.isFinite(t)||Math.abs(t-e)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,s()}),c())});if(u.timer=setTimeout(t.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!i||n.readyState>=2;d()&&(f?l():n.onloadeddata=l)}}function n(e,n){const i=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(i.onerror=e.guard(()=>{throw i.error||new Error("Auto-thumbnail media failed to load")}),i.onloadedmetadata=e.guard(()=>{i.onloadedmetadata=null;const r=i.duration,o=i.videoHeight,a=i.videoWidth,{height:u,canvasWidth:d,canvasHeight:s}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),i=Math.trunc(10*e.width),r=Math.trunc(n*Math.ceil(e.number/10));if(![n,i,r].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:i,canvasHeight:r}}(n,{duration:r,videoHeight:o,videoWidth:a});if(!e.active())return;i.width=a,i.height=o,i.style.width=`${a}px`,i.style.height=`${o}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=s;const l=c.getContext("2d");if(!l)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=t(e,i);let h=0;const m=e.guard(()=>{h>=n.number?e.dispose():f(r*h/n.number,e.guard(()=>{if(l.drawImage(i,h%10*n.width,Math.floor(h/10)*u,n.width,u),!e.active())return;let t=!1;c.toBlob(e.guard(i=>{t||(t=!0,e.publish(i,{height:u,column:10,number:n.number,width:n.width,scale:n.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(i.src=n.url))}return function(t){return async i=>{const r=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),o=function(t,n){let i,r,o=!1;const a=new Set;function u(e){a.delete(e)&&URL.revokeObjectURL(e)}function d(){const e=i;i=void 0,e?.dispose()}return{start:function(){if(o)return;const d=i,s=[];let c=!1;const l={active:()=>!o&&!c&&i===l,own(t){if(c){const i=e([t]);i.failed&&n(i.failure)}else s.push(t)},dispose(){if(c)return;c=!0,i===l&&(i=void 0);const t=e(s.splice(0).reverse());t.failed&&n(t.failure)},fail(e){l.active()&&(l.dispose(),n(e))},guard:e=>(...t)=>{if(l.active())try{return e(...t)}catch(n){l.fail(n)}},publish(i,o){if(!l.active())return;if(!i)throw new Error("Auto-thumbnail encoding returned no Blob");const d=URL.createObjectURL(i);if(a.add(d),!l.active())return void u(d);const s=r;r=d;try{t({url:d,...o})}catch(c){r===d&&(r=a.has(s)?s:void 0);const t=e([()=>u(d)]);throw t.failed&&n(t.failure),c}u(s)}};return i=l,d?.dispose(),l},cancel:d,destroy:function(){if(o)return;o=!0;const t=e([d,...[...a].map(e=>()=>u(e))]);r=void 0,t.failed&&n(t.failure)},get closed(){return o}}}(e=>{i.thumbnails=e},r),a=[],u=()=>{const e=o.start();e&&e.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(t,()=>i.option.url);e.active()&&n(e,r)})()},d=()=>{const t=a.splice(0),n=e([o.destroy,...t]);n.failed&&r(n.failure)};try{for(const[e,t]of[["destroy",d],["restart",o.cancel],["video:loadedmetadata",u]]){if(o.closed)break;a.push(()=>i.off(e,t)),i.on(e,t),o.closed&&i.off(e,t)}}catch(s){throw d(),s}return{name:"artplayerPluginAutoThumbnail"}}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";function e(e){let t,n=!1;for(const r of e)try{r()}catch(i){n||(n=!0,t=i)}return{failed:n,failure:t}}function t(t,n){const i="function"===typeof n.requestVideoFrameCallback&&"function"===typeof n.cancelVideoFrameCallback;let r;function o(){const t=r;if(r=void 0,n.onloadeddata=null,n.onseeked=null,!t)return;const i=e([()=>{null!==t.timer&&clearTimeout(t.timer)},()=>{null!==t.frame&&n.cancelVideoFrameCallback(t.frame)}]);if(i.failed)throw i.failure}return t.own(o),(e,a)=>{if(o(),!t.active())return;const u={frame:null,timer:null,sought:!1,presented:!i,retries:0,sequence:0,started:!1};r=u;const d=()=>t.active()&&r===u,s=t.guard(()=>{d()&&u.sought&&u.presented&&(o(),t.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!i)return;const e=++u.sequence;u.presented=!1;const r=u.frame;if(u.frame=null,null!==r&&n.cancelVideoFrameCallback(r),!d())return;let o=!1;const a=n.requestVideoFrameCallback(t.guard(()=>{d()&&e===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,s())}));d()&&e===u.sequence&&!o?u.frame=a:n.cancelVideoFrameCallback(a)})(),d()&&(n.currentTime=e)},l=t.guard(()=>{d()&&!u.started&&(u.started=!0,n.onloadeddata=null,n.onseeked=t.guard(()=>{if(!d()||n.seeking)return;const t=n.currentTime;if(d())if(!Number.isFinite(t)||Math.abs(t-e)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,s()}),c())});if(u.timer=setTimeout(t.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!i||n.readyState>=2;d()&&(f?l():n.onloadeddata=l)}}function n(e,n){const i=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(i.onerror=e.guard(()=>{throw i.error||new Error("Auto-thumbnail media failed to load")}),i.onloadedmetadata=e.guard(()=>{i.onloadedmetadata=null;const r=i.duration,o=i.videoHeight,a=i.videoWidth,{height:u,canvasWidth:d,canvasHeight:s}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),i=Math.trunc(10*e.width),r=Math.trunc(n*Math.ceil(e.number/10));if(![n,i,r].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:i,canvasHeight:r}}(n,{duration:r,videoHeight:o,videoWidth:a});if(!e.active())return;i.width=a,i.height=o,i.style.width=`${a}px`,i.style.height=`${o}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=s;const l=c.getContext("2d");if(!l)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=t(e,i);let h=0;const m=e.guard(()=>{h>=n.number?e.dispose():f(r*h/n.number,e.guard(()=>{if(l.drawImage(i,h%10*n.width,Math.floor(h/10)*u,n.width,u),!e.active())return;let t=!1;c.toBlob(e.guard(i=>{t||(t=!0,e.publish(i,{height:u,column:10,number:n.number,width:n.width,scale:n.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(i.src=n.url))}return function(t){return async i=>{const r=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),o=function(t,n){let i,r,o=!1;const a=new Set;function u(e){void 0!==e&&a.delete(e)&&URL.revokeObjectURL(e)}function d(){const e=i;i=void 0,e?.dispose()}return{start:function(){if(o)return;const d=i,s=[];let c=!1;const l={active:()=>!o&&!c&&i===l,own(t){if(c){const i=e([t]);i.failed&&n(i.failure)}else s.push(t)},dispose(){if(c)return;c=!0,i===l&&(i=void 0);const t=e(s.splice(0).reverse());t.failed&&n(t.failure)},fail(e){l.active()&&(l.dispose(),n(e))},guard:e=>(...t)=>{if(l.active())try{return e(...t)}catch(n){l.fail(n)}},publish(i,o){if(!l.active())return;if(!i)throw new Error("Auto-thumbnail encoding returned no Blob");const d=URL.createObjectURL(i);if(a.add(d),!l.active())return void u(d);const s=r;r=d;try{t({url:d,...o})}catch(c){r===d&&(r=void 0!==s&&a.has(s)?s:void 0);const t=e([()=>u(d)]);throw t.failed&&n(t.failure),c}u(s)}};return i=l,d?.dispose(),l},cancel:d,destroy:function(){if(o)return;o=!0;const t=e([d,...[...a].map(e=>()=>u(e))]);r=void 0,t.failed&&n(t.failure)},get closed(){return o}}}(e=>{i.thumbnails=e},r),a=[],u=()=>{const e=o.start();e&&e.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(t,()=>i.option.url);e.active()&&n(e,r)})()},d=()=>{const t=a.splice(0),n=e([o.destroy,...t]);n.failed&&r(n.failure)};try{const e=[["destroy",d],["restart",o.cancel],["video:loadedmetadata",u]];for(const[t,n]of e){if(o.closed)break;a.push(()=>i.off(t,n)),i.on(t,n),o.closed&&i.off(t,n)}}catch(s){throw d(),s}return{name:"artplayerPluginAutoThumbnail"}}}}); diff --git a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js index 148680572..e024473a3 100644 --- a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js +++ b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,i=(t,n,r)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[n]=r;function o(e){let t,n=!1;for(const i of e)try{i()}catch(r){n||(n=!0,t=r)}return{failed:n,failure:t}}function a(e,a){let u,d,l=!1;const c=new Set;function s(e){c.delete(e)&&URL.revokeObjectURL(e)}function f(){const e=u;u=void 0,null==e||e.dispose()}return{start:function(){if(l)return;const f=u,h=[];let m=!1;const b={active:()=>!l&&!m&&u===b,own(e){if(m){const t=o([e]);t.failed&&a(t.failure)}else h.push(e)},dispose(){if(m)return;m=!0,u===b&&(u=void 0);const e=o(h.splice(0).reverse());e.failed&&a(e.failure)},fail(e){b.active()&&(b.dispose(),a(e))},guard:e=>(...t)=>{if(b.active())try{return e(...t)}catch(n){b.fail(n)}},publish(u,l){if(!b.active())return;if(!u)throw new Error("Auto-thumbnail encoding returned no Blob");const f=URL.createObjectURL(u);if(c.add(f),!b.active())return void s(f);const h=d;d=f;try{e(((e,o)=>{for(var a in o||(o={}))n.call(o,a)&&i(e,a,o[a]);if(t)for(var a of t(o))r.call(o,a)&&i(e,a,o[a]);return e})({url:f},l))}catch(m){d===f&&(d=c.has(h)?h:void 0);const e=o([()=>s(f)]);throw e.failed&&a(e.failure),m}s(h)}};return u=b,null==f||f.dispose(),b},cancel:f,destroy:function(){if(l)return;l=!0;const e=o([f,...[...c].map(e=>()=>s(e))]);d=void 0,e.failed&&a(e.failure)},get closed(){return l}}}function u(e,t){const n="function"===typeof t.requestVideoFrameCallback&&"function"===typeof t.cancelVideoFrameCallback;let r;function i(){const e=r;if(r=void 0,t.onloadeddata=null,t.onseeked=null,!e)return;const n=o([()=>clearTimeout(e.timer),()=>{null!==e.frame&&t.cancelVideoFrameCallback(e.frame)}]);if(n.failed)throw n.failure}return e.own(i),(o,a)=>{if(i(),!e.active())return;const u={frame:null,timer:null,sought:!1,presented:!n,retries:0,sequence:0,started:!1};r=u;const d=()=>e.active()&&r===u,l=e.guard(()=>{d()&&u.sought&&u.presented&&(i(),e.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!n)return;const r=++u.sequence;u.presented=!1;const i=u.frame;if(u.frame=null,null!==i&&t.cancelVideoFrameCallback(i),!d())return;let o=!1;const a=t.requestVideoFrameCallback(e.guard(()=>{d()&&r===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,l())}));d()&&r===u.sequence&&!o?u.frame=a:t.cancelVideoFrameCallback(a)})(),d()&&(t.currentTime=o)},s=e.guard(()=>{d()&&!u.started&&(u.started=!0,t.onloadeddata=null,t.onseeked=e.guard(()=>{if(!d()||t.seeking)return;const e=t.currentTime;if(d())if(!Number.isFinite(e)||Math.abs(e-o)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,l()}),c())});if(u.timer=setTimeout(e.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!n||t.readyState>=2;d()&&(f?s():t.onloadeddata=s)}}function d(e,t){const n=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(n.onerror=e.guard(()=>{throw n.error||new Error("Auto-thumbnail media failed to load")}),n.onloadedmetadata=e.guard(()=>{n.onloadedmetadata=null;const r=n.duration,i=n.videoHeight,o=n.videoWidth,{height:a,canvasWidth:d,canvasHeight:l}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),r=Math.trunc(10*e.width),i=Math.trunc(n*Math.ceil(e.number/10));if(![n,r,i].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:r,canvasHeight:i}}(t,{duration:r,videoHeight:i,videoWidth:o});if(!e.active())return;n.width=o,n.height=i,n.style.width=`${o}px`,n.style.height=`${i}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=l;const s=c.getContext("2d");if(!s)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=u(e,n);let h=0;const m=e.guard(()=>{h>=t.number?e.dispose():f(r*h/t.number,e.guard(()=>{if(s.drawImage(n,h%10*t.width,Math.floor(h/10)*a,t.width,a),!e.active())return;let r=!1;c.toBlob(e.guard(n=>{r||(r=!0,e.publish(n,{height:a,column:10,number:t.number,width:t.width,scale:t.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(n.src=t.url))}return function(e){return t=>{return n=null,r=null,i=function*(){const n=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),r=a(e=>{t.thumbnails=e},n),i=[],u=()=>{const n=r.start();n&&n.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(e,()=>t.option.url);n.active()&&d(n,r)})()},l=()=>{const e=i.splice(0),t=o([r.destroy,...e]);t.failed&&n(t.failure)};try{for(const[e,n]of[["destroy",l],["restart",r.cancel],["video:loadedmetadata",u]]){if(r.closed)break;i.push(()=>t.off(e,n)),t.on(e,n),r.closed&&t.off(e,n)}}catch(c){throw l(),c}return{name:"artplayerPluginAutoThumbnail"}},new Promise((e,t)=>{var o=e=>{try{u(i.next(e))}catch(n){t(n)}},a=e=>{try{u(i.throw(e))}catch(n){t(n)}},u=t=>t.done?e(t.value):Promise.resolve(t.value).then(o,a);u((i=i.apply(n,r)).next())});var n,r,i}}}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginAutoThumbnail=t(),define(function(){return e.artplayerPluginAutoThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAutoThumbnail=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,i=(t,n,r)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[n]=r;function o(e){let t,n=!1;for(const i of e)try{i()}catch(r){n||(n=!0,t=r)}return{failed:n,failure:t}}function a(e,a){let u,d,l=!1;const c=new Set;function s(e){void 0!==e&&c.delete(e)&&URL.revokeObjectURL(e)}function f(){const e=u;u=void 0,null==e||e.dispose()}return{start:function(){if(l)return;const f=u,h=[];let m=!1;const v={active:()=>!l&&!m&&u===v,own(e){if(m){const t=o([e]);t.failed&&a(t.failure)}else h.push(e)},dispose(){if(m)return;m=!0,u===v&&(u=void 0);const e=o(h.splice(0).reverse());e.failed&&a(e.failure)},fail(e){v.active()&&(v.dispose(),a(e))},guard:e=>(...t)=>{if(v.active())try{return e(...t)}catch(n){v.fail(n)}},publish(u,l){if(!v.active())return;if(!u)throw new Error("Auto-thumbnail encoding returned no Blob");const f=URL.createObjectURL(u);if(c.add(f),!v.active())return void s(f);const h=d;d=f;try{e(((e,o)=>{for(var a in o||(o={}))n.call(o,a)&&i(e,a,o[a]);if(t)for(var a of t(o))r.call(o,a)&&i(e,a,o[a]);return e})({url:f},l))}catch(m){d===f&&(d=void 0!==h&&c.has(h)?h:void 0);const e=o([()=>s(f)]);throw e.failed&&a(e.failure),m}s(h)}};return u=v,null==f||f.dispose(),v},cancel:f,destroy:function(){if(l)return;l=!0;const e=o([f,...[...c].map(e=>()=>s(e))]);d=void 0,e.failed&&a(e.failure)},get closed(){return l}}}function u(e,t){const n="function"===typeof t.requestVideoFrameCallback&&"function"===typeof t.cancelVideoFrameCallback;let r;function i(){const e=r;if(r=void 0,t.onloadeddata=null,t.onseeked=null,!e)return;const n=o([()=>{null!==e.timer&&clearTimeout(e.timer)},()=>{null!==e.frame&&t.cancelVideoFrameCallback(e.frame)}]);if(n.failed)throw n.failure}return e.own(i),(o,a)=>{if(i(),!e.active())return;const u={frame:null,timer:null,sought:!1,presented:!n,retries:0,sequence:0,started:!1};r=u;const d=()=>e.active()&&r===u,l=e.guard(()=>{d()&&u.sought&&u.presented&&(i(),e.active()&&a())}),c=()=>{u.sought=!1,(()=>{if(!n)return;const r=++u.sequence;u.presented=!1;const i=u.frame;if(u.frame=null,null!==i&&t.cancelVideoFrameCallback(i),!d())return;let o=!1;const a=t.requestVideoFrameCallback(e.guard(()=>{d()&&r===u.sequence&&!o&&(o=!0,u.frame=null,u.presented=!0,l())}));d()&&r===u.sequence&&!o?u.frame=a:t.cancelVideoFrameCallback(a)})(),d()&&(t.currentTime=o)},s=e.guard(()=>{d()&&!u.started&&(u.started=!0,t.onloadeddata=null,t.onseeked=e.guard(()=>{if(!d()||t.seeking)return;const e=t.currentTime;if(d())if(!Number.isFinite(e)||Math.abs(e-o)>.05){if(++u.retries>3)throw new Error("Auto-thumbnail seek did not reach the requested time");c()}else u.sought=!0,l()}),c())});if(u.timer=setTimeout(e.guard(()=>{if(d())throw new Error("Auto-thumbnail frame readiness timed out")}),3e4),!d())return void clearTimeout(u.timer);const f=!n||t.readyState>=2;d()&&(f?s():t.onloadeddata=s)}}function d(e,t){const n=function(e){const t=document.createElement("video");e.own(()=>t.remove()),e.own(()=>t.load()),e.own(()=>t.removeAttribute("src")),e.own(()=>t.pause());for(const n of["onloadedmetadata","onloadeddata","onseeked","onerror"])e.own(()=>{t[n]=null});return e.active()?(t.crossOrigin="anonymous",t.muted=!0,t.playsInline=!0,t.tabIndex=-1,t.setAttribute("aria-hidden","true"),t.style.cssText="position:fixed;left:0;top:0;visibility:hidden;pointer-events:none;display:block;width:auto;height:auto;max-width:none;max-height:none",document.documentElement.appendChild(t),e.active()||t.remove(),t):t}(e);e.active()&&(n.onerror=e.guard(()=>{throw n.error||new Error("Auto-thumbnail media failed to load")}),n.onloadedmetadata=e.guard(()=>{n.onloadedmetadata=null;const r=n.duration,i=n.videoHeight,o=n.videoWidth,{height:a,canvasWidth:d,canvasHeight:l}=function(e,t){if(!Number.isFinite(t.duration)||t.duration<=0||!Number.isFinite(t.videoWidth)||t.videoWidth<=0||!Number.isFinite(t.videoHeight)||t.videoHeight<=0)throw new TypeError("Auto-thumbnail requires finite media duration and dimensions");const n=Math.floor(e.width*t.videoHeight/t.videoWidth),r=Math.trunc(10*e.width),i=Math.trunc(n*Math.ceil(e.number/10));if(![n,r,i].every(e=>Number.isSafeInteger(e)&&e>0&&e<=4294967295))throw new RangeError("Auto-thumbnail canvas dimensions are invalid");return{height:n,canvasWidth:r,canvasHeight:i}}(t,{duration:r,videoHeight:i,videoWidth:o});if(!e.active())return;n.width=o,n.height=i,n.style.width=`${o}px`,n.style.height=`${i}px`;const c=document.createElement("canvas");if(!e.active())return;c.width=d,c.height=l;const s=c.getContext("2d");if(!s)throw new Error("Auto-thumbnail canvas context is unavailable");if(!e.active())return;const f=u(e,n);let h=0;const m=e.guard(()=>{h>=t.number?e.dispose():f(r*h/t.number,e.guard(()=>{if(s.drawImage(n,h%10*t.width,Math.floor(h/10)*a,t.width,a),!e.active())return;let r=!1;c.toBlob(e.guard(n=>{r||(r=!0,e.publish(n,{height:a,column:10,number:t.number,width:t.width,scale:t.scale}),e.active()&&(h+=1,m()))}),"image/jpeg")}))});m()}),e.active()&&(n.src=t.url))}return function(e){return t=>{return n=null,r=null,i=function*(){const n=e=>console.warn("ArtPlayer auto-thumbnail failed:",e),r=a(e=>{t.thumbnails=e},n),i=[],u=()=>{const n=r.start();n&&n.guard(()=>{const r=function(e,t){const n={url:e.url||t(),width:e.width||160,number:e.number||100,scale:e.scale||1};if(!Number.isFinite(Number(n.width))||n.width<=0||!Number.isFinite(Number(n.number))||n.number<=0)throw new TypeError("Auto-thumbnail width and number must be finite positive numbers");return n}(e,()=>t.option.url);n.active()&&d(n,r)})()},l=()=>{const e=i.splice(0),t=o([r.destroy,...e]);t.failed&&n(t.failure)};try{const e=[["destroy",l],["restart",r.cancel],["video:loadedmetadata",u]];for(const[n,o]of e){if(r.closed)break;i.push(()=>t.off(n,o)),t.on(n,o),r.closed&&t.off(n,o)}}catch(c){throw l(),c}return{name:"artplayerPluginAutoThumbnail"}},new Promise((e,t)=>{var o=e=>{try{u(i.next(e))}catch(n){t(n)}},a=e=>{try{u(i.throw(e))}catch(n){t(n)}},u=t=>t.done?e(t.value):Promise.resolve(t.value).then(o,a);u((i=i.apply(n,r)).next())});var n,r,i}}}); diff --git a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs index 77f24658f..948ba1870 100644 --- a/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs +++ b/packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs @@ -25,7 +25,7 @@ function createSession(publish, report) { let lastUrl; const urls = /* @__PURE__ */ new Set(); function release(url) { - if (urls.delete(url)) + if (url !== void 0 && urls.delete(url)) URL.revokeObjectURL(url); } function cancel() { @@ -103,7 +103,7 @@ function createSession(publish, report) { publish({ url, ...config }); } catch (error) { if (lastUrl === url) - lastUrl = urls.has(previousUrl) ? previousUrl : void 0; + lastUrl = previousUrl !== void 0 && urls.has(previousUrl) ? previousUrl : void 0; const result = cleanupAll([() => release(url)]); if (result.failed) report(result.failure); @@ -136,7 +136,10 @@ function createFrameReader(job, video) { if (!previous) return; const result = cleanupAll([ - () => clearTimeout(previous.timer), + () => { + if (previous.timer !== null) + clearTimeout(previous.timer); + }, () => { if (previous.frame !== null) video.cancelVideoFrameCallback(previous.frame); @@ -360,7 +363,8 @@ function artplayerPluginAutoThumbnail(option) { report(result.failure); }; try { - for (const [name, callback] of [["destroy", onDestroy], ["restart", session.cancel], ["video:loadedmetadata", onMetadata]]) { + const events = [["destroy", onDestroy], ["restart", session.cancel], ["video:loadedmetadata", onMetadata]]; + for (const [name, callback] of events) { if (session.closed) break; subscriptions.push(() => art.off(name, callback)); diff --git a/packages/artplayer-plugin-auto-thumbnail/src/extraction.js b/packages/artplayer-plugin-auto-thumbnail/src/extraction.ts similarity index 93% rename from packages/artplayer-plugin-auto-thumbnail/src/extraction.js rename to packages/artplayer-plugin-auto-thumbnail/src/extraction.ts index ee921dcb6..f837cbf6b 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/extraction.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/extraction.ts @@ -1,8 +1,9 @@ +import type { ExtractionConfig, ExtractionJob } from './types' import createFrameReader from './frames' import { sheetSize } from './options' import createVideo from './video' -export default function extract(job, config) { +export default function extract(job: ExtractionJob, config: ExtractionConfig) { const video = createVideo(job) if (!job.active()) return diff --git a/packages/artplayer-plugin-auto-thumbnail/src/frames.js b/packages/artplayer-plugin-auto-thumbnail/src/frames.ts similarity index 81% rename from packages/artplayer-plugin-auto-thumbnail/src/frames.js rename to packages/artplayer-plugin-auto-thumbnail/src/frames.ts index 65990816a..5e072bfc3 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/frames.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/frames.ts @@ -1,9 +1,20 @@ +import type { ExtractionJob } from './types' import { cleanupAll } from './session' -export default function createFrameReader(job, video) { +interface PendingFrame { + frame: number | null + timer: ReturnType | null + sought: boolean + presented: boolean + retries: number + sequence: number + started: boolean +} + +export default function createFrameReader(job: ExtractionJob, video: HTMLVideoElement) { const presentedFrames = typeof video.requestVideoFrameCallback === 'function' && typeof video.cancelVideoFrameCallback === 'function' - let pending + let pending: PendingFrame | undefined function clear() { const previous = pending @@ -13,7 +24,10 @@ export default function createFrameReader(job, video) { if (!previous) return const result = cleanupAll([ - () => clearTimeout(previous.timer), + () => { + if (previous.timer !== null) + clearTimeout(previous.timer) + }, () => { if (previous.frame !== null) video.cancelVideoFrameCallback(previous.frame) @@ -24,11 +38,11 @@ export default function createFrameReader(job, video) { } job.own(clear) - return (target, draw) => { + return (target: number, draw: () => void) => { clear() if (!job.active()) return - const current = { frame: null, timer: null, sought: false, presented: !presentedFrames, retries: 0, sequence: 0, started: false } + const current: PendingFrame = { frame: null, timer: null, sought: false, presented: !presentedFrames, retries: 0, sequence: 0, started: false } pending = current const active = () => job.active() && pending === current const finish = job.guard(() => { diff --git a/packages/artplayer-plugin-auto-thumbnail/src/index.js b/packages/artplayer-plugin-auto-thumbnail/src/index.ts similarity index 66% rename from packages/artplayer-plugin-auto-thumbnail/src/index.js rename to packages/artplayer-plugin-auto-thumbnail/src/index.ts index 35c9f1725..234db8d38 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/index.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/index.ts @@ -1,14 +1,15 @@ +import type { Cleanup, ThumbnailHost, ThumbnailOptions } from './types' import extract from './extraction' import { readOptions } from './options' import createSession, { cleanupAll } from './session' -export default function artplayerPluginAutoThumbnail(option) { - return async (art) => { - const report = error => console.warn('ArtPlayer auto-thumbnail failed:', error) +export default function artplayerPluginAutoThumbnail(option: ThumbnailOptions) { + return async (art: ThumbnailHost) => { + const report = (error: unknown) => console.warn('ArtPlayer auto-thumbnail failed:', error) const session = createSession((config) => { art.thumbnails = config }, report) - const subscriptions = [] + const subscriptions: Cleanup[] = [] const onMetadata = () => { const job = session.start() if (job) { @@ -26,7 +27,8 @@ export default function artplayerPluginAutoThumbnail(option) { report(result.failure) } try { - for (const [name, callback] of [['destroy', onDestroy], ['restart', session.cancel], ['video:loadedmetadata', onMetadata]]) { + const events: [string, Cleanup][] = [['destroy', onDestroy], ['restart', session.cancel], ['video:loadedmetadata', onMetadata]] + for (const [name, callback] of events) { if (session.closed) break subscriptions.push(() => art.off(name, callback)) diff --git a/packages/artplayer-plugin-auto-thumbnail/src/options.js b/packages/artplayer-plugin-auto-thumbnail/src/options.ts similarity index 79% rename from packages/artplayer-plugin-auto-thumbnail/src/options.js rename to packages/artplayer-plugin-auto-thumbnail/src/options.ts index 6006ab83e..b619dcfda 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/options.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/options.ts @@ -1,4 +1,6 @@ -export function readOptions(option, getFallbackUrl) { +import type { ExtractionConfig, ThumbnailOptions } from './types' + +export function readOptions(option: ThumbnailOptions, getFallbackUrl: () => string): ExtractionConfig { const config = { url: option.url || getFallbackUrl(), width: option.width || 160, @@ -10,7 +12,7 @@ export function readOptions(option, getFallbackUrl) { return config } -export function sheetSize(config, video) { +export function sheetSize(config: ExtractionConfig, video: Pick) { if (!Number.isFinite(video.duration) || video.duration <= 0 || !Number.isFinite(video.videoWidth) || video.videoWidth <= 0 || !Number.isFinite(video.videoHeight) || video.videoHeight <= 0) throw new TypeError('Auto-thumbnail requires finite media duration and dimensions') const height = Math.floor(config.width * video.videoHeight / video.videoWidth) diff --git a/packages/artplayer-plugin-auto-thumbnail/src/session.js b/packages/artplayer-plugin-auto-thumbnail/src/session.ts similarity index 80% rename from packages/artplayer-plugin-auto-thumbnail/src/session.js rename to packages/artplayer-plugin-auto-thumbnail/src/session.ts index 07368ba6b..c2320c908 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/session.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/session.ts @@ -1,5 +1,7 @@ -export function cleanupAll(actions) { - let failure +import type { Cleanup, ExtractionJob, ThumbnailSheet } from './types' + +export function cleanupAll(actions: Cleanup[]) { + let failure: unknown let failed = false for (const action of actions) { try { @@ -15,14 +17,14 @@ export function cleanupAll(actions) { return { failed, failure } } -export default function createSession(publish, report) { +export default function createSession(publish: (sheet: ThumbnailSheet) => void, report: (error: unknown) => void) { let closed = false - let current - let lastUrl - const urls = new Set() + let current: ExtractionJob | undefined + let lastUrl: string | undefined + const urls = new Set() - function release(url) { - if (urls.delete(url)) + function release(url: string | undefined) { + if (url !== undefined && urls.delete(url)) URL.revokeObjectURL(url) } @@ -47,9 +49,9 @@ export default function createSession(publish, report) { return // Install the replacement before cleanup, which can reenter through host APIs. const previous = current - const actions = [] + const actions: Cleanup[] = [] let disposed = false - const job = { + const job: ExtractionJob = { active: () => !closed && !disposed && current === job, own(action) { if (disposed) { @@ -107,7 +109,7 @@ export default function createSession(publish, report) { } catch (error) { if (lastUrl === url) - lastUrl = urls.has(previousUrl) ? previousUrl : undefined + lastUrl = previousUrl !== undefined && urls.has(previousUrl) ? previousUrl : undefined const result = cleanupAll([() => release(url)]) if (result.failed) report(result.failure) diff --git a/packages/artplayer-plugin-auto-thumbnail/src/types.ts b/packages/artplayer-plugin-auto-thumbnail/src/types.ts new file mode 100644 index 000000000..d5154673a --- /dev/null +++ b/packages/artplayer-plugin-auto-thumbnail/src/types.ts @@ -0,0 +1,43 @@ +// Internal contracts only. Public declarations retain their historical entrypoint. +export interface ThumbnailOptions { + url?: string + width?: number + number?: number + scale?: number + height?: number +} + +export interface ExtractionConfig { + url: string + width: number + number: number + scale: number +} + +export interface SheetConfig { + height: number + column: number + number: number + width: number + scale: number +} + +export interface ThumbnailSheet extends SheetConfig { url: string } +export type Cleanup = () => void + +export interface ExtractionJob { + active: () => boolean + own: (action: Cleanup) => void + dispose: Cleanup + fail: (error: unknown) => void + guard: (callback: (...args: Args) => Result) => (...args: Args) => Result | undefined + publish: (blob: Blob | null, config: SheetConfig) => void +} + +export interface ThumbnailHost { + option: { url: string } + // A host may already hold a partial user configuration; generated sheets are complete. + thumbnails: { url: string } & Partial + on: (event: string, callback: Cleanup) => unknown + off: (event: string, callback: Cleanup) => unknown +} diff --git a/packages/artplayer-plugin-auto-thumbnail/src/video.js b/packages/artplayer-plugin-auto-thumbnail/src/video.ts similarity index 85% rename from packages/artplayer-plugin-auto-thumbnail/src/video.js rename to packages/artplayer-plugin-auto-thumbnail/src/video.ts index 33f445359..5b49cbaae 100644 --- a/packages/artplayer-plugin-auto-thumbnail/src/video.js +++ b/packages/artplayer-plugin-auto-thumbnail/src/video.ts @@ -1,10 +1,12 @@ -export default function createVideo(job) { +import type { ExtractionJob } from './types' + +export default function createVideo(job: ExtractionJob) { const video = document.createElement('video') job.own(() => video.remove()) job.own(() => video.load()) job.own(() => video.removeAttribute('src')) job.own(() => video.pause()) - for (const property of ['onloadedmetadata', 'onloadeddata', 'onseeked', 'onerror']) + for (const property of ['onloadedmetadata', 'onloadeddata', 'onseeked', 'onerror'] as const) job.own(() => { video[property] = null }) if (!job.active()) return video diff --git a/packages/artplayer-plugin-auto-thumbnail/tsconfig.json b/packages/artplayer-plugin-auto-thumbnail/tsconfig.json new file mode 100644 index 000000000..04fef559a --- /dev/null +++ b/packages/artplayer-plugin-auto-thumbnail/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "allowJs": false }, + "include": ["src/**/*.ts", "../../refactor/fixtures/implementation/auto-thumbnail.ts"] +} diff --git a/refactor/baselines/auto-thumbnail-internal-types.json b/refactor/baselines/auto-thumbnail-internal-types.json new file mode 100644 index 000000000..6bd1f8331 --- /dev/null +++ b/refactor/baselines/auto-thumbnail-internal-types.json @@ -0,0 +1,3770 @@ +{ + "schemaVersion": 1, + "task": "PKG-AUTO-THUMB-03", + "status": "checkpoint-task-remains-doing", + "capturedAt": "2026-09-13T15:32:25.629Z", + "baselineCommit": "00e81309ca4799e5819a6b112f1a9e5e09289226", + "implementation": "Six runtime modules migrated to strict TypeScript; one internal type module, package config and real Artplayer host assignment fixture. No public declaration or dependency/version changes.", + "units": [ + { + "file": "refactor/.cache/auto-thumbnail-internal-types-unit.log", + "sha256": "a85773d11dcfe5b4ec5058caaf3fc85c254d604beea38413affb72bd2ed69823", + "tests": 154 + }, + { + "file": "refactor/.cache/auto-thumbnail-internal-types-main-unit.log", + "sha256": "60d67e2821cb04c24b2b6b7dcf8b18988aad76dd664ec6c30a0ca6fb0a8dbf18", + "tests": 52 + }, + { + "file": "refactor/.cache/auto-thumbnail-internal-types-legacy-unit.log", + "sha256": "ec16e7767601fd65b65bd500518d68763df4ded430f381d89668b4d6fda88ee2", + "tests": 52 + } + ], + "browsers": [ + { + "format": "source", + "file": "refactor/.cache/auto-thumbnail-internal-types-source-browser.json", + "sha256": "40b2096e8ed59c10752d6608b12a902c5a0932a580dcb4d3f9d3c455fc5af28e", + "stats": { + "startTime": "2026-09-13T15:23:50.380Z", + "duration": 20317.548, + "expected": 21, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "versions": { + "chromium": { + "browser": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "browser": "155.0", + "platform": "win32" + }, + "webkit": { + "browser": "26.6", + "platform": "win32" + } + }, + "cases": [ + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 374, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 375, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 272, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 168, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 270, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1674, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1704, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 519, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 493, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 531, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1206, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1272, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1202, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1056, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1076, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 285, + "annotations": [], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 126, + 0, + 126, + 255 + ], + [ + 254, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 1, + 252, + 255 + ], + [ + 255, + 255, + 1, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 127, + 0, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 254, + 0, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 255, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 296, + "annotations": [], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 15, + 13, + 138, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 14, + 15, + 141, + 255 + ], + [ + 213, + 61, + 47, + 255 + ] + ], + "spatial": [ + [ + 244, + 1, + 0, + 255 + ], + [ + 5, + 251, + 2, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 255, + 0, + 253, + 255 + ], + [ + 2, + 255, + 253, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 253, + 52, + 0, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.583333, + "currentTime": 1.6 + }, + { + "mediaTime": 3.166667, + "currentTime": 3.2 + }, + { + "mediaTime": 4.791667, + "currentTime": 4.8 + }, + { + "mediaTime": 6.375, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 508, + "annotations": [], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 136, + 19, + 126, + 255 + ], + [ + 254, + 24, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 254, + 241, + 2, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 137, + 19, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 24, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 241, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 784, + "annotations": [], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 5, + 15, + 250, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 247, + 64, + 0, + 255 + ] + ], + "spatial": [ + [ + 255, + 22, + 9, + 255 + ], + [ + 0, + 216, + 3, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 255, + 36, + 254, + 255 + ], + [ + 1, + 229, + 254, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 69, + 1, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 1283, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 238, + 14, + 14, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 15, + 239, + 255 + ], + [ + 235, + 236, + 12, + 255 + ] + ], + "spatial": [ + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ] + ], + "draws": [ + { + "time": 0.0022181, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6020552, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 238, + 14, + 14, + 255 + ] + }, + { + "time": 3.2025943, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 16, + 16, + 255 + ] + }, + { + "time": 4.8025587, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4017965, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 235, + 236, + 12, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 6441, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "8e587103b60c1da0d811cf8e6cbfc0c37ed65af7d2c5d609d773b5f055f03289", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 217, + 65, + 51, + 255 + ] + ], + "spatial": [ + [ + 238, + 14, + 15, + 255 + ], + [ + 14, + 238, + 14, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 238, + 13, + 237, + 255 + ], + [ + 14, + 237, + 237, + 255 + ] + ], + "draws": [ + { + "time": 0.0011895, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6016229, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 3.2012411, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 4.8014734, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4061077, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 239, + 62, + 14, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + } + ] + }, + { + "format": "main", + "file": "refactor/.cache/auto-thumbnail-internal-types-main-browser.json", + "sha256": "94d6551d99c665ac75b012cff2b12ab3760ec0617dac68546ce879d9bedb1ebf", + "stats": { + "startTime": "2026-09-13T15:25:27.022Z", + "duration": 19642.729, + "expected": 21, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "versions": { + "chromium": { + "browser": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "browser": "155.0", + "platform": "win32" + }, + "webkit": { + "browser": "26.6", + "platform": "win32" + } + }, + "cases": [ + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 375, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 400, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 269, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 274, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 263, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1636, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1673, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 509, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 515, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 477, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1224, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1191, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1303, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1102, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1046, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 332, + "annotations": [], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 126, + 0, + 126, + 255 + ], + [ + 254, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 1, + 252, + 255 + ], + [ + 255, + 255, + 1, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 127, + 0, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 254, + 0, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 255, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 299, + "annotations": [], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 15, + 13, + 138, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 14, + 15, + 141, + 255 + ], + [ + 213, + 61, + 47, + 255 + ] + ], + "spatial": [ + [ + 244, + 1, + 0, + 255 + ], + [ + 5, + 251, + 2, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 255, + 0, + 253, + 255 + ], + [ + 2, + 255, + 253, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 253, + 52, + 0, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.583333, + "currentTime": 1.6 + }, + { + "mediaTime": 3.166667, + "currentTime": 3.2 + }, + { + "mediaTime": 4.791667, + "currentTime": 4.8 + }, + { + "mediaTime": 6.375, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 504, + "annotations": [], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 136, + 19, + 126, + 255 + ], + [ + 254, + 24, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 254, + 241, + 2, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 137, + 19, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 24, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 241, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 761, + "annotations": [], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 5, + 15, + 250, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 247, + 64, + 0, + 255 + ] + ], + "spatial": [ + [ + 255, + 22, + 9, + 255 + ], + [ + 0, + 216, + 3, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 255, + 36, + 254, + 255 + ], + [ + 1, + 229, + 254, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 69, + 1, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 1373, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 238, + 14, + 14, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 15, + 239, + 255 + ], + [ + 235, + 236, + 12, + 255 + ] + ], + "spatial": [ + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ] + ], + "draws": [ + { + "time": 0.0012328, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6021034, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 238, + 14, + 14, + 255 + ] + }, + { + "time": 3.2010096, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 16, + 16, + 255 + ] + }, + { + "time": 4.8020184, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4019061, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 235, + 236, + 12, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 6454, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 217, + 65, + 51, + 255 + ] + ], + "spatial": [ + [ + 238, + 14, + 15, + 255 + ], + [ + 14, + 238, + 14, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 238, + 13, + 237, + 255 + ], + [ + 14, + 237, + 237, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6020625, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 3.2018047, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 4.8012267, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4059399, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 239, + 62, + 14, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + } + ] + }, + { + "format": "legacy", + "file": "refactor/.cache/auto-thumbnail-internal-types-legacy-browser.json", + "sha256": "856b5db07e045ced0a462afe57523e8fe986f6e84592910459eb577605efcb32", + "stats": { + "startTime": "2026-09-13T15:25:47.406Z", + "duration": 19308.398, + "expected": 21, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "versions": { + "chromium": { + "browser": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "browser": "155.0", + "platform": "win32" + }, + "webkit": { + "browser": "26.6", + "platform": "win32" + } + }, + "cases": [ + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 364, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 372, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 260, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 168, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 156, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1565, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1697, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 515, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 479, + "annotations": [] + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 519, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1174, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1188, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native complete owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1214, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-destroy owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1078, + "annotations": [] + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate native frame-restart owns decoder and encoded URLs", + "status": "passed", + "durationMs": 1093, + "annotations": [] + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 283, + "annotations": [], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 126, + 0, + 126, + 255 + ], + [ + 254, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 1, + 252, + 255 + ], + [ + 255, + 255, + 1, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 127, + 0, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 254, + 0, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 255, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "chromium", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 282, + "annotations": [], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 15, + 13, + 138, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 14, + 15, + 141, + 255 + ], + [ + 213, + 61, + 47, + 255 + ] + ], + "spatial": [ + [ + 244, + 1, + 0, + 255 + ], + [ + 5, + 251, + 2, + 255 + ], + [ + 15, + 15, + 139, + 255 + ], + [ + 255, + 0, + 253, + 255 + ], + [ + 2, + 255, + 253, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 253, + 52, + 0, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.583333, + "currentTime": 1.6 + }, + { + "mediaTime": 3.166667, + "currentTime": 3.2 + }, + { + "mediaTime": 4.791667, + "currentTime": 4.8 + }, + { + "mediaTime": 6.375, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 509, + "annotations": [], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 136, + 19, + 126, + 255 + ], + [ + 254, + 24, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 254, + 241, + 2, + 255 + ] + ], + "spatial": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ], + [ + 0, + 0, + 0, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 137, + 19, + 126, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 24, + 0, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 241, + 2, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "firefox", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 758, + "annotations": [], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 5, + 15, + 250, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 247, + 64, + 0, + 255 + ] + ], + "spatial": [ + [ + 255, + 22, + 9, + 255 + ], + [ + 0, + 216, + 3, + 255 + ], + [ + 1, + 14, + 253, + 255 + ], + [ + 255, + 36, + 254, + 255 + ], + [ + 1, + 229, + 254, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 1.6, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 3.2, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 4.8, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 14, + 253, + 255 + ] + }, + { + "time": 6.4, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 255, + 69, + 1, + 255 + ] + } + ], + "presented": [ + { + "mediaTime": 0, + "currentTime": 0 + }, + { + "mediaTime": 1.6, + "currentTime": 1.6 + }, + { + "mediaTime": 3.2, + "currentTime": 3.2 + }, + { + "mediaTime": 4.8, + "currentTime": 4.8 + }, + { + "mediaTime": 6.4, + "currentTime": 6.4 + } + ], + "supportsPresentation": true, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented auto-thumbnail-timeline pixels and records fallback limits", + "status": "passed", + "durationMs": 1334, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "auto-thumbnail-timeline", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 238, + 14, + 14, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 15, + 239, + 255 + ], + [ + 235, + 236, + 12, + 255 + ] + ], + "spatial": [ + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ], + [ + 16, + 16, + 16, + 255 + ] + ], + "draws": [ + { + "time": 0, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6019401, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 238, + 14, + 14, + 255 + ] + }, + { + "time": 3.2015531, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 16, + 16, + 255 + ] + }, + { + "time": 4.8013107, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4024188, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 235, + 236, + 12, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + }, + { + "engine": "webkit", + "title": "Auto-thumbnail candidate verifies presented pattern pixels and records fallback limits", + "status": "passed", + "durationMs": 6445, + "annotations": [ + { + "type": "remaining-initial-frame-risk", + "description": "webkit: no native presentation callback; cells 0-1 remain evidence only and cannot close AUTO-THUMB-PIXEL-01 or PKG-AUTO-THUMB-03." + } + ], + "pixels": { + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616", + "media": "pattern", + "scope": "All five cells are asserted where native frame presentation callbacks are available. Without that capability only cells 2-4 are asserted, with cells 0-1 recorded as unresolved diagnostics under AUTO-THUMB-PIXEL-01. Native HTTP/JPEG and a stub player host, not a core integration.", + "cells": [ + [ + 0, + 0, + 0, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 201, + 71, + 57, + 255 + ] + ], + "spatial": [ + [ + 238, + 14, + 15, + 255 + ], + [ + 14, + 238, + 14, + 255 + ], + [ + 28, + 28, + 140, + 255 + ], + [ + 238, + 13, + 237, + 255 + ], + [ + 14, + 237, + 237, + 255 + ] + ], + "draws": [ + { + "time": 0.0012302, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 0, + 0, + 0, + 0 + ] + }, + { + "time": 1.6009068, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 3.2017037, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 4.8009416, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 16, + 15, + 239, + 255 + ] + }, + { + "time": 6.4020228, + "duration": 8, + "connected": true, + "visibility": "hidden", + "display": "block", + "width": 320, + "height": 180, + "raw": [ + 238, + 61, + 10, + 255 + ] + } + ], + "presented": [], + "supportsPresentation": false, + "final": [ + { + "connected": false, + "src": null, + "paused": true + } + ], + "playing": 0, + "warnings": [] + } + } + ] + } + ], + "outputs": [ + { + "build": { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js", + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a" + }, + "docs": { + "file": "docs/compiled/artplayer-plugin-auto-thumbnail.js", + "sha256": "806d3c4601099bc67210c500f6b556072a90c7de0269d45821cb19d5e3b0c69a" + } + }, + { + "build": { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js", + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616" + }, + "docs": { + "file": "docs/compiled/artplayer-plugin-auto-thumbnail.legacy.js", + "sha256": "d5f5def04fbb4378d2560324b54e7ada222ade395ad1f968adf0cd0af1192616" + } + }, + { + "build": { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs", + "sha256": "f5a11607816f8db540ea78b3ddb97ba5731392bae47ec7683a9ff1cb37f9ddec" + }, + "docs": { + "file": "docs/compiled/artplayer-plugin-auto-thumbnail.mjs", + "sha256": "f5a11607816f8db540ea78b3ddb97ba5731392bae47ec7683a9ff1cb37f9ddec" + } + } + ], + "source": [ + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/extraction.ts", + "sha256": "e1a994647b8c37ffe71d8cb8ebb2a2b1728a0a2cd6d6690f18399658ba0666ef" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/frames.ts", + "sha256": "0c1ce944dfe0684feba51987ccb78a3de7a9c784f17d16993634916e0eb1db31" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/index.ts", + "sha256": "59324c12a302092d7e4a081b78936f0e760731994c1adb7ed94fd27d87282961" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/options.ts", + "sha256": "8481d30b912fca90dee72224fc87e1c6c295b03f1f01fd675643d4364ae0e4ae" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/session.ts", + "sha256": "c831c720f99d42374905e443526a336e065b041f3421e17875b42cddf1fd1659" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/types.ts", + "sha256": "57d34100fef8bf93ec39df8c90759ae977600d28cc46fd8e4fc4ef3530520992" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/src/video.ts", + "sha256": "d675a45d3baddfca8d7d469050b373e3b7a66ea3f9746768469d61b408b59426" + } + ], + "sourceUnitScope": "52 candidate lifecycle/frame tests plus 102 fixed historical contract/failure checks; historical defect assertions are not candidate acceptance.", + "checks": [ + { + "file": "refactor/.cache/auto-thumbnail-internal-types-check.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/auto-thumbnail-internal-types-all-types.log", + "sha256": "19ec2de05e7a446a3240fd61d80b4e0954448a8b2ed3971a49def4a3e7ec51a5" + }, + { + "file": "refactor/.cache/auto-thumbnail-internal-types-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/auto-thumbnail-internal-types-build.log", + "sha256": "4b9fdaf765abcf7ba88221786456c5b8d422f3b898b8ae11dc66c7e80894e752" + } + ], + "typeScope": "Repository check passed 398 production TS files and compiler consumer modes. The subsequently added final core-host fixture and corrected partial host configuration were separately checked with the package strict config. Public plugin declaration migration remains task04.", + "fixture": { + "file": "refactor/fixtures/implementation/auto-thumbnail.ts", + "sha256": "392159869f45b32a0346fba7c7bb50cbbcf71137d6b7a657f16b7109b8e4b7c4" + }, + "typeFinding": "Initial source host required all existing thumbnails fields; actual Artplayer allows optional height, producing TS2345. Host view now permits partial SheetConfig while produced ThumbnailSheet and publish remain complete.", + "limitations": [ + "AUTO-THUMB-PIXEL-01 stays open: without native frame callbacks, first two cells remain diagnostic, not pixel acceptance.", + "Remaining task03 resource and fallback behavior work", + "Task04 public types and distribution declarations", + "Final core/device/package/release acceptance" + ], + "packaging": { + "archive": "refactor/.cache/auto-thumbnail-internal-types.tgz", + "sha256": "a1d4c53b0865b9edee64a29ee021565b4974f5b65d2bdded5754db51ed90280d", + "files": [ + "package/ARCHITECTURE.md", + "package/README.md", + "package/dist/artplayer-plugin-auto-thumbnail.js", + "package/dist/artplayer-plugin-auto-thumbnail.legacy.js", + "package/dist/artplayer-plugin-auto-thumbnail.mjs", + "package/package.json", + "package/types/artplayer-plugin-auto-thumbnail.d.ts" + ], + "scope": "Yarn 1.22.22 actual archive file and entrypoint check; no isolated install or full distribution acceptance claimed" + } +} diff --git a/refactor/changes/2026-09-13-PKG-AUTO-THUMB-03-internal-types.md b/refactor/changes/2026-09-13-PKG-AUTO-THUMB-03-internal-types.md new file mode 100644 index 000000000..a45b3a6cf --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-AUTO-THUMB-03-internal-types.md @@ -0,0 +1,42 @@ +# PKG-AUTO-THUMB-03 内部严格TS检查点 + +本检查点推进内部代码维护,03仍doing;公开声明和类型分发仍属04。没有降低 +首帧像素要求,也没有将WebKit的早期帧诊断计入完整像素验收。 + +## 改动 + +原有六个职责模块index/options/session/extraction/video/frames迁为TS,新增types.ts +集中描述任务所有权、guard参数/返回、完整图集配置和最小宿主。新增分包严格配置, +不启用allowJs/skipLibCheck,不引入any兜底;沿用现有构建的JS/TS共存能力。 +公开types/、入口/版本/依赖不变,注册仍为异步Promise,抽帧在后续metadata进行。 +分包tsconfig仅用于仓库实施检查,加入.npmignore,避免发布引用仓库外fixture的配置。 + +数字字段只作名义类型标注,没有加入运行时归一化、转换或额外getter读取;旧JS +可转换字符串/小数输入、原始width/number/scale向宿主转交及读取时序继续由既有 +用例保护。URL集合不接受undefined,timer清理排除尚未取得的null句柄;有效0 +句柄和取消中重入保持原有行为。 + +新增真正使用核心公开Artplayer类型的源码接入fixture。最初最小宿主错误要求 +已存在的thumbnails配置所有字段必填,编译报TS2345(height可为undefined)。 +修正为宿主允许既有部分配置,而job.publish产出的ThumbnailSheet仍完整必填; +最终分包严格检查包含该fixture并通过。没有修改核心或插件公开声明来迁就内部类型。 + +## 验证与限制 + +源码联合154项通过,其中52项当前生命周期/帧读取、102项历史契约与缺陷记录; +main/legacy各52项候选单测通过。源码/main/legacy各21共63项浏览器通过,0重试/ +跳过;有原生帧回调时五格像素严格断言,无回调路径的前两格仍是诊断,不关闭 +AUTO-THUMB-PIXEL-01。三格式正常构建和docs副本一致。新源码lint及分包严格TS通过。 +Yarn实际pack的七个文件通过分发入口检查,无src或tsconfig泄漏;此检查不替代安装消费验收。 +全仓类型流程通过398个生产TS及旧/新编译器消费者;其后新增的最终宿主接入fixture +又由分包严格检查单独验证。完整插件公开声明/安装包兼容仍待04及后续分发任务。 + +重跑:`yarn test:auto-thumbnail`、 +`yarn exec tsc -p packages/artplayer-plugin-auto-thumbnail/tsconfig.json --noEmit`、 +`yarn build artplayer-plugin-auto-thumbnail`;浏览器运行 +`yarn test:browser test/browser/auto-thumbnail-lifecycle.spec.js test/browser/auto-thumbnail-pixels.spec.js --workers=2`。 +以ARTPLAYER_AUTO_THUMBNAIL_ARTIFACT逐个选择main/legacy,必须等待终态并归档报告。 +机器结果见[内部TS证据](../baselines/auto-thumbnail-internal-types.json)。 + +03继续处理缺少原生帧回调时的首帧和其余资源边界;Mask03同时进行但文件和提交 +独立。本检查点可回退六个模块和新增类型/config/fixture,再正常构建;不推送或发布。 diff --git a/refactor/fixtures/implementation/auto-thumbnail.ts b/refactor/fixtures/implementation/auto-thumbnail.ts new file mode 100644 index 000000000..9796ddaef --- /dev/null +++ b/refactor/fixtures/implementation/auto-thumbnail.ts @@ -0,0 +1,6 @@ +import type Artplayer from 'artplayer' +import factory from '../../../packages/artplayer-plugin-auto-thumbnail/src' + +declare const art: Artplayer +const registration: Promise<{ name: string }> = factory({ width: 80, number: 5 })(art) +export { registration } diff --git a/refactor/progress.md b/refactor/progress.md index f7d7ed1cb..6c8cfe4cc 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,14 @@ # 进度与证据 +## PKG-AUTO-THUMB-03 内部TS检查点(仍doing) + +六个抽帧/会话/资源运行模块迁到严格TS,内部类型和实际核心宿主接入fixture齐备。 +源码154项(52候选、102历史)、产物各52项、三格式浏览器各21共63项通过; +严格分包及全仓398生产TS流程通过,最终宿主兼容修正另经分包fixture验证。 +仅当前实现内部契约;公开声明04、WebKit无帧回调的前两格像素与资源边界仍待完成。 +详见[检查点](changes/2026-09-13-PKG-AUTO-THUMB-03-internal-types.md);不增加done数, +不关闭像素风险。该检查点单独本地提交,Mask源码与状态不纳入同一提交。 + ## PKG-DANMUKU-07 弹幕稳定性阶段验收完成 12后的源码在三引擎完成新旧插件两档负载对照,每例三轮42秒原生播放;候选