mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
refactor(vtt-thumbnail): [PKG-VTT-THUMB-04] preserve legacy types and expose async runtime entry
This commit is contained in:
1 parent
7f2906ae52
commit
db358a3ae4
33 files changed
+3864
-98
No files matched your search
+26
-7
@@ -1,10 +1,29 @@
|
||||
declare const artplayerPluginVttThumbnail: (option: { vtt?: string, style?: Partial<CSSStyleDeclaration> }) => (
|
||||
art: Artplayer,
|
||||
) => {
|
||||
name: 'artplayerPluginVttThumbnail'
|
||||
// Generated from the package public declaration by yarn build:ts. Do not edit.
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
declare namespace artplayerPluginVttThumbnailDefinitions {
|
||||
export interface Option {
|
||||
vtt?: string
|
||||
style?: Partial<CSSStyleDeclaration>
|
||||
}
|
||||
export interface Result {
|
||||
name: 'artplayerPluginVttThumbnail'
|
||||
}
|
||||
/** Historical factory type. Actual registration is asynchronous. */
|
||||
export type Factory = (option: Option) => (art: Artplayer) => Result
|
||||
/** Accurate runtime view available without casts through the /runtime entry. */
|
||||
export interface RuntimeFactory {
|
||||
(option: Option): (art: Artplayer) => Promise<Result>
|
||||
default: RuntimeFactory
|
||||
}
|
||||
/** Preserve historical extraction and replacement-function compatibility. */
|
||||
export function artplayerPluginVttThumbnail(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
declare const artplayerPluginVttThumbnail: typeof artplayerPluginVttThumbnailDefinitions.artplayerPluginVttThumbnail
|
||||
declare namespace artplayerPluginVttThumbnail {
|
||||
export type Option = artplayerPluginVttThumbnailDefinitions.Option
|
||||
export type Result = artplayerPluginVttThumbnailDefinitions.Result
|
||||
export type Factory = artplayerPluginVttThumbnailDefinitions.Factory
|
||||
export type RuntimeFactory = artplayerPluginVttThumbnailDefinitions.RuntimeFactory
|
||||
}
|
||||
|
||||
export default artplayerPluginVttThumbnail
|
||||
|
||||
export = artplayerPluginVttThumbnail
|
||||
export as namespace artplayerPluginVttThumbnail;
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&m<n.clientWidth?function(t,e,i){c(t,"backgroundImage",`url(${e.url})`),c(t,"height",`${e.h}px`),c(t,"width",`${e.w}px`),c(t,"backgroundPosition",`-${e.x}px -${e.y}px`),i<=Number(e.w)/2?c(t,"left",0):i>n.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s<r.length&&!r[s].trim();)s++}if(l(),s===r.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(r[s].trim())&&!r[s].includes("--\x3e")||t(s+1,"expected WEBVTT header"),s++;const c=[];for(;s<r.length&&(l(),s!==r.length);){const i=r[s].trim();if(/^NOTE(?:[\t ]|$)/.test(i)||"STYLE"===i||"REGION"===i){for(;s<r.length&&r[s].trim();)s++;continue}const u=s+1;i.includes("--\x3e")||(s++,s!==r.length&&r[s].trim()||t(u,"cue identifier must be followed by timing"));const a=s+1,d=r[s].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);m<f&&t(a,"cue end precedes its start"),s++,l(),s===r.length&&t(a,"missing sprite image");const h=n(r[s].trim(),o,s+1);c.push({start:Math.floor(f),end:Math.floor(m),...h}),s++}return c}(m,f),p=i({lifetime:a,thumbnails:h,progress:u,duration:()=>r.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}});
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&m<n.clientWidth?function(t,e,i){c(t,"backgroundImage",`url(${e.url})`),c(t,"height",`${e.h}px`),c(t,"width",`${e.w}px`),c(t,"backgroundPosition",`-${e.x}px -${e.y}px`),i<=Number(e.w)/2?c(t,"left",0):i>n.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}function o(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s<r.length&&!r[s].trim();)s++}if(l(),s===r.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(r[s].trim())&&!r[s].includes("--\x3e")||t(s+1,"expected WEBVTT header"),s++;const c=[];for(;s<r.length&&(l(),s!==r.length);){const i=r[s].trim();if(/^NOTE(?:[\t ]|$)/.test(i)||"STYLE"===i||"REGION"===i){for(;s<r.length&&r[s].trim();)s++;continue}const u=s+1;i.includes("--\x3e")||(s++,s!==r.length&&r[s].trim()||t(u,"cue identifier must be followed by timing"));const a=s+1,d=r[s].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);m<f&&t(a,"cue end precedes its start"),s++,l(),s===r.length&&t(a,"missing sprite image");const h=n(r[s].trim(),o,s+1);c.push({start:Math.floor(f),end:Math.floor(m),...h}),s++}return c}(m,f),p=i({lifetime:a,thumbnails:h,progress:u,duration:()=>r.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}return Object.assign(o,{default:o})});
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&m<n.clientWidth?function(t,e,o){u(t,"backgroundImage",`url(${e.url})`),u(t,"height",`${e.h}px`),u(t,"width",`${e.w}px`),u(t,"backgroundPosition",`-${e.x}px -${e.y}px`),o<=Number(e.w)/2?u(t,"left",0):o>n.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o<n.length&&!n[o].trim();)o++}if(r(),o===n.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(n[o].trim())&&!n[o].includes("--\x3e")||s(o+1,"expected WEBVTT header"),o++;const l=[];for(;o<n.length&&(r(),o!==n.length);){const t=n[o].trim();if(/^NOTE(?:[\t ]|$)/.test(t)||"STYLE"===t||"REGION"===t){for(;o<n.length&&n[o].trim();)o++;continue}const a=o+1;t.includes("--\x3e")||(o++,o!==n.length&&n[o].trim()||s(a,"cue identifier must be followed by timing"));const d=o+1,f=n[o].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);p<m&&s(d,"cue end precedes its start"),o++,r(),o===n.length&&s(d,"missing sprite image");const h=u(n[o].trim(),e,o+1);l.push(i({start:Math.floor(m),end:Math.floor(p)},h)),o++}return l}(h,p),y=a({lifetime:Line truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&m<n.clientWidth?function(t,e,o){u(t,"backgroundImage",`url(${e.url})`),u(t,"height",`${e.h}px`),u(t,"width",`${e.w}px`),u(t,"backgroundPosition",`-${e.x}px -${e.y}px`),o<=Number(e.w)/2?u(t,"left",0):o>n.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}function d(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o<n.length&&!n[o].trim();)o++}if(r(),o===n.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(n[o].trim())&&!n[o].includes("--\x3e")||s(o+1,"expected WEBVTT header"),o++;const l=[];for(;o<n.length&&(r(),o!==n.length);){const t=n[o].trim();if(/^NOTE(?:[\t ]|$)/.test(t)||"STYLE"===t||"REGION"===t){for(;o<n.length&&n[o].trim();)o++;continue}const a=o+1;t.includes("--\x3e")||(o++,o!==n.length&&n[o].trim()||s(a,"cue identifier must be followed by timing"));const d=o+1,f=n[o].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);p<m&&s(d,"cue end precedes its start"),o++,r(),o===n.length&&s(d,"missing sprite image");const h=u(n[o].trim(),e,o+1);l.push(i({start:Math.floor(m),end:Math.floor(p)},h)),o++}return l}(h,p),y=a({lifetime:f,thuLine truncated
|
||||
@@ -77,9 +77,9 @@ function rectangle(text, vttUrl, line) {
|
||||
if (values.length !== 4)
|
||||
invalid(line, "expected four rectangle coordinates");
|
||||
const rect = {};
|
||||
for (let index = 0; index < 4; index++) {
|
||||
const key = match[2][index];
|
||||
const value = values[index];
|
||||
for (let index2 = 0; index2 < 4; index2++) {
|
||||
const key = match[2][index2];
|
||||
const value = values[index2];
|
||||
if (!/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(value) || !Number.isFinite(Number(value)))
|
||||
invalid(line, "rectangle coordinates must be finite non-negative decimals");
|
||||
if ((key === "w" || key === "h") && Number(value) === 0)
|
||||
@@ -102,49 +102,49 @@ function parseVtt(text, vttUrl = "") {
|
||||
if (typeof text !== "string")
|
||||
invalid(1, "expected text");
|
||||
const lines = text.replace(/^\uFEFF/, "").split(/\r\n|\r|\n/);
|
||||
let index = 0;
|
||||
let index2 = 0;
|
||||
function skipEmpty() {
|
||||
while (index < lines.length && !lines[index].trim())
|
||||
index++;
|
||||
while (index2 < lines.length && !lines[index2].trim())
|
||||
index2++;
|
||||
}
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
return [];
|
||||
if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index].trim()) || lines[index].includes("-->"))
|
||||
invalid(index + 1, "expected WEBVTT header");
|
||||
index++;
|
||||
if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index2].trim()) || lines[index2].includes("-->"))
|
||||
invalid(index2 + 1, "expected WEBVTT header");
|
||||
index2++;
|
||||
const thumbnails = [];
|
||||
while (index < lines.length) {
|
||||
while (index2 < lines.length) {
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
break;
|
||||
const first = lines[index].trim();
|
||||
const first = lines[index2].trim();
|
||||
if (/^NOTE(?:[\t ]|$)/.test(first) || first === "STYLE" || first === "REGION") {
|
||||
while (index < lines.length && lines[index].trim())
|
||||
index++;
|
||||
while (index2 < lines.length && lines[index2].trim())
|
||||
index2++;
|
||||
continue;
|
||||
}
|
||||
const identifierLine = index + 1;
|
||||
const identifierLine = index2 + 1;
|
||||
if (!first.includes("-->")) {
|
||||
index++;
|
||||
if (index === lines.length || !lines[index].trim())
|
||||
index2++;
|
||||
if (index2 === lines.length || !lines[index2].trim())
|
||||
invalid(identifierLine, "cue identifier must be followed by timing");
|
||||
}
|
||||
const timingLine = index + 1;
|
||||
const timing = lines[index].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);
|
||||
const timingLine = index2 + 1;
|
||||
const timing = lines[index2].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);
|
||||
if (!timing)
|
||||
invalid(timingLine, "invalid cue timing");
|
||||
const start = timestamp(timing[1], timingLine);
|
||||
const end = timestamp(timing[2], timingLine);
|
||||
if (end < start)
|
||||
invalid(timingLine, "cue end precedes its start");
|
||||
index++;
|
||||
index2++;
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
invalid(timingLine, "missing sprite image");
|
||||
const image = rectangle(lines[index].trim(), vttUrl, index + 1);
|
||||
const image = rectangle(lines[index2].trim(), vttUrl, index2 + 1);
|
||||
thumbnails.push({ start: Math.floor(start), end: Math.floor(end), ...image });
|
||||
index++;
|
||||
index2++;
|
||||
}
|
||||
return thumbnails;
|
||||
}
|
||||
@@ -273,6 +273,7 @@ function artplayerPluginVttThumbnail(option) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginVttThumbnail, { default: artplayerPluginVttThumbnail });
|
||||
export {
|
||||
artplayerPluginVttThumbnail as default
|
||||
index as default
|
||||
};
|
||||
+2
-1
@@ -86,7 +86,8 @@
|
||||
"test:thumbnail": "node --test test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs",
|
||||
"test:thumbnail-types-package": "node refactor/scripts/thumbnail-package-types.mjs",
|
||||
"test:auto-thumbnail": "node --test test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js refactor/scripts/auto-thumbnail-contract.test.mjs",
|
||||
"test:vtt-thumbnail": "node --test test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs"
|
||||
"test:vtt-thumbnail": "node --test test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js refactor/scripts/vtt-thumbnail-contract.test.mjs refactor/scripts/vtt-thumbnail-types.test.mjs",
|
||||
"test:vtt-thumbnail-types-package": "node refactor/scripts/vtt-thumbnail-package-types.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,4 +2,5 @@ node_modules
|
||||
.DS_Store
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
src
|
||||
src
|
||||
tsconfig.json
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
The factory remains `artplayerPluginVttThumbnail(option)`. Its registration function is
|
||||
asynchronous and resolves to `{ name: 'artplayerPluginVttThumbnail' }`. Existing public
|
||||
declarations still describe a synchronous result; task PKG-VTT-THUMB-04 owns that conflict
|
||||
and the public declaration/consumer migration. The seven internal TS files are strict. Do not infer the return shape from those declarations.
|
||||
root/legacy declarations retain the historical synchronous result for existing type extraction
|
||||
and replacement factories. The optional `/runtime` entry describes the real Promise without
|
||||
consumer assertions. Both entries load the same binary and function. The seven internal TS
|
||||
files are strict. Never infer synchronous runtime behavior from the compatibility declarations.
|
||||
|
||||
## Modules and ownership
|
||||
|
||||
@@ -34,14 +36,16 @@ rolled back if initialization throws, preserving the original rejection.
|
||||
|
||||
## Runtime types
|
||||
|
||||
`src/types.ts` defines options, result, rectangle/cue data, owned events, cleanup and
|
||||
`types/artplayer-plugin-vtt-thumbnail.d.ts` owns Option and Result; `src/types.ts` reexports
|
||||
them and defines rectangle/cue data, owned events, cleanup and
|
||||
preview inputs. Request cancellation is typed as a void branch; actual registration is
|
||||
Promise<Result>. The lifetime accepts only destroy/setBar and uses the core event tuples.
|
||||
The preview receives only its required DOM/style/duration inputs.
|
||||
|
||||
Assertions are limited to the real Artplayer constructor boundary, validated regex groups
|
||||
and loop indexes, the completed four-key rectangle, and native responses after the closed
|
||||
check. The latter is valid because native fetch returns Response and cancellation only
|
||||
check, plus the recursive `.default` factory alias created by Object.assign. The response
|
||||
assertion is valid because native fetch returns Response and cancellation only
|
||||
resolves void after closing; malformed custom responses still throw at runtime. Numeric
|
||||
rectangle strings are explicitly converted for arithmetic while the zero style write stays
|
||||
numeric. No any, unchecked JS or skipLibCheck exception is introduced.
|
||||
@@ -49,8 +53,22 @@ numeric. No any, unchecked JS or skipLibCheck exception is introduced.
|
||||
The package tsconfig also checks test/types/vtt-thumbnail-runtime.ts. It tests the async
|
||||
result, required options, CSS/URL types, event tuples, cancellation, readonly state and raw
|
||||
rectangle strings, including expected errors. It separately proves the old public result
|
||||
is synchronous in declarations; those declarations are not silently corrected in this
|
||||
runtime checkpoint. New/old declaration consumers and export aliases remain task 04.
|
||||
is synchronous in declarations. `test/types/vtt-thumbnail-public.ts` separately checks exact
|
||||
legacy parameter/result/factory extraction, replacement factories and accurate async calls
|
||||
through `/runtime`, including negative cases. The source factory explicitly returns Promise;
|
||||
its final assertion only describes the self-reference that Object.assign creates.
|
||||
|
||||
Root and legacy use paired `.d.mts`/`.d.cts` default declarations. `/runtime` uses an ESM
|
||||
default and a CommonJS export assignment, supporting direct and `.default` require calls.
|
||||
Classic resolution uses `.d.ts` and typesVersions, including TypeScript 4.3. The generated
|
||||
editor global retains the legacy callable type; its RuntimeFactory namespace member is only
|
||||
a type, not an additional runtime global. Do not hand-edit generated editor declarations.
|
||||
|
||||
The installed-consumer command packs both core and plugin, installs outside the workspace,
|
||||
verifies member hashes, and repeats an offline frozen install. It tests five actual old npm
|
||||
declarations with classic default-import consumers on TS 4.3/5.9 and seven candidate modes.
|
||||
Historical NodeNext and raw require type replacement forms still need investigation in 04;
|
||||
these checks do not prove all historical compiler/module combinations.
|
||||
|
||||
## Compatibility and intentional fixes
|
||||
|
||||
@@ -78,9 +96,11 @@ invalid CSS or partially mounted controls. Each cue has one image/rectangle payl
|
||||
multiline subtitle text, timestamp-map offsets and full caption semantics are outside this
|
||||
plugin. Empty or comment-only input remains a valid empty preview.
|
||||
|
||||
The old 1.0.x export objects, earlier `thumbnails` control name and incorrect declarations
|
||||
are separately frozen and remain compatibility work for tasks 04-06. Current runtime work
|
||||
does not claim those consumer migrations or the full package refactor are finished.
|
||||
The exported callable has a writable self `.default` alias, restoring the usable 1.0.x
|
||||
CommonJS default access while retaining direct calls. It does not add a name property to the
|
||||
registration Promise. Earlier `thumbnails` control names and remaining historical declaration
|
||||
forms are separately frozen and remain compatibility work for tasks 04-06. The full package
|
||||
refactor is not yet finished.
|
||||
|
||||
## Verification and future changes
|
||||
|
||||
@@ -88,6 +108,8 @@ Use Yarn 1.22.22 and the repository's pinned Node version:
|
||||
|
||||
```sh
|
||||
yarn test:vtt-thumbnail
|
||||
yarn test:vtt-thumbnail-types-package
|
||||
yarn build:ts artplayer-plugin-vtt-thumbnail
|
||||
yarn test:browser test/browser/vtt-thumbnail-lifecycle.spec.js
|
||||
yarn build artplayer-plugin-vtt-thumbnail
|
||||
```
|
||||
|
||||
@@ -6,6 +6,30 @@ Vtt thumbnail plugin for ArtPlayer
|
||||
|
||||
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-vtt-thumbnail/index.js&example=vtt.thumbnail)
|
||||
|
||||
## TypeScript usage
|
||||
|
||||
The registration function is asynchronous. The optional runtime entry provides its accurate
|
||||
Promise type and uses the same implementation as the default entry:
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer'
|
||||
import vttThumbnail from 'artplayer-plugin-vtt-thumbnail/runtime'
|
||||
|
||||
const art = new Artplayer({ container: '#player', url: '/video.mp4' })
|
||||
const result = await vttThumbnail({ vtt: '/thumbnails/index.vtt' })(art)
|
||||
console.log(result.name)
|
||||
```
|
||||
|
||||
You can also pass `vttThumbnail({ vtt: '/thumbnails/index.vtt' })` in the player's `plugins`
|
||||
array. Existing default and `/legacy` imports remain available. Their declarations preserve
|
||||
the historical synchronous result type for compatibility, although registration has always
|
||||
returned a Promise at runtime. Reading `result.name` requires awaiting registration; changing
|
||||
to `/runtime` opts into that accurate type without changing runtime behavior. The option
|
||||
object remains required, and its `vtt` and `style` fields remain optional.
|
||||
|
||||
See [ARCHITECTURE.md](ARCHITECTURE.md) for module ownership, testing commands and remaining
|
||||
compatibility validation.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Harvey Zhao
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&m<n.clientWidth?function(t,e,i){c(t,"backgroundImage",`url(${e.url})`),c(t,"height",`${e.h}px`),c(t,"width",`${e.w}px`),c(t,"backgroundPosition",`-${e.x}px -${e.y}px`),i<=Number(e.w)/2?c(t,"left",0):i>n.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s<r.length&&!r[s].trim();)s++}if(l(),s===r.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(r[s].trim())&&!r[s].includes("--\x3e")||t(s+1,"expected WEBVTT header"),s++;const c=[];for(;s<r.length&&(l(),s!==r.length);){const i=r[s].trim();if(/^NOTE(?:[\t ]|$)/.test(i)||"STYLE"===i||"REGION"===i){for(;s<r.length&&r[s].trim();)s++;continue}const u=s+1;i.includes("--\x3e")||(s++,s!==r.length&&r[s].trim()||t(u,"cue identifier must be followed by timing"));const a=s+1,d=r[s].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);m<f&&t(a,"cue end precedes its start"),s++,l(),s===r.length&&t(a,"missing sprite image");const h=n(r[s].trim(),o,s+1);c.push({start:Math.floor(f),end:Math.floor(m),...h}),s++}return c}(m,f),p=i({lifetime:a,thumbnails:h,progress:u,duration:()=>r.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}});
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&m<n.clientWidth?function(t,e,i){c(t,"backgroundImage",`url(${e.url})`),c(t,"height",`${e.h}px`),c(t,"width",`${e.w}px`),c(t,"backgroundPosition",`-${e.x}px -${e.y}px`),i<=Number(e.w)/2?c(t,"left",0):i>n.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}function o(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s<r.length&&!r[s].trim();)s++}if(l(),s===r.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(r[s].trim())&&!r[s].includes("--\x3e")||t(s+1,"expected WEBVTT header"),s++;const c=[];for(;s<r.length&&(l(),s!==r.length);){const i=r[s].trim();if(/^NOTE(?:[\t ]|$)/.test(i)||"STYLE"===i||"REGION"===i){for(;s<r.length&&r[s].trim();)s++;continue}const u=s+1;i.includes("--\x3e")||(s++,s!==r.length&&r[s].trim()||t(u,"cue identifier must be followed by timing"));const a=s+1,d=r[s].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);m<f&&t(a,"cue end precedes its start"),s++,l(),s===r.length&&t(a,"missing sprite image");const h=n(r[s].trim(),o,s+1);c.push({start:Math.floor(f),end:Math.floor(m),...h}),s++}return c}(m,f),p=i({lifetime:a,thumbnails:h,progress:u,duration:()=>r.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}return Object.assign(o,{default:o})});
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&m<n.clientWidth?function(t,e,o){u(t,"backgroundImage",`url(${e.url})`),u(t,"height",`${e.h}px`),u(t,"width",`${e.w}px`),u(t,"backgroundPosition",`-${e.x}px -${e.y}px`),o<=Number(e.w)/2?u(t,"left",0):o>n.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o<n.length&&!n[o].trim();)o++}if(r(),o===n.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(n[o].trim())&&!n[o].includes("--\x3e")||s(o+1,"expected WEBVTT header"),o++;const l=[];for(;o<n.length&&(r(),o!==n.length);){const t=n[o].trim();if(/^NOTE(?:[\t ]|$)/.test(t)||"STYLE"===t||"REGION"===t){for(;o<n.length&&n[o].trim();)o++;continue}const a=o+1;t.includes("--\x3e")||(o++,o!==n.length&&n[o].trim()||s(a,"cue identifier must be followed by timing"));const d=o+1,f=n[o].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);p<m&&s(d,"cue end precedes its start"),o++,r(),o===n.length&&s(d,"missing sprite image");const h=u(n[o].trim(),e,o+1);l.push(i({start:Math.floor(m),end:Math.floor(p)},h)),o++}return l}(h,p),y=a({lifetime:Line truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&m<n.clientWidth?function(t,e,o){u(t,"backgroundImage",`url(${e.url})`),u(t,"height",`${e.h}px`),u(t,"width",`${e.w}px`),u(t,"backgroundPosition",`-${e.x}px -${e.y}px`),o<=Number(e.w)/2?u(t,"left",0):o>n.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}function d(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o<n.length&&!n[o].trim();)o++}if(r(),o===n.length)return[];/^WEBVTT(?:[\t ].*)?$/.test(n[o].trim())&&!n[o].includes("--\x3e")||s(o+1,"expected WEBVTT header"),o++;const l=[];for(;o<n.length&&(r(),o!==n.length);){const t=n[o].trim();if(/^NOTE(?:[\t ]|$)/.test(t)||"STYLE"===t||"REGION"===t){for(;o<n.length&&n[o].trim();)o++;continue}const a=o+1;t.includes("--\x3e")||(o++,o!==n.length&&n[o].trim()||s(a,"cue identifier must be followed by timing"));const d=o+1,f=n[o].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);p<m&&s(d,"cue end precedes its start"),o++,r(),o===n.length&&s(d,"missing sprite image");const h=u(n[o].trim(),e,o+1);l.push(i({start:Math.floor(m),end:Math.floor(p)},h)),o++}return l}(h,p),y=a({lifetime:f,thuLine truncated
|
||||
+26
-25
@@ -77,9 +77,9 @@ function rectangle(text, vttUrl, line) {
|
||||
if (values.length !== 4)
|
||||
invalid(line, "expected four rectangle coordinates");
|
||||
const rect = {};
|
||||
for (let index = 0; index < 4; index++) {
|
||||
const key = match[2][index];
|
||||
const value = values[index];
|
||||
for (let index2 = 0; index2 < 4; index2++) {
|
||||
const key = match[2][index2];
|
||||
const value = values[index2];
|
||||
if (!/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(value) || !Number.isFinite(Number(value)))
|
||||
invalid(line, "rectangle coordinates must be finite non-negative decimals");
|
||||
if ((key === "w" || key === "h") && Number(value) === 0)
|
||||
@@ -102,49 +102,49 @@ function parseVtt(text, vttUrl = "") {
|
||||
if (typeof text !== "string")
|
||||
invalid(1, "expected text");
|
||||
const lines = text.replace(/^\uFEFF/, "").split(/\r\n|\r|\n/);
|
||||
let index = 0;
|
||||
let index2 = 0;
|
||||
function skipEmpty() {
|
||||
while (index < lines.length && !lines[index].trim())
|
||||
index++;
|
||||
while (index2 < lines.length && !lines[index2].trim())
|
||||
index2++;
|
||||
}
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
return [];
|
||||
if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index].trim()) || lines[index].includes("-->"))
|
||||
invalid(index + 1, "expected WEBVTT header");
|
||||
index++;
|
||||
if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index2].trim()) || lines[index2].includes("-->"))
|
||||
invalid(index2 + 1, "expected WEBVTT header");
|
||||
index2++;
|
||||
const thumbnails = [];
|
||||
while (index < lines.length) {
|
||||
while (index2 < lines.length) {
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
break;
|
||||
const first = lines[index].trim();
|
||||
const first = lines[index2].trim();
|
||||
if (/^NOTE(?:[\t ]|$)/.test(first) || first === "STYLE" || first === "REGION") {
|
||||
while (index < lines.length && lines[index].trim())
|
||||
index++;
|
||||
while (index2 < lines.length && lines[index2].trim())
|
||||
index2++;
|
||||
continue;
|
||||
}
|
||||
const identifierLine = index + 1;
|
||||
const identifierLine = index2 + 1;
|
||||
if (!first.includes("-->")) {
|
||||
index++;
|
||||
if (index === lines.length || !lines[index].trim())
|
||||
index2++;
|
||||
if (index2 === lines.length || !lines[index2].trim())
|
||||
invalid(identifierLine, "cue identifier must be followed by timing");
|
||||
}
|
||||
const timingLine = index + 1;
|
||||
const timing = lines[index].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);
|
||||
const timingLine = index2 + 1;
|
||||
const timing = lines[index2].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/);
|
||||
if (!timing)
|
||||
invalid(timingLine, "invalid cue timing");
|
||||
const start = timestamp(timing[1], timingLine);
|
||||
const end = timestamp(timing[2], timingLine);
|
||||
if (end < start)
|
||||
invalid(timingLine, "cue end precedes its start");
|
||||
index++;
|
||||
index2++;
|
||||
skipEmpty();
|
||||
if (index === lines.length)
|
||||
if (index2 === lines.length)
|
||||
invalid(timingLine, "missing sprite image");
|
||||
const image = rectangle(lines[index].trim(), vttUrl, index + 1);
|
||||
const image = rectangle(lines[index2].trim(), vttUrl, index2 + 1);
|
||||
thumbnails.push({ start: Math.floor(start), end: Math.floor(end), ...image });
|
||||
index++;
|
||||
index2++;
|
||||
}
|
||||
return thumbnails;
|
||||
}
|
||||
@@ -273,6 +273,7 @@ function artplayerPluginVttThumbnail(option) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginVttThumbnail, { default: artplayerPluginVttThumbnail });
|
||||
export {
|
||||
artplayerPluginVttThumbnail as default
|
||||
index as default
|
||||
};
|
||||
@@ -19,19 +19,49 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.ts",
|
||||
"import": "./dist/artplayer-plugin-vtt-thumbnail.mjs",
|
||||
"require": "./dist/artplayer-plugin-vtt-thumbnail.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.mts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.cts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.ts",
|
||||
"import": "./dist/artplayer-plugin-vtt-thumbnail.legacy.js",
|
||||
"require": "./dist/artplayer-plugin-vtt-thumbnail.legacy.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.mts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.cts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.legacy.js"
|
||||
}
|
||||
},
|
||||
"./runtime": {
|
||||
"import": {
|
||||
"types": "./types/runtime.d.mts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/runtime.d.cts",
|
||||
"default": "./dist/artplayer-plugin-vtt-thumbnail.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-plugin-vtt-thumbnail.js",
|
||||
"module": "./dist/artplayer-plugin-vtt-thumbnail.mjs",
|
||||
"types": "./types/artplayer-plugin-vtt-thumbnail.d.ts",
|
||||
"legacy": "./dist/artplayer-plugin-vtt-thumbnail.legacy.js",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-plugin-vtt-thumbnail.d.ts"
|
||||
],
|
||||
"runtime": [
|
||||
"types/runtime.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
import type { RuntimeFactory } from '../types/artplayer-plugin-vtt-thumbnail'
|
||||
import type { Option, Result } from './types'
|
||||
import createLifetime from './lifetime'
|
||||
import parseVtt from './parseVtt'
|
||||
import createPreview from './preview'
|
||||
import requestVtt from './request'
|
||||
|
||||
export default function artplayerPluginVttThumbnail(option: Option) {
|
||||
function artplayerPluginVttThumbnail(option: Option) {
|
||||
return async (art: Artplayer): Promise<Result> => {
|
||||
const { setStyle, isMobile, addClass } = (art.constructor as typeof Artplayer).utils
|
||||
const { $progress } = art.template
|
||||
@@ -47,3 +48,6 @@ export default function artplayerPluginVttThumbnail(option: Option) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Object.assign adds the alias to the same callable referenced by that alias.
|
||||
export default Object.assign(artplayerPluginVttThumbnail, { default: artplayerPluginVttThumbnail }) as RuntimeFactory
|
||||
@@ -1,14 +1,7 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
import type { Events, Utils } from 'artplayer'
|
||||
|
||||
export interface Option {
|
||||
vtt?: string
|
||||
style?: Partial<CSSStyleDeclaration>
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
name: 'artplayerPluginVttThumbnail'
|
||||
}
|
||||
export type { Option, Result } from '../types/artplayer-plugin-vtt-thumbnail'
|
||||
|
||||
export interface Rectangle {
|
||||
x: string
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": { "allowJs": false },
|
||||
"include": ["src/**/*.ts", "types/**/*.d.ts", "../../types/assets.d.ts", "../../test/types/vtt-thumbnail-runtime.ts"]
|
||||
"compilerOptions": {
|
||||
"allowJs": false
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"types/**/*.d.ts",
|
||||
"../../types/assets.d.ts",
|
||||
"../../test/types/vtt-thumbnail-runtime.ts",
|
||||
"types/**/*.d.mts",
|
||||
"types/**/*.d.cts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { Factory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: Factory
|
||||
export default artplayerPluginVttThumbnail
|
||||
export type { Factory, Option, Result, RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { Factory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: Factory
|
||||
export default artplayerPluginVttThumbnail
|
||||
export type { Factory, Option, Result, RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
+17
-3
@@ -1,9 +1,23 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: (option: { vtt?: string, style?: Partial<CSSStyleDeclaration> }) => (
|
||||
art: Artplayer,
|
||||
) => {
|
||||
export interface Option {
|
||||
vtt?: string
|
||||
style?: Partial<CSSStyleDeclaration>
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
name: 'artplayerPluginVttThumbnail'
|
||||
}
|
||||
|
||||
/** Historical factory type. Actual registration is asynchronous. */
|
||||
export type Factory = (option: Option) => (art: Artplayer) => Result
|
||||
|
||||
/** Accurate runtime view available without casts through the /runtime entry. */
|
||||
export interface RuntimeFactory {
|
||||
(option: Option): (art: Artplayer) => Promise<Result>
|
||||
default: RuntimeFactory
|
||||
}
|
||||
|
||||
/** Preserve historical extraction and replacement-function compatibility. */
|
||||
declare function artplayerPluginVttThumbnail(option: Option): (art: Artplayer) => Result
|
||||
export default artplayerPluginVttThumbnail
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: RuntimeFactory
|
||||
export = artplayerPluginVttThumbnail
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: RuntimeFactory
|
||||
export default artplayerPluginVttThumbnail
|
||||
export type { Option, Result, RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
|
||||
declare const artplayerPluginVttThumbnail: RuntimeFactory
|
||||
export default artplayerPluginVttThumbnail
|
||||
export type { Option, Result, RuntimeFactory } from './artplayer-plugin-vtt-thumbnail.js'
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,48 @@
|
||||
# PKG-VTT-THUMB-04 公开类型与安装消费者检查点
|
||||
|
||||
本次覆盖 API-06/09/11:保留 root/legacy 的旧工厂参数、同步结果类型与工厂替换赋值,
|
||||
增加 `/runtime` 入口,给新代码准确的 Promise<Result>。两种入口映射到同一产物和函数;
|
||||
不会给 Promise 添加 name 字段来伪造同步返回。Option/Result 由公开定义统一维护,
|
||||
内部实现引用这些数据类型,保持必传 options 对象、可选 vtt/style 和原有 CSS 字段类型。
|
||||
|
||||
## 消费方式与兼容边界
|
||||
|
||||
旧代码 `import vtt from 'artplayer-plugin-vtt-thumbnail'` 不需要改动。
|
||||
旧声明误把实际异步注册写成同步 Result;直接修正会影响 ReturnType 和替换工厂赋值。
|
||||
本次保留该兼容声明,新代码可使用
|
||||
`import vtt from 'artplayer-plugin-vtt-thumbnail/runtime'`,然后
|
||||
`const result = await vtt({ vtt: '/cues.vtt' })(art)`,无需消费者类型断言。
|
||||
没有改变运行时异步行为或默认产品配置。
|
||||
|
||||
导出函数增加可写的自引用 `.default`,兼容旧 1.0.x 有效的 CommonJS default 调用,
|
||||
并保持最新直接调用。新回归在上个提交的真实 bundle 中仅此项失败,另外 21 项通过;
|
||||
候选注册仍返回无同步 name 的 Promise。根入口/legacy 用 .d.mts/.d.cts 保留默认导入类型,
|
||||
runtime 的 CommonJS 声明使用 export=,支持直接及 default 调用。typesVersions 支持
|
||||
classic 解析。编辑器全局仍为旧调用类型,命名空间 RuntimeFactory 只是类型,并非新增全局函数。
|
||||
|
||||
## 验证和工程改动
|
||||
|
||||
- 本包测试 251 项;源码/main/legacy 解析与生命周期各 48 项通过。纯 parser 断言仍取源码,
|
||||
非法输入、扩展 VTT 注册和生命周期会执行选定产物,不将纯函数检查计作安装包行为。
|
||||
- TS 5.9.3/5.1.6 严格源码检查通过,移除 expect-error 各复现 8 项预期错误。
|
||||
公开声明五种模式各验证 10 项反例,编辑器 TS 5.9/4.3 各验证 2 项反例。
|
||||
- `yarn test:vtt-thumbnail-types-package` 是新增的实际打包验证脚本;固定 Yarn 1.22.22,
|
||||
在仓库外安装 packed core 和五份历史 npm 包/候选包,核对成员字节并执行 offline/frozen 重装。
|
||||
五份旧声明 × 两个 classic 编译器,加候选七种模式,共 17 格通过。候选还验证原生 Node
|
||||
root/runtime ESM 与 CJS 入口身份相同、legacy 默认导入与 require 身份相同。
|
||||
- 第一次实际 pack 发现 tsconfig.json 泄漏,已通过 .npmignore 排除,未关闭包内容检查。
|
||||
新脚本及旧消费者夹具完成显式 lint;没有增加依赖或修改锁文件。
|
||||
- 原生 Chromium/Firefox/Windows WebKit,发布/候选核心,三场景和三种插件入口共 54 项通过,
|
||||
覆盖真实 hover、sprite 解码、非法输入恢复、原生 fetch 取消和 destroy 清理。不是实体 Safari 验收。
|
||||
- 正常 build 更新三个 dist 与 docs 副本;build:ts 用语义生成器更新编辑器声明。
|
||||
完整 CI 的最终结果及内容指纹见 [验证记录](../baselines/vtt-thumbnail-public-types.json)。
|
||||
|
||||
## 剩余工作与回退
|
||||
|
||||
04 仍为 doing。已通过的历史类型消费者是 classic default-import 和工厂替换;历史 NodeNext、
|
||||
原始 import=require 的类型替换形式仍需逐项核对,不能扩称所有历史 TS 用法都通过。
|
||||
早期控件名、完整核心/设备组合、源码深路径分发与在线编辑器交互继续归 05/06。
|
||||
相关 TYPE/EXPORT 风险保持 open,尚未升级版本或达到 npm 发布条件。
|
||||
|
||||
本次作为独立本地检查点提交。回退本提交可恢复前次公开声明/入口并移除新增 runtime 入口
|
||||
及 default 别名;此前请求、解析与生命周期修复不受影响。不推送、不发布。
|
||||
@@ -0,0 +1,9 @@
|
||||
import Artplayer from 'artplayer'
|
||||
import vtt from 'artplayer-plugin-vtt-thumbnail'
|
||||
|
||||
const option: Parameters<typeof vtt>[0] = { vtt: '/cues.vtt', style: { opacity: '0.8' } }
|
||||
const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [vtt(option)] })
|
||||
const result: ReturnType<ReturnType<typeof vtt>> = { name: 'artplayerPluginVttThumbnail' }
|
||||
const replacement: typeof vtt = (_option: Parameters<typeof vtt>[0]) => (_art: Artplayer) => result
|
||||
void vtt(option)(art).name
|
||||
void replacement
|
||||
+1
-1
@@ -500,7 +500,7 @@
|
||||
- PKG-VTT-THUMB-01: [记录](baselines/vtt-thumbnail-release.json) [记录](baselines/vtt-thumbnail-contract.md) [记录](changes/2026-09-13-PKG-VTT-THUMB-01-contract.md)
|
||||
- PKG-VTT-THUMB-02: [记录](baselines/vtt-thumbnail-failures.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-02-failures.md)
|
||||
- PKG-VTT-THUMB-03: [记录](changes/2026-09-13-PKG-VTT-THUMB-03-resources.md) [记录](baselines/vtt-thumbnail-resources.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-03-parser.md) [记录](baselines/vtt-thumbnail-parser.json)
|
||||
- PKG-VTT-THUMB-04: [记录](changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md) [记录](baselines/vtt-thumbnail-runtime-types.json)
|
||||
- PKG-VTT-THUMB-04: [记录](changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md) [记录](baselines/vtt-thumbnail-runtime-types.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-04-public-types.md) [记录](baselines/vtt-thumbnail-public-types.json)
|
||||
- PKG-HLS-01: [记录](baselines/hls-control-contract.md) [记录](baselines/hls-control-release.json) [记录](changes/2026-09-11-PKG-HLS-01-contract.md)
|
||||
- PKG-HLS-02: [记录](hls-validation.md) [记录](baselines/hls-sdk.json) [记录](baselines/hls-validation.json) [记录](changes/2026-09-11-PKG-HLS-02-tests.md)
|
||||
- PKG-HLS-03: [记录](changes/2026-09-12-PKG-HLS-03-modules.md) [记录](baselines/hls-modules-validation.json) [记录](hls-validation.md)
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-VTT-THUMB-04 公开类型与安装消费者检查点(doing)
|
||||
|
||||
保留 root/legacy 旧参数、同步结果类型及替换工厂赋值,新增 /runtime 表达真实 Promise,
|
||||
两入口复用相同函数。可写 .default 自引用恢复旧 CJS default 调用,Promise 不伪装同步 name。
|
||||
公开 Option/Result 与内部实现共用;成对模块声明、classic typesVersions 和编辑器生成同步。
|
||||
本包 251 项、三入口各 48 项、原生三入口共 54 项通过;仓库外实际安装五历史包与候选包,
|
||||
17 格类型矩阵和 Node 入口身份通过,offline/frozen 重装及成员字节核对通过。
|
||||
修复实际 pack 发现的 tsconfig 泄漏。TS 5.9.3/5.1.6 严格正反例通过,完整 CI 2069 项通过。
|
||||
见[变更](changes/2026-09-13-PKG-VTT-THUMB-04-public-types.md)和
|
||||
[验证](baselines/vtt-thumbnail-public-types.json)。历史 NodeNext/raw require 类型形式仍待 04,
|
||||
完整核心/设备、早期控件名与分发/demo 仍待 05/06,TYPE/EXPORT 风险未关闭。
|
||||
222 项:117 done、13 doing、92 todo;独立本地检查点提交,不推送、不发布。
|
||||
|
||||
|
||||
## PKG-VTT-THUMB-04 严格运行时 TS 检查点(doing)
|
||||
|
||||
六个 JS 实现迁为 TS,另有明确数据/资源类型模块,分包 allowJs=false。TS 5.9.3/5.1.6
|
||||
|
||||
+8
-2
@@ -4202,7 +4202,10 @@
|
||||
"refactor/baselines/vtt-thumbnail-contract.md",
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs",
|
||||
"test/types/vtt-thumbnail-runtime.ts",
|
||||
"refactor/baselines/vtt-thumbnail-runtime-types.json"
|
||||
"refactor/baselines/vtt-thumbnail-runtime-types.json",
|
||||
"refactor/changes/2026-09-13-PKG-VTT-THUMB-04-public-types.md",
|
||||
"refactor/baselines/vtt-thumbnail-public-types.json",
|
||||
"refactor/scripts/vtt-thumbnail-package-types.mjs"
|
||||
],
|
||||
"compatibleResolution": "Preserve valid consumers and actual async runtime, with explicit decisions for irreconcilable inference changes.",
|
||||
"closureCriteria": "Isolated old/new compiler and installed consumers verify options, styles, callback and result forms."
|
||||
@@ -4220,7 +4223,10 @@
|
||||
"evidence": [
|
||||
"refactor/baselines/vtt-thumbnail-release.json",
|
||||
"refactor/baselines/vtt-thumbnail-contract.md",
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs"
|
||||
"refactor/scripts/vtt-thumbnail-contract.test.mjs",
|
||||
"refactor/changes/2026-09-13-PKG-VTT-THUMB-04-public-types.md",
|
||||
"refactor/baselines/vtt-thumbnail-public-types.json",
|
||||
"refactor/scripts/vtt-thumbnail-package-types.mjs"
|
||||
],
|
||||
"compatibleResolution": "Validate historical import forms and control integrations before selecting compatibility adapters.",
|
||||
"closureCriteria": "Actual package consumers and old/new cores prove supported forms; unresolved differences stay explicit."
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
|
||||
import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs'
|
||||
import { hash, readMember } from './releases.mjs'
|
||||
import { verifyVttThumbnailContract } from './vtt-thumbnail-contract.mjs'
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:vtt-thumbnail-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/vtt-thumbnail-package-types-'))
|
||||
console.log(`VTT thumbnail isolated package evidence: ${output}`)
|
||||
const contract = await verifyVttThumbnailContract()
|
||||
const packages = []
|
||||
for (const name of ['artplayer', 'artplayer-plugin-vtt-thumbnail']) {
|
||||
const archive = path.join(output, `${name}.tgz`)
|
||||
fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name)))
|
||||
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
|
||||
const files = packedFiles(archive)
|
||||
checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : [])
|
||||
packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
|
||||
}
|
||||
const matrix = []
|
||||
const core = packages[0]
|
||||
const candidates = [
|
||||
...[...contract.baseline.previous].reverse().concat(contract.baseline.release).map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })),
|
||||
{ ...packages[1], label: 'candidate' },
|
||||
]
|
||||
for (const plugin of candidates) {
|
||||
const consumer = consumerDirectory()
|
||||
try {
|
||||
writeJson(path.join(consumer, 'package.json'), { name: 'vtt-thumbnail-isolated-consumer', private: true, dependencies: Object.fromEntries([core, plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) })
|
||||
fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock'))
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
const lock = fs.readFileSync(path.join(consumer, 'yarn.lock'))
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock)
|
||||
for (const pkg of [core, plugin]) {
|
||||
const root = path.join(consumer, 'node_modules', pkg.name)
|
||||
assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link')
|
||||
for (const [member, expected] of Object.entries(pkg.files))
|
||||
assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`)
|
||||
}
|
||||
const runtimeFile = path.join(consumer, 'exports.cjs')
|
||||
fs.writeFileSync(runtimeFile, plugin.label === 'candidate'
|
||||
? `const assert = require('node:assert/strict');
|
||||
for (const name of ['artplayer-plugin-vtt-thumbnail', 'artplayer-plugin-vtt-thumbnail/legacy', 'artplayer-plugin-vtt-thumbnail/runtime']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory({}), 'function'); }
|
||||
assert.equal(require('artplayer-plugin-vtt-thumbnail/runtime'), require('artplayer-plugin-vtt-thumbnail'));
|
||||
Promise.all(['artplayer-plugin-vtt-thumbnail', 'artplayer-plugin-vtt-thumbnail/runtime', 'artplayer-plugin-vtt-thumbnail/legacy'].map(name => import(name))).then(([root, runtime, legacy]) => { assert.deepEqual(Object.keys(root), ['default']); assert.deepEqual(Object.keys(runtime), ['default']); assert.equal(runtime.default, root.default); assert.equal(root.default.default, root.default); assert.equal(legacy.default, require('artplayer-plugin-vtt-thumbnail/legacy')); assert.equal(legacy.default.default, legacy.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
: plugin.version === '1.0.0'
|
||||
? `const assert = require('node:assert/strict'); assert.throws(() => require('artplayer-plugin-vtt-thumbnail'), error => error.name === 'SyntaxError' && error.message.includes('Invalid regular expression')); console.log('Expected historical 1.0.0 invalid runtime syntax preserved');`
|
||||
: `const assert = require('node:assert/strict'); const value = require('artplayer-plugin-vtt-thumbnail'); assert.equal(typeof value, '${plugin.version === '1.1.0' ? 'function' : 'object'}'); assert.equal(typeof (value.default || value)({}), 'function');`)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
|
||||
const candidateModes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
if (plugin.label === 'candidate')
|
||||
candidateModes.push([ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop'])
|
||||
for (const [compiler, mode] of (plugin.label === 'candidate' ? candidateModes : [[ts, 'node10-commonjs'], [compat, 'node10-commonjs']])) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/vtt-thumbnail-public.ts' : 'refactor/fixtures/consumers/vtt-thumbnail-published.ts'), 'utf8')
|
||||
const filename = path.join(consumer, `consumer.${extension}`)
|
||||
const historical = path.join(consumer, `historical.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/vtt-thumbnail-published.ts'), historical)
|
||||
const files = plugin.label === 'candidate' ? [filename, historical] : [filename]
|
||||
if (mode === 'nodenext-cjs' && plugin.label === 'candidate') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import vtt = require('artplayer-plugin-vtt-thumbnail'); import legacy = require('artplayer-plugin-vtt-thumbnail/legacy'); import runtime = require('artplayer-plugin-vtt-thumbnail/runtime'); vtt.default({}); legacy.default({}); runtime({}); runtime.default({});`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
|
||||
function compile(code) {
|
||||
fs.writeFileSync(filename, code)
|
||||
const program = compiler.createProgram(files, options)
|
||||
for (const file of program.getSourceFiles()) {
|
||||
const actual = fs.realpathSync(file.fileName)
|
||||
assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`)
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
const expected = []
|
||||
const historicalFailure = false
|
||||
writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}-diagnostics.json`), { diagnostics, historicalFailure })
|
||||
assert.deepEqual(diagnostics.map(item => item.code), expected, `${plugin.label} ${compiler.version} ${mode}`)
|
||||
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (plugin.label === 'candidate')
|
||||
assert.equal(invalid.length, 10, 'Installed declarations must reject all invalid uses')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
|
||||
}
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'vtt-thumbnail-isolated-package-types', introducedBy: 'PKG-VTT-THUMB-04', scope: 'Five actual published packages (classic default-import consumers on TS 4.3 and 5.9) and packed candidate (seven compiler modes) installed outside the workspace with packed core. Offline/frozen reinstall and byte identity are verified. Candidate provides an opt-in accurate runtime entry; 1.0.0 invalid bundle syntax is retained. Historical NodeNext variants, raw require type replacements, device and full distribution acceptance remain separate.', packages, published: candidates.slice(0, 5).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: hash(fs.readFileSync(pkg.archive)), missingEntrypoints: pkg.missingEntrypoints })), matrix })
|
||||
console.log(`VTT thumbnail installed types passed: ${matrix.length} cases; ${output}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,81 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Compatibility declaration runner.
|
||||
import test from 'node:test'
|
||||
import { ESLint } from 'eslint'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
|
||||
import { checkConsumer } from '../../scripts/typecheck.mjs'
|
||||
import { readMember } from './releases.mjs'
|
||||
import { verifyVttThumbnailContract } from './vtt-thumbnail-contract.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
|
||||
test('VTT generated editor globals retain legacy calls and expose separately named data and runtime types', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-plugin-vtt-thumbnail/types/artplayer-plugin-vtt-thumbnail.d.ts', 'utf8')
|
||||
const generated = generatePluginEditorDeclaration(source, 'artplayerPluginVttThumbnail')
|
||||
const file = 'docs/assets/ts/artplayer-plugin-vtt-thumbnail.d.ts'
|
||||
const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(generated, { filePath: file })
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
const consumer = `declare const art: Artplayer;
|
||||
const options: artplayerPluginVttThumbnail.Option = { vtt: '/cues.vtt' };
|
||||
const result: artplayerPluginVttThumbnail.Result = artplayerPluginVttThumbnail(options)(art);
|
||||
declare const runtime: artplayerPluginVttThumbnail.RuntimeFactory;
|
||||
const pending: Promise<artplayerPluginVttThumbnail.Result> = runtime(options)(art);
|
||||
// @ts-expect-error Legacy extraction stays synchronous.
|
||||
const changed: Promise<artplayerPluginVttThumbnail.Result> = result;
|
||||
// @ts-expect-error URL must be text.
|
||||
artplayerPluginVttThumbnail({ vtt: 12 });`
|
||||
for (const compiler of [ts, compat]) {
|
||||
assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 2)
|
||||
}
|
||||
})
|
||||
|
||||
test('VTT public legacy extraction remains exact while runtime entry gives cast-free Promise types', () => {
|
||||
const source = fs.readFileSync('test/types/vtt-thumbnail-public.ts', 'utf8')
|
||||
for (const [compiler, mode] of modes) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 10)
|
||||
}
|
||||
})
|
||||
|
||||
test('VTT runtime CommonJS declaration supports direct and default calls without an interop cast', () => {
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import runtime = require('artplayer-plugin-vtt-thumbnail/runtime');
|
||||
import legacy = require('artplayer-plugin-vtt-thumbnail');
|
||||
import type Artplayer from 'artplayer'; declare const art: Artplayer;
|
||||
const first = runtime({})(art); const second = runtime.default({})(art);
|
||||
const promise: Promise<{name: 'artplayerPluginVttThumbnail'}> = first;
|
||||
legacy.default({})(art).name; void [promise, second];`), [])
|
||||
})
|
||||
|
||||
test('VTT all five historical declarations keep the same default-import consumers and replaceable old factory', async () => {
|
||||
const contract = await verifyVttThumbnailContract()
|
||||
const consumer = fs.readFileSync('refactor/fixtures/consumers/vtt-thumbnail-published.ts', 'utf8')
|
||||
for (const release of [contract.baseline.release, ...contract.baseline.previous]) {
|
||||
const old = readMember(contract.archives.get(release.version), `package/${release.manifest.types.replace(/^\.\//, '')}`).toString()
|
||||
for (const compiler of [ts, compat]) {
|
||||
const filename = path.resolve('test/types/vtt-historical-consumer.ts')
|
||||
const declaration = path.resolve('test/types/vtt-historical.d.ts')
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, module: compiler.ModuleKind.CommonJS, moduleResolution: compiler.ModuleResolutionKind.NodeJs, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'] }
|
||||
function historical(source) {
|
||||
const host = compiler.createCompilerHost(options)
|
||||
const read = host.getSourceFile.bind(host)
|
||||
host.getSourceFile = (file, language, ...rest) => path.resolve(file) === filename
|
||||
? compiler.createSourceFile(file, source.replace('from \'artplayer-plugin-vtt-thumbnail\'', 'from \'./vtt-historical\''), language, true)
|
||||
: path.resolve(file) === declaration ? compiler.createSourceFile(file, old, language, true) : read(file, language, ...rest)
|
||||
const exists = host.fileExists.bind(host)
|
||||
host.fileExists = file => path.resolve(file) === declaration || exists(file)
|
||||
return compiler.getPreEmitDiagnostics(compiler.createProgram([filename, declaration], options, host)).map(error => error.code)
|
||||
}
|
||||
assert.deepEqual(historical(consumer), [])
|
||||
assert.deepEqual(historical(`${consumer}\nvtt();`), [2554])
|
||||
assert.deepEqual(historical(`${consumer}\nconst absent: Parameters<typeof vtt>[0] = undefined;`), [2322])
|
||||
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', consumer), [])
|
||||
}
|
||||
}
|
||||
})
|
||||
+3
-1
@@ -1927,7 +1927,9 @@
|
||||
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
|
||||
"evidence": [
|
||||
"changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md",
|
||||
"baselines/vtt-thumbnail-runtime-types.json"
|
||||
"baselines/vtt-thumbnail-runtime-types.json",
|
||||
"changes/2026-09-13-PKG-VTT-THUMB-04-public-types.md",
|
||||
"baselines/vtt-thumbnail-public-types.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ console.log(`✨ Built ${artplayerTSoutput}`);
|
||||
const type = pluginsTS[index]
|
||||
const { name, file } = parsePluginInfo(type)
|
||||
const source = String(fs.readFileSync(type))
|
||||
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name)
|
||||
const semanticPlugin = ['artplayerPluginVttThumbnail', 'artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name)
|
||||
const localTypes = name === 'artplayerProxyMediabunny' ? { './media': fs.readFileSync(path.join(path.dirname(type), 'media.d.ts'), 'utf8') } : {}
|
||||
const code = semanticPlugin
|
||||
? generatePluginEditorDeclaration(source, name, localTypes)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Factory, Option, Result, RuntimeFactory } from 'artplayer-plugin-vtt-thumbnail'
|
||||
import Artplayer from 'artplayer'
|
||||
import legacy from 'artplayer-plugin-vtt-thumbnail'
|
||||
import legacyEntry from 'artplayer-plugin-vtt-thumbnail/legacy'
|
||||
import runtime from 'artplayer-plugin-vtt-thumbnail/runtime'
|
||||
|
||||
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
|
||||
type Assert<T extends true> = T
|
||||
type ExactOptions = Assert<Equal<Parameters<typeof legacy>, [option: Option]>>
|
||||
type ExactFactory = Assert<Equal<typeof legacy, Factory>>
|
||||
type ExactResult = Assert<Equal<ReturnType<ReturnType<typeof legacy>>, Result>>
|
||||
type ActualResult = Assert<Equal<ReturnType<ReturnType<typeof runtime>>, Promise<Result>>>
|
||||
|
||||
const option: Option = { vtt: '/cues.vtt', style: { opacity: '0.8' } }
|
||||
const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [legacy(option), runtime(option)] })
|
||||
const old: Result = legacy(option)(art)
|
||||
const oldReplacement: typeof legacy = (_option: Option) => (_art: Artplayer) => ({ name: 'artplayerPluginVttThumbnail' })
|
||||
legacyEntry(option)
|
||||
const factory: RuntimeFactory = runtime
|
||||
const pending: Promise<Result> = factory(option)(art)
|
||||
const alias: Promise<Result> = factory.default.default(option)(art)
|
||||
factory.default = runtime
|
||||
const awaitedName: Promise<'artplayerPluginVttThumbnail'> = pending.then(result => result.name)
|
||||
// @ts-expect-error Published extraction keeps the option object required.
|
||||
const absent: Parameters<typeof legacy>[0] = undefined
|
||||
// @ts-expect-error Root declarations preserve historical synchronous extraction.
|
||||
const changed: Promise<Result> = old
|
||||
// @ts-expect-error Runtime entry does not invent a synchronous result field.
|
||||
void pending.name
|
||||
// @ts-expect-error Runtime options remain required.
|
||||
runtime()
|
||||
// @ts-expect-error Required option object is not undefined.
|
||||
runtime(undefined)
|
||||
// @ts-expect-error URL remains a string.
|
||||
runtime({ vtt: 1 })
|
||||
// @ts-expect-error CSS width remains a string.
|
||||
runtime({ style: { width: 80 } })
|
||||
// @ts-expect-error Unknown options are not silently accepted.
|
||||
runtime({ unknown: true })
|
||||
// @ts-expect-error A host is required.
|
||||
runtime({})()
|
||||
// @ts-expect-error The runtime entry cannot be replaced with a synchronous registrar.
|
||||
const wrong: typeof runtime = oldReplacement
|
||||
void [alias, awaitedName, absent, changed, wrong]
|
||||
export type { ActualResult, ExactFactory, ExactOptions, ExactResult }
|
||||
@@ -11,6 +11,18 @@ async function flush() {
|
||||
}
|
||||
const count = env => [...env.listeners.values()].reduce((sum, items) => sum + items.length, 0)
|
||||
|
||||
test('VTT candidate preserves callable default identity without adding synchronous fields to the registration Promise', async () => {
|
||||
const env = create()
|
||||
assert.equal(env.factory.default, env.factory)
|
||||
assert.equal(env.factory.default.default, env.factory)
|
||||
assert.equal(Object.getOwnPropertyDescriptor(env.factory, 'default').writable, true)
|
||||
const pending = env.factory.default({})(env.art)
|
||||
assert.equal(pending.name, undefined)
|
||||
assert.deepEqual(Object.keys(pending), [])
|
||||
assert.equal((await pending).name, 'artplayerPluginVttThumbnail')
|
||||
await env.emit('destroy')
|
||||
})
|
||||
|
||||
test('VTT candidate retains asynchronous registration, live style, floored bounds and literal image paths', async () => {
|
||||
const env = create({ deferred: true })
|
||||
const option = { vtt: '/folder/cues.vtt', style: {} }
|
||||
|
||||
Reference in new issue
Block a user