mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
refactor(ads): [PKG-ADS-04] reconcile public types and historical imports
This commit is contained in:
1 parent
39e2d4cdfb
commit
fb1cfa701d
41 files changed
+1208
-149
No files matched your search
+75
-49
@@ -1,52 +1,78 @@
|
||||
interface Option {
|
||||
/**
|
||||
* 广告源文本,支持视频链接、图片链接、HTML文本
|
||||
*/
|
||||
source: string
|
||||
|
||||
/**
|
||||
* 知名广告的类型:'video' | 'image' | 'html'
|
||||
*/
|
||||
type: 'video' | 'image' | 'html'
|
||||
|
||||
/**
|
||||
* 广告必看的时长,单位为秒
|
||||
*/
|
||||
playDuration?: number
|
||||
|
||||
/**
|
||||
* 广告总的时长,单位为秒
|
||||
*/
|
||||
totalDuration?: number
|
||||
|
||||
/**
|
||||
* 视频广告是否默认静音
|
||||
*/
|
||||
muted?: boolean
|
||||
// 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 artplayerPluginAds {
|
||||
interface Translations {
|
||||
close: string
|
||||
countdown: string
|
||||
detail: string
|
||||
canBeClosed: string
|
||||
}
|
||||
/** Implemented options. Video takes precedence over HTML. */
|
||||
interface Option {
|
||||
html?: string
|
||||
video?: string
|
||||
url?: string
|
||||
/** Seconds before the close button becomes available. @default 5 */
|
||||
playDuration?: number
|
||||
/** Total advertisement duration in seconds. @default 10 */
|
||||
totalDuration?: number
|
||||
/** Initial ad-video mute state. @default false */
|
||||
muted?: boolean
|
||||
/** All four fields replace the default translation object together. */
|
||||
i18n?: Translations
|
||||
}
|
||||
/** Historical published declaration. String durations still fail runtime validation. */
|
||||
interface LegacyOption extends Omit<Option, 'totalDuration'> {
|
||||
/** @deprecated Incorrect in the old declaration; use a numeric duration. */
|
||||
totalDuration?: string
|
||||
}
|
||||
/** Historical unpublished workspace declaration; these fields are not runtime aliases. */
|
||||
interface WorkspaceOption extends Option {
|
||||
/** @deprecated Ignored by the runtime. Use html or video instead. */
|
||||
source: string
|
||||
/** @deprecated Ignored by the runtime. Images are supplied through html. */
|
||||
type: 'video' | 'image' | 'html'
|
||||
}
|
||||
/** Input acceptance for both historical declaration families. */
|
||||
interface CompatOption extends Omit<Option, 'totalDuration'> {
|
||||
/** @deprecated The string branch exists for old types only and is rejected at runtime. */
|
||||
totalDuration?: number | string
|
||||
/** @deprecated Ignored by the runtime. Use html or video instead. */
|
||||
source?: string
|
||||
/** @deprecated Ignored by the runtime. Images are supplied through html. */
|
||||
type?: 'video' | 'image' | 'html'
|
||||
}
|
||||
interface Result {
|
||||
name: 'artplayerPluginAds'
|
||||
/** Complete once; before initialization this cancels the pending preroll. */
|
||||
skip: () => void
|
||||
/** Pause only the countdown, leaving ad video playback unchanged. */
|
||||
pause: () => void
|
||||
/** Resume only the countdown without adding extra timers. */
|
||||
play: () => void
|
||||
}
|
||||
interface Callable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** @deprecated Compatibility with erroneous old string-duration declarations only. */
|
||||
(option: LegacyOption): (art: Artplayer) => Result
|
||||
(option: WorkspaceOption): (art: Artplayer) => Result
|
||||
(option?: CompatOption): (art: Artplayer) => Result
|
||||
/** Required final signature keeps Parameters extraction free of top-level undefined. */
|
||||
(option: CompatOption): (art: Artplayer) => Result
|
||||
}
|
||||
interface Factory extends Callable {
|
||||
/** Same function; supports historical require(package).default calls. */
|
||||
readonly default: Callable
|
||||
}
|
||||
interface RuntimeCallable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
/** Accurate typing for the identical implementation at /runtime. */
|
||||
interface RuntimeFactory extends RuntimeCallable {
|
||||
readonly default: RuntimeCallable
|
||||
}
|
||||
}
|
||||
|
||||
interface Ads {
|
||||
name: 'artplayerPluginAds'
|
||||
|
||||
/**
|
||||
* 跳过广告
|
||||
*/
|
||||
skip: () => void
|
||||
|
||||
/**
|
||||
* 暂停广告
|
||||
*/
|
||||
pause: () => void
|
||||
|
||||
/**
|
||||
* 播放广告
|
||||
*/
|
||||
play: () => void
|
||||
}
|
||||
|
||||
declare const artplayerPluginAds: (option: Option) => (art: Artplayer) => Ads
|
||||
|
||||
export default artplayerPluginAds
|
||||
|
||||
declare const artplayerPluginAds: artplayerPluginAds.Factory
|
||||
export = artplayerPluginAds
|
||||
export as namespace artplayerPluginAds;
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";function e(e){console.warn("Artplayer Ads:",e)}function n(n){const t=[];let i=!1;function l(e,n){if(!i){t.push(n);try{e()}finally{i&&n()}}}return{on(e,t){const a=()=>{i||t()};l(()=>{n.on(e,a)},()=>{n.off(e,a)})},dom(e,n,t){const a=()=>{i||t()};l(()=>e.addEventListener(n,a),()=>e.removeEventListener(n,a))},dispose(){if(!i){i=!0;for(const i of t.splice(0).reverse())try{i()}catch(n){e(n)}}}}}const t=(e,n)=>n.replace("%s",String(e));function i(i,l,a,o){const r=n(i),s=n(i),d=i.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let i,l=0,a=!1,o=!1,r=!1;function s(){a&&o&&!r&&void 0===i&&(i=setTimeout(()=>{i=void 0,!r&&o&&(l+=1,n(l),r||(l>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==i&&clearTimeout(i),i=void 0}return{start(){a||r||(a=!0,o=!0,s())},play(){a&&!r&&(o=!0,s())},pause:d,stop(){r=!0,d()}}}(()=>l.totalDuration,e=>p?.render(e),b),m=()=>"active"===c&&!i.isDestroy,h=()=>"destroyed"===c||i.isDestroy;function x(n){try{n?.pause()}catch(t){e(t)}}function w(n,t,i){try{Promise.resolve(n()).then(()=>{m()||i?.()},t).catch(e)}catch(l){t(l)}}function b(){if("ended"!==c&&"destroyed"!==c&&!i.isDestroy){if(c="ended",v.stop(),r.dispose(),y){if(w(()=>i.play(),n=>{"destroyed"!==c&&e(n)}),h())return;if(x(p?.video),h())return;p?.hide()}h()||i.emit("artplayerPluginAds:skip",l)}}function D(){const n=p?.video;if(x(n),n)try{n.removeAttribute("src"),n.load()}catch(t){e(t)}try{u?.remove()}catch(t){e(t)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),r.dispose(),s.dispose(),D())}function A(){if("waiting"===c&&!i.isDestroy){c="active",y=!0;try{if(p=function(e,n,i,l,a){const{append:o,query:r,setStyle:s}=l,d=o(e,'<div class="artplayer-plugin-ads"></div>');a(d);const u=o(d,i.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${i.html}</div>`),p=i.video?u:null,c=o(d,'<div class="artplayer-plugin-ads-loading"></div>');o(c,n.loading);const y=o(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=r(".artplayer-plugin-ads-close",y),g=r(".artplayer-plugin-ads-countdown",y),v=o(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${i.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=r(".artplayer-plugin-ads-detail",v),h=r(".artplayer-plugin-ads-muted",v),x=r(".artplayer-plugin-ads-fullscreen",v);let w=i.playDuration<=0;function b(){s(n.volume,"display",p?.muted?"none":"inline-flex"),s(n.volumeClose,"display",p?.muted?"inline-flex":"none")}function D(e){const n=i.playDuration-e;w=0===e?i.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=w?i.i18n.close:t(n,i.i18n.canBeClosed),g.innerHTML=t(i.totalDuration-e,i.i18n.countdown)}return i.playDuration>=i.totalDuration&&s(f,"display","none"),i.url||s(m,"display","none"),p?(o(h,n.volume),o(h,n.volumeClose),p.muted=Boolean(i.muted),b()):s(h,"display","none"),o(x,n.fullscreenOn),o(x,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){s(y,"display","flex"),s(v,"display","flex"),s(c,"display","none")},hide(){s(d,"display","none")},fullscreen(e){s(n.fullscreenOn,"display",e?"none":"inline-flex"),s(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{w&&n.skip()}),e.dom(u,"click",n.click),i.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,b()}),e.dom(x,"click",n.fullscreen)}}}(d.$player,a,l,o,e=>{u=e,d.$ads=e}),!m())return void D();if(i.pause(),!m())return;const n=p,s=u.ownerDocument;n.fullscreen(i.fullscreen),n.bind(r,{skip:b,click(){l.url&&(s.defaultView||window).open(l.url),m()&&i.emit("artplayerPluginAds:click",l)},fullscreen(){i.fullscreen=!i.fullscreen,m()&&n.fullscreen(i.fullscreen)}}),r.on("fullscreen",()=>n.fullscreen(i.fullscreen));const c=()=>s.hidden?v.pause():v.play();r.dom(s,"visibilitychange",c),r.on("document:visibilitychange",c);const y=()=>{if(m()&&!g){if(g=!0,v.start(),s.hidden&&v.pause(),n.video){const t=n.video;w(()=>t.play(),n=>{m()&&(e(n),b())},()=>x(t))}m()&&n.ready()}};n.video?(r.dom(n.video,"error",b),r.dom(n.video,"loadedmetadata",y),n.video.src=l.video):y()}catch(n){throw k(),n}}}try{s.on("destroy",k),r.on("ready",()=>{f||"waiting"!==c||(f=!0,r.on("play",A),r.on("video:playing",A))}),i.isDestroy&&k()}catch(C){throw k(),C}return{name:"artplayerPluginAds",skip:b,pause:v.pause,play:v.play}}if("undefined"!==typeof document&&!document.getElementById("artplayer-plugin-ads")){const e=document.createElement("style");e.id="artplayer-plugin-ads",e.textContent=".artplayeLine truncated
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";function e(e){console.warn("Artplayer Ads:",e)}function n(n){const t=[];let i=!1;function l(e,n){if(!i){t.push(n);try{e()}finally{i&&n()}}}return{on(e,t){const a=()=>{i||t()};l(()=>{n.on(e,a)},()=>{n.off(e,a)})},dom(e,n,t){const a=()=>{i||t()};l(()=>e.addEventListener(n,a),()=>e.removeEventListener(n,a))},dispose(){if(!i){i=!0;for(const i of t.splice(0).reverse())try{i()}catch(n){e(n)}}}}}const t=(e,n)=>n.replace("%s",String(e));function i(i,l,a,o){const r=n(i),s=n(i),d=i.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let i,l=0,a=!1,o=!1,r=!1;function s(){a&&o&&!r&&void 0===i&&(i=setTimeout(()=>{i=void 0,!r&&o&&(l+=1,n(l),r||(l>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==i&&clearTimeout(i),i=void 0}return{start(){a||r||(a=!0,o=!0,s())},play(){a&&!r&&(o=!0,s())},pause:d,stop(){r=!0,d()}}}(()=>l.totalDuration,e=>p?.render(e),w),m=()=>"active"===c&&!i.isDestroy,h=()=>"destroyed"===c||i.isDestroy;function x(n){try{n?.pause()}catch(t){e(t)}}function b(n,t,i){try{Promise.resolve(n()).then(()=>{m()||i?.()},t).catch(e)}catch(l){t(l)}}function w(){if("ended"!==c&&"destroyed"!==c&&!i.isDestroy){if(c="ended",v.stop(),r.dispose(),y){if(b(()=>i.play(),n=>{"destroyed"!==c&&e(n)}),h())return;if(x(p?.video),h())return;p?.hide()}h()||i.emit("artplayerPluginAds:skip",l)}}function D(){const n=p?.video;if(x(n),n)try{n.removeAttribute("src"),n.load()}catch(t){e(t)}try{u?.remove()}catch(t){e(t)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),r.dispose(),s.dispose(),D())}function O(){if("waiting"===c&&!i.isDestroy){c="active",y=!0;try{if(p=function(e,n,i,l,a){const{append:o,query:r,setStyle:s}=l,d=o(e,'<div class="artplayer-plugin-ads"></div>');a(d);const u=o(d,i.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${i.html}</div>`),p=i.video?u:null,c=o(d,'<div class="artplayer-plugin-ads-loading"></div>');o(c,n.loading);const y=o(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=r(".artplayer-plugin-ads-close",y),g=r(".artplayer-plugin-ads-countdown",y),v=o(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${i.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=r(".artplayer-plugin-ads-detail",v),h=r(".artplayer-plugin-ads-muted",v),x=r(".artplayer-plugin-ads-fullscreen",v);let b=i.playDuration<=0;function w(){s(n.volume,"display",p?.muted?"none":"inline-flex"),s(n.volumeClose,"display",p?.muted?"inline-flex":"none")}function D(e){const n=i.playDuration-e;b=0===e?i.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=b?i.i18n.close:t(n,i.i18n.canBeClosed),g.innerHTML=t(i.totalDuration-e,i.i18n.countdown)}return i.playDuration>=i.totalDuration&&s(f,"display","none"),i.url||s(m,"display","none"),p?(o(h,n.volume),o(h,n.volumeClose),p.muted=Boolean(i.muted),w()):s(h,"display","none"),o(x,n.fullscreenOn),o(x,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){s(y,"display","flex"),s(v,"display","flex"),s(c,"display","none")},hide(){s(d,"display","none")},fullscreen(e){s(n.fullscreenOn,"display",e?"none":"inline-flex"),s(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{b&&n.skip()}),e.dom(u,"click",n.click),i.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(x,"click",n.fullscreen)}}}(d.$player,a,l,o,e=>{u=e,d.$ads=e}),!m())return void D();if(i.pause(),!m())return;const n=p,s=u.ownerDocument;n.fullscreen(i.fullscreen),n.bind(r,{skip:w,click(){l.url&&(s.defaultView||window).open(l.url),m()&&i.emit("artplayerPluginAds:click",l)},fullscreen(){i.fullscreen=!i.fullscreen,m()&&n.fullscreen(i.fullscreen)}}),r.on("fullscreen",()=>n.fullscreen(i.fullscreen));const c=()=>s.hidden?v.pause():v.play();r.dom(s,"visibilitychange",c),r.on("document:visibilitychange",c);const y=()=>{if(m()&&!g){if(g=!0,v.start(),s.hidden&&v.pause(),n.video){const t=n.video;b(()=>t.play(),n=>{m()&&(e(n),w())},()=>x(t))}m()&&n.ready()}};n.video?(r.dom(n.video,"error",w),r.dom(n.video,"loadedmetadata",y),n.video.src=l.video):y()}catch(n){throw k(),n}}}try{s.on("destroy",k),r.on("ready",()=>{f||"waiting"!==c||(f=!0,r.on("play",O),r.on("video:playing",O))}),i.isDestroy&&k()}catch(A){throw k(),A}return{name:"artplayerPluginAds",skip:w,pause:v.pause,play:v.play}}function l(e){return n=>{const t=n.constructor;if("function"!==typeof t.validator||!["append","query","setStyle"].every(e=>"function"===typeof Reflect.get(t.utils||{},e)))throwLine truncated
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";var e=Object.defineProperty,n=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable,i=(n,l,t)=>l in n?e(n,l,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[l]=t;function r(e,r){return r(((e,r)=>{for(var o in r||(r={}))l.call(r,o)&&i(e,o,r[o]);if(n)for(var o of n(r))t.call(r,o)&&i(e,o,r[o]);return e})({html:"",video:"",url:"",playDuration:5,totalDuration:10,muted:!1,i18n:{close:"\u5173\u95ed\u5e7f\u544a",countdown:"%s\u79d2",detail:"\u67e5\u770b\u8be6\u60c5",canBeClosed:"%s\u79d2\u540e\u53ef\u5173\u95ed\u5e7f\u544a"}},e),{html:"?string",video:"?string",url:"?string",playDuration:"number",totalDuration:"number",muted:"?boolean",i18n:{close:"string",countdown:"string",detail:"string",canBeClosed:"string"}})}function o(e){console.warn("Artplayer Ads:",e)}function a(e){const n=[];let l=!1;function t(e,t){if(!l){n.push(t);try{e()}finally{l&&t()}}}return{on(n,i){const r=()=>{l||i()};t(()=>{e.on(n,r)},()=>{e.off(n,r)})},dom(e,n,i){const r=()=>{l||i()};t(()=>e.addEventListener(n,r),()=>e.removeEventListener(n,r))},dispose(){if(!l){l=!0;for(const l of n.splice(0).reverse())try{l()}catch(e){o(e)}}}}}const s=(e,n)=>n.replace("%s",String(e));function d(e,n,l,t){const i=a(e),r=a(e),d=e.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,l){let t,i=0,r=!1,o=!1,a=!1;function s(){r&&o&&!a&&void 0===t&&(t=setTimeout(()=>{t=void 0,!a&&o&&(i+=1,n(i),a||(i>=e()?l():s()))},1e3))}function d(){o=!1,void 0!==t&&clearTimeout(t),t=void 0}return{start(){r||a||(r=!0,o=!0,s())},play(){r&&!a&&(o=!0,s())},pause:d,stop(){a=!0,d()}}}(()=>n.totalDuration,e=>null==p?void 0:p.render(e),w),m=()=>"active"===c&&!e.isDestroy,h=()=>"destroyed"===c||e.isDestroy;function b(e){try{null==e||e.pause()}catch(n){o(n)}}function x(e,n,l){try{Promise.resolve(e()).then(()=>{m()||null==l||l()},n).catch(o)}catch(t){n(t)}}function w(){if("ended"!==c&&"destroyed"!==c&&!e.isDestroy){if(c="ended",v.stop(),i.dispose(),y){if(x(()=>e.play(),e=>{"destroyed"!==c&&o(e)}),h())return;if(b(null==p?void 0:p.video),h())return;null==p||p.hide()}h()||e.emit("artplayerPluginAds:skip",n)}}function D(){const e=null==p?void 0:p.video;if(b(e),e)try{e.removeAttribute("src"),e.load()}catch(n){o(n)}try{null==u||u.remove()}catch(n){o(n)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),i.dispose(),r.dispose(),D())}function O(){if("waiting"===c&&!e.isDestroy){c="active",y=!0;try{if(p=function(e,n,l,t,i){const{append:r,query:o,setStyle:a}=t,d=r(e,'<div class="artplayer-plugin-ads"></div>');i(d);const u=r(d,l.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${l.html}</div>`),p=l.video?u:null,c=r(d,'<div class="artplayer-plugin-ads-loading"></div>');r(c,n.loading);const y=r(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=o(".artplayer-plugin-ads-close",y),g=o(".artplayer-plugin-ads-countdown",y),v=r(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${l.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=o(".artplayer-plugin-ads-detail",v),h=o(".artplayer-plugin-ads-muted",v),b=o(".artplayer-plugin-ads-fullscreen",v);let x=l.playDuration<=0;function w(){a(n.volume,"display",(null==p?void 0:p.muted)?"none":"inline-flex"),a(n.volumeClose,"display",(null==p?void 0:p.muted)?"inline-flex":"none")}function D(e){const n=l.playDuration-e;x=0===e?l.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=x?l.i18n.close:s(n,l.i18n.canBeClosed),g.innerHTML=s(l.totalDuration-e,l.i18n.countdown)}return l.playDuration>=l.totalDuration&&a(f,"display","none"),l.url||a(m,"display","none"),p?(r(h,n.volume),r(h,n.volumeClose),p.muted=Boolean(l.muted),w()):a(h,"display","none"),r(b,n.fullscreenOn),r(b,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){a(y,"display","flex"),a(v,"display","flex"),a(c,"display","none")},hide(){a(d,"display","none")},fullscreen(e){a(n.fullscreenOn,"display",e?"none":"inline-flex"),a(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{x&&n.skip()}),e.dom(u,"click",n.click),l.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(b,"click",n.fullscreen)}}}(d.$player,l,n,t,e=>{u=e,d.$ads=e}),!m())return void D();if(e.pause(),!m())return;const r=p,a=u.ownerDocument;r.fullscreen(e.fullscreen),r.bind(i,{skip:w,click(){n.url&&(a.defaultView||window).open(n.url),m()&&e.emit("artplayerPluginAds:click",n)},fullscreenLine truncated
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";var e=Object.defineProperty,n=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,l=Object.prototype.propertyIsEnumerable,i=(n,t,l)=>t in n?e(n,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):n[t]=l;function r(e,r){return r(((e,r)=>{for(var o in r||(r={}))t.call(r,o)&&i(e,o,r[o]);if(n)for(var o of n(r))l.call(r,o)&&i(e,o,r[o]);return e})({html:"",video:"",url:"",playDuration:5,totalDuration:10,muted:!1,i18n:{close:"\u5173\u95ed\u5e7f\u544a",countdown:"%s\u79d2",detail:"\u67e5\u770b\u8be6\u60c5",canBeClosed:"%s\u79d2\u540e\u53ef\u5173\u95ed\u5e7f\u544a"}},e),{html:"?string",video:"?string",url:"?string",playDuration:"number",totalDuration:"number",muted:"?boolean",i18n:{close:"string",countdown:"string",detail:"string",canBeClosed:"string"}})}function o(e){console.warn("Artplayer Ads:",e)}function a(e){const n=[];let t=!1;function l(e,l){if(!t){n.push(l);try{e()}finally{t&&l()}}}return{on(n,i){const r=()=>{t||i()};l(()=>{e.on(n,r)},()=>{e.off(n,r)})},dom(e,n,i){const r=()=>{t||i()};l(()=>e.addEventListener(n,r),()=>e.removeEventListener(n,r))},dispose(){if(!t){t=!0;for(const t of n.splice(0).reverse())try{t()}catch(e){o(e)}}}}}const s=(e,n)=>n.replace("%s",String(e));function d(e,n,t,l){const i=a(e),r=a(e),d=e.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let l,i=0,r=!1,o=!1,a=!1;function s(){r&&o&&!a&&void 0===l&&(l=setTimeout(()=>{l=void 0,!a&&o&&(i+=1,n(i),a||(i>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==l&&clearTimeout(l),l=void 0}return{start(){r||a||(r=!0,o=!0,s())},play(){r&&!a&&(o=!0,s())},pause:d,stop(){a=!0,d()}}}(()=>n.totalDuration,e=>null==p?void 0:p.render(e),w),m=()=>"active"===c&&!e.isDestroy,h=()=>"destroyed"===c||e.isDestroy;function b(e){try{null==e||e.pause()}catch(n){o(n)}}function x(e,n,t){try{Promise.resolve(e()).then(()=>{m()||null==t||t()},n).catch(o)}catch(l){n(l)}}function w(){if("ended"!==c&&"destroyed"!==c&&!e.isDestroy){if(c="ended",v.stop(),i.dispose(),y){if(x(()=>e.play(),e=>{"destroyed"!==c&&o(e)}),h())return;if(b(null==p?void 0:p.video),h())return;null==p||p.hide()}h()||e.emit("artplayerPluginAds:skip",n)}}function D(){const e=null==p?void 0:p.video;if(b(e),e)try{e.removeAttribute("src"),e.load()}catch(n){o(n)}try{null==u||u.remove()}catch(n){o(n)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),i.dispose(),r.dispose(),D())}function O(){if("waiting"===c&&!e.isDestroy){c="active",y=!0;try{if(p=function(e,n,t,l,i){const{append:r,query:o,setStyle:a}=l,d=r(e,'<div class="artplayer-plugin-ads"></div>');i(d);const u=r(d,t.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${t.html}</div>`),p=t.video?u:null,c=r(d,'<div class="artplayer-plugin-ads-loading"></div>');r(c,n.loading);const y=r(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=o(".artplayer-plugin-ads-close",y),g=o(".artplayer-plugin-ads-countdown",y),v=r(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${t.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=o(".artplayer-plugin-ads-detail",v),h=o(".artplayer-plugin-ads-muted",v),b=o(".artplayer-plugin-ads-fullscreen",v);let x=t.playDuration<=0;function w(){a(n.volume,"display",(null==p?void 0:p.muted)?"none":"inline-flex"),a(n.volumeClose,"display",(null==p?void 0:p.muted)?"inline-flex":"none")}function D(e){const n=t.playDuration-e;x=0===e?t.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=x?t.i18n.close:s(n,t.i18n.canBeClosed),g.innerHTML=s(t.totalDuration-e,t.i18n.countdown)}return t.playDuration>=t.totalDuration&&a(f,"display","none"),t.url||a(m,"display","none"),p?(r(h,n.volume),r(h,n.volumeClose),p.muted=Boolean(t.muted),w()):a(h,"display","none"),r(b,n.fullscreenOn),r(b,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){a(y,"display","flex"),a(v,"display","flex"),a(c,"display","none")},hide(){a(d,"display","none")},fullscreen(e){a(n.fullscreenOn,"display",e?"none":"inline-flex"),a(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{x&&n.skip()}),e.dom(u,"click",n.click),t.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(b,"click",n.fullscreen)}}}(d.$player,t,n,l,e=>{u=e,d.$ads=e}),!m())return void D();if(e.pause(),!m())return;const r=p,a=u.ownerDocument;r.fullscreen(e.fullscreen),r.bind(i,{skip:w,click(){n.url&&(a.defaultView||window).open(n.url),m()&&e.emit("artplayerPluginAds:click",n)},fullscreenLine truncated
|
||||
@@ -390,6 +390,7 @@ function artplayerPluginAds(input) {
|
||||
return createSession(art, option, { volume, volumeClose, fullscreenOn, fullscreenOff, loading }, constructor.utils);
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginAds, { default: artplayerPluginAds });
|
||||
if (typeof document !== "undefined" && !document.getElementById("artplayer-plugin-ads")) {
|
||||
const element = document.createElement("style");
|
||||
element.id = "artplayer-plugin-ads";
|
||||
@@ -397,5 +398,5 @@ if (typeof document !== "undefined" && !document.getElementById("artplayer-plugi
|
||||
document.head.appendChild(element);
|
||||
}
|
||||
export {
|
||||
artplayerPluginAds as default
|
||||
index as default
|
||||
};
|
||||
+2
-1
@@ -62,7 +62,8 @@
|
||||
"test:package:release": "node scripts/package-check.mjs --release",
|
||||
"build:types": "node scripts/build-types.mjs --write",
|
||||
"check:types": "node scripts/build-types.mjs --check",
|
||||
"test:dash-types-package": "node refactor/scripts/dash-package-types.mjs"
|
||||
"test:dash-types-package": "node refactor/scripts/dash-package-types.mjs",
|
||||
"test:ads-types-package": "node refactor/scripts/ads-package-types.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Ads implementation and maintenance
|
||||
|
||||
Ads is a single preroll attached by `artplayerPluginAds(option)(art)`. The factory and
|
||||
returned `{ name, skip, pause, play }` are synchronous. Package public declarations are
|
||||
still scheduled for PKG-ADS-04; do not infer that their current source/type fields are
|
||||
implemented runtime aliases. Runtime input remains html/video/url plus timing, mute and i18n.
|
||||
returned `{ name, skip, pause, play }` are synchronous. Runtime input remains html/video/url
|
||||
plus timing, mute and i18n. Historical source/type fields are accepted by the compatibility
|
||||
declaration but are not implemented runtime aliases.
|
||||
|
||||
## Module boundaries
|
||||
|
||||
@@ -11,7 +11,7 @@ implemented runtime aliases. Runtime input remains html/video/url plus timing, m
|
||||
| --- | --- |
|
||||
| src/index.ts | Public factory, checked legacy constructor capabilities, import-time style injection |
|
||||
| src/options.ts | Per-attachment defaults and the existing shallow validator schema |
|
||||
| src/types.ts | Internal options, result and narrow core/DOM dependencies; public types remain separate for now |
|
||||
| src/types.ts | Normalized options and narrow core/DOM dependencies, reusing public input/result shapes |
|
||||
| src/countdown.ts | One owned timeout, whole-second counting, pause/resume and terminal cancellation; no core or DOM dependency |
|
||||
| src/view.ts | Existing HTML/CSS hooks, labels, ad element, mute and fullscreen icon rendering |
|
||||
| src/resources.ts | Instance-owned core/DOM subscriptions, guarded callbacks and disposal that continues after cleanup failures |
|
||||
@@ -22,6 +22,33 @@ only a parent, icons, options, three DOM utilities and callbacks, rather than th
|
||||
Style remains in src/style.less. There are no runtime imports from the ArtPlayer core package;
|
||||
all imports of its declarations are erased. Old cores do not need new lifecycle helpers.
|
||||
|
||||
## Public declarations and imports
|
||||
|
||||
`types/artplayer-plugin-ads.d.ts` owns the callable namespace and public shapes. CommonJS
|
||||
bridges use `.d.cts`; ESM bridges use `.d.mts`. Root and `/legacy` accept historical inputs.
|
||||
The new `/runtime` entry resolves to the same JS implementation and restricts options to
|
||||
implemented fields. `Option` is accurate, `LegacyOption` preserves the published string
|
||||
duration declaration error, and `WorkspaceOption` represents the unpublished source/type
|
||||
declaration. `CompatOption` accepts both families. No runtime duration coercion was added.
|
||||
The existing validator rejects string durations before the single localized normalization
|
||||
assertion in options.ts. Translation objects still replace all four fields together.
|
||||
|
||||
The factory has a self-referencing `.default`, supporting both callable require and the
|
||||
published `require(package).default` access. This does not emulate the old noncallable
|
||||
CommonJS namespace object's identity or key set. The returned plugin and its lifecycle
|
||||
remain unchanged. Root and `/runtime` resolve to the same module for each module system.
|
||||
|
||||
The editor generator in scripts/plugin-editor-types.mjs accepts this type-only namespace
|
||||
shape explicitly and rejects unknown imports/exports. Generate docs globals with
|
||||
`yarn build:ts`; test their semantics and actual Monaco emit/run, not only parseability.
|
||||
|
||||
ADS-TYPE-01 is accepted with scope by the maintainer on 2026-09-12: `Parameters<typeof factory>[0]` accepts both old option
|
||||
objects, but reading totalDuration yields `number | string | undefined`; the published
|
||||
declaration yielded `string | undefined`. Workspace source/type fields also become optional.
|
||||
Frozen consumers reproduce these differences. This approval covers only these historical
|
||||
inference corrections, not general API changes. Preserve the diagnostic cases and migration
|
||||
examples in the README; do not claim every historical TS program compiles unchanged.
|
||||
|
||||
## State and ordering
|
||||
|
||||
`waiting -> active -> ended`, with any state able to become `destroyed`. Ready arms the two
|
||||
@@ -78,6 +105,9 @@ generates main/legacy/ESM through the repository build. Do not hand-edit dist or
|
||||
|
||||
Tests distinguish actual media decoding from injected errors/controlled clocks. Baseline and
|
||||
open release gates live in [Ads validation](../../refactor/ads-validation.md). Public declaration
|
||||
reconciliation, old CommonJS default compatibility, isolated package consumption and demo
|
||||
validation remain PKG-ADS-04/06. Real hidden-page/device behavior and complete media-resource
|
||||
reconciliation is recorded in PKG-ADS-04; complete historical distribution and demo validation
|
||||
remain PKG-ADS-06. `yarn test:ads-types-package` packs and installs outside the workspace,
|
||||
checks all installed bytes, frozen offline install, five compiler modes and Node exports.
|
||||
`yarn test:browser test/browser/ads-editor-types.spec.js` verifies actual Monaco integration.
|
||||
Real hidden-page/device behavior and complete media-resource
|
||||
acceptance remain PKG-ADS-05. No claim of compatibility with every historical 4.x core is made.
|
||||
@@ -5,6 +5,57 @@ Ads plugin for ArtPlayer
|
||||
Implementation, resource ownership and regression commands:
|
||||
[maintenance guide](ARCHITECTURE.md).
|
||||
|
||||
## Usage and TypeScript
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer'
|
||||
import ads from 'artplayer-plugin-ads'
|
||||
|
||||
new Artplayer({
|
||||
container: '#player',
|
||||
url: '/content.mp4',
|
||||
plugins: [ads({ video: '/advertisement.mp4', url: '/details', playDuration: 5, totalDuration: 10 })],
|
||||
})
|
||||
```
|
||||
|
||||
Use `html` for HTML or an image element string. When both are present, `video` takes
|
||||
precedence. `playDuration` controls when closing is allowed; `totalDuration` controls
|
||||
the countdown. Durations are numeric seconds. Plugin `play()` and `pause()` control
|
||||
only the countdown; `skip()` completes the advertisement once. All three return void.
|
||||
|
||||
For accurate options without historical declaration mistakes, import the same runtime
|
||||
from `artplayer-plugin-ads/runtime`. Root and `/legacy` retain old input acceptance:
|
||||
the old `totalDuration: string` declaration compiles but strings still fail runtime
|
||||
validation; old workspace `source/type` fields are ignored. Use `video` or `html`.
|
||||
An `i18n` object must supply `close`, `countdown`, `detail` and `canBeClosed` together.
|
||||
|
||||
CommonJS supports both `require('artplayer-plugin-ads')(options)` and
|
||||
`require('artplayer-plugin-ads').default(options)`. ESM uses the default export.
|
||||
## Migrating inferred option types
|
||||
|
||||
The maintainer approved correcting conflicting historical inference on 2026-09-12.
|
||||
Existing option calls remain accepted, but `Parameters<typeof ads>[0].totalDuration`
|
||||
now reads as `number | string | undefined`; historical workspace `source/type` are
|
||||
optional. Code that assumed only one old declaration's field types may need adjustment.
|
||||
This does not make string durations or source/type runtime aliases valid.
|
||||
|
||||
Prefer accurate options for new or migrated code:
|
||||
|
||||
```ts
|
||||
import type { Option } from 'artplayer-plugin-ads'
|
||||
import ads from 'artplayer-plugin-ads/runtime'
|
||||
|
||||
const option: Option = { video: '/advertisement.mp4', totalDuration: 10 }
|
||||
const duration: number | undefined = option.totalDuration
|
||||
const plugin = ads(option)
|
||||
```
|
||||
|
||||
For a generic wrapper receiving historical input, narrow `totalDuration` before numeric
|
||||
operations and check `source/type` before reading them as required fields. Do not use a
|
||||
cast to hide string durations: the runtime validator rejects them. `LegacyOption` and
|
||||
`WorkspaceOption` remain available for code explicitly representing those historical
|
||||
declarations; `Option` describes the implemented configuration.
|
||||
|
||||
## Demo
|
||||
|
||||
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-ads/index.js&example=ads)
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";function e(e){console.warn("Artplayer Ads:",e)}function n(n){const t=[];let i=!1;function l(e,n){if(!i){t.push(n);try{e()}finally{i&&n()}}}return{on(e,t){const a=()=>{i||t()};l(()=>{n.on(e,a)},()=>{n.off(e,a)})},dom(e,n,t){const a=()=>{i||t()};l(()=>e.addEventListener(n,a),()=>e.removeEventListener(n,a))},dispose(){if(!i){i=!0;for(const i of t.splice(0).reverse())try{i()}catch(n){e(n)}}}}}const t=(e,n)=>n.replace("%s",String(e));function i(i,l,a,o){const r=n(i),s=n(i),d=i.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let i,l=0,a=!1,o=!1,r=!1;function s(){a&&o&&!r&&void 0===i&&(i=setTimeout(()=>{i=void 0,!r&&o&&(l+=1,n(l),r||(l>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==i&&clearTimeout(i),i=void 0}return{start(){a||r||(a=!0,o=!0,s())},play(){a&&!r&&(o=!0,s())},pause:d,stop(){r=!0,d()}}}(()=>l.totalDuration,e=>p?.render(e),b),m=()=>"active"===c&&!i.isDestroy,h=()=>"destroyed"===c||i.isDestroy;function x(n){try{n?.pause()}catch(t){e(t)}}function w(n,t,i){try{Promise.resolve(n()).then(()=>{m()||i?.()},t).catch(e)}catch(l){t(l)}}function b(){if("ended"!==c&&"destroyed"!==c&&!i.isDestroy){if(c="ended",v.stop(),r.dispose(),y){if(w(()=>i.play(),n=>{"destroyed"!==c&&e(n)}),h())return;if(x(p?.video),h())return;p?.hide()}h()||i.emit("artplayerPluginAds:skip",l)}}function D(){const n=p?.video;if(x(n),n)try{n.removeAttribute("src"),n.load()}catch(t){e(t)}try{u?.remove()}catch(t){e(t)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),r.dispose(),s.dispose(),D())}function A(){if("waiting"===c&&!i.isDestroy){c="active",y=!0;try{if(p=function(e,n,i,l,a){const{append:o,query:r,setStyle:s}=l,d=o(e,'<div class="artplayer-plugin-ads"></div>');a(d);const u=o(d,i.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${i.html}</div>`),p=i.video?u:null,c=o(d,'<div class="artplayer-plugin-ads-loading"></div>');o(c,n.loading);const y=o(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=r(".artplayer-plugin-ads-close",y),g=r(".artplayer-plugin-ads-countdown",y),v=o(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${i.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=r(".artplayer-plugin-ads-detail",v),h=r(".artplayer-plugin-ads-muted",v),x=r(".artplayer-plugin-ads-fullscreen",v);let w=i.playDuration<=0;function b(){s(n.volume,"display",p?.muted?"none":"inline-flex"),s(n.volumeClose,"display",p?.muted?"inline-flex":"none")}function D(e){const n=i.playDuration-e;w=0===e?i.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=w?i.i18n.close:t(n,i.i18n.canBeClosed),g.innerHTML=t(i.totalDuration-e,i.i18n.countdown)}return i.playDuration>=i.totalDuration&&s(f,"display","none"),i.url||s(m,"display","none"),p?(o(h,n.volume),o(h,n.volumeClose),p.muted=Boolean(i.muted),b()):s(h,"display","none"),o(x,n.fullscreenOn),o(x,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){s(y,"display","flex"),s(v,"display","flex"),s(c,"display","none")},hide(){s(d,"display","none")},fullscreen(e){s(n.fullscreenOn,"display",e?"none":"inline-flex"),s(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{w&&n.skip()}),e.dom(u,"click",n.click),i.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,b()}),e.dom(x,"click",n.fullscreen)}}}(d.$player,a,l,o,e=>{u=e,d.$ads=e}),!m())return void D();if(i.pause(),!m())return;const n=p,s=u.ownerDocument;n.fullscreen(i.fullscreen),n.bind(r,{skip:b,click(){l.url&&(s.defaultView||window).open(l.url),m()&&i.emit("artplayerPluginAds:click",l)},fullscreen(){i.fullscreen=!i.fullscreen,m()&&n.fullscreen(i.fullscreen)}}),r.on("fullscreen",()=>n.fullscreen(i.fullscreen));const c=()=>s.hidden?v.pause():v.play();r.dom(s,"visibilitychange",c),r.on("document:visibilitychange",c);const y=()=>{if(m()&&!g){if(g=!0,v.start(),s.hidden&&v.pause(),n.video){const t=n.video;w(()=>t.play(),n=>{m()&&(e(n),b())},()=>x(t))}m()&&n.ready()}};n.video?(r.dom(n.video,"error",b),r.dom(n.video,"loadedmetadata",y),n.video.src=l.video):y()}catch(n){throw k(),n}}}try{s.on("destroy",k),r.on("ready",()=>{f||"waiting"!==c||(f=!0,r.on("play",A),r.on("video:playing",A))}),i.isDestroy&&k()}catch(C){throw k(),C}return{name:"artplayerPluginAds",skip:b,pause:v.pause,play:v.play}}if("undefined"!==typeof document&&!document.getElementById("artplayer-plugin-ads")){const e=document.createElement("style");e.id="artplayer-plugin-ads",e.textContent=".artplayeLine truncated
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";function e(e){console.warn("Artplayer Ads:",e)}function n(n){const t=[];let i=!1;function l(e,n){if(!i){t.push(n);try{e()}finally{i&&n()}}}return{on(e,t){const a=()=>{i||t()};l(()=>{n.on(e,a)},()=>{n.off(e,a)})},dom(e,n,t){const a=()=>{i||t()};l(()=>e.addEventListener(n,a),()=>e.removeEventListener(n,a))},dispose(){if(!i){i=!0;for(const i of t.splice(0).reverse())try{i()}catch(n){e(n)}}}}}const t=(e,n)=>n.replace("%s",String(e));function i(i,l,a,o){const r=n(i),s=n(i),d=i.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let i,l=0,a=!1,o=!1,r=!1;function s(){a&&o&&!r&&void 0===i&&(i=setTimeout(()=>{i=void 0,!r&&o&&(l+=1,n(l),r||(l>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==i&&clearTimeout(i),i=void 0}return{start(){a||r||(a=!0,o=!0,s())},play(){a&&!r&&(o=!0,s())},pause:d,stop(){r=!0,d()}}}(()=>l.totalDuration,e=>p?.render(e),w),m=()=>"active"===c&&!i.isDestroy,h=()=>"destroyed"===c||i.isDestroy;function x(n){try{n?.pause()}catch(t){e(t)}}function b(n,t,i){try{Promise.resolve(n()).then(()=>{m()||i?.()},t).catch(e)}catch(l){t(l)}}function w(){if("ended"!==c&&"destroyed"!==c&&!i.isDestroy){if(c="ended",v.stop(),r.dispose(),y){if(b(()=>i.play(),n=>{"destroyed"!==c&&e(n)}),h())return;if(x(p?.video),h())return;p?.hide()}h()||i.emit("artplayerPluginAds:skip",l)}}function D(){const n=p?.video;if(x(n),n)try{n.removeAttribute("src"),n.load()}catch(t){e(t)}try{u?.remove()}catch(t){e(t)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),r.dispose(),s.dispose(),D())}function O(){if("waiting"===c&&!i.isDestroy){c="active",y=!0;try{if(p=function(e,n,i,l,a){const{append:o,query:r,setStyle:s}=l,d=o(e,'<div class="artplayer-plugin-ads"></div>');a(d);const u=o(d,i.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${i.html}</div>`),p=i.video?u:null,c=o(d,'<div class="artplayer-plugin-ads-loading"></div>');o(c,n.loading);const y=o(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=r(".artplayer-plugin-ads-close",y),g=r(".artplayer-plugin-ads-countdown",y),v=o(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${i.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=r(".artplayer-plugin-ads-detail",v),h=r(".artplayer-plugin-ads-muted",v),x=r(".artplayer-plugin-ads-fullscreen",v);let b=i.playDuration<=0;function w(){s(n.volume,"display",p?.muted?"none":"inline-flex"),s(n.volumeClose,"display",p?.muted?"inline-flex":"none")}function D(e){const n=i.playDuration-e;b=0===e?i.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=b?i.i18n.close:t(n,i.i18n.canBeClosed),g.innerHTML=t(i.totalDuration-e,i.i18n.countdown)}return i.playDuration>=i.totalDuration&&s(f,"display","none"),i.url||s(m,"display","none"),p?(o(h,n.volume),o(h,n.volumeClose),p.muted=Boolean(i.muted),w()):s(h,"display","none"),o(x,n.fullscreenOn),o(x,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){s(y,"display","flex"),s(v,"display","flex"),s(c,"display","none")},hide(){s(d,"display","none")},fullscreen(e){s(n.fullscreenOn,"display",e?"none":"inline-flex"),s(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{b&&n.skip()}),e.dom(u,"click",n.click),i.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(x,"click",n.fullscreen)}}}(d.$player,a,l,o,e=>{u=e,d.$ads=e}),!m())return void D();if(i.pause(),!m())return;const n=p,s=u.ownerDocument;n.fullscreen(i.fullscreen),n.bind(r,{skip:w,click(){l.url&&(s.defaultView||window).open(l.url),m()&&i.emit("artplayerPluginAds:click",l)},fullscreen(){i.fullscreen=!i.fullscreen,m()&&n.fullscreen(i.fullscreen)}}),r.on("fullscreen",()=>n.fullscreen(i.fullscreen));const c=()=>s.hidden?v.pause():v.play();r.dom(s,"visibilitychange",c),r.on("document:visibilitychange",c);const y=()=>{if(m()&&!g){if(g=!0,v.start(),s.hidden&&v.pause(),n.video){const t=n.video;b(()=>t.play(),n=>{m()&&(e(n),w())},()=>x(t))}m()&&n.ready()}};n.video?(r.dom(n.video,"error",w),r.dom(n.video,"loadedmetadata",y),n.video.src=l.video):y()}catch(n){throw k(),n}}}try{s.on("destroy",k),r.on("ready",()=>{f||"waiting"!==c||(f=!0,r.on("play",O),r.on("video:playing",O))}),i.isDestroy&&k()}catch(A){throw k(),A}return{name:"artplayerPluginAds",skip:w,pause:v.pause,play:v.play}}function l(e){return n=>{const t=n.constructor;if("function"!==typeof t.validator||!["append","query","setStyle"].every(e=>"function"===typeof Reflect.get(t.utils||{},e)))throwLine truncated
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";var e=Object.defineProperty,n=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable,i=(n,l,t)=>l in n?e(n,l,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[l]=t;function r(e,r){return r(((e,r)=>{for(var o in r||(r={}))l.call(r,o)&&i(e,o,r[o]);if(n)for(var o of n(r))t.call(r,o)&&i(e,o,r[o]);return e})({html:"",video:"",url:"",playDuration:5,totalDuration:10,muted:!1,i18n:{close:"\u5173\u95ed\u5e7f\u544a",countdown:"%s\u79d2",detail:"\u67e5\u770b\u8be6\u60c5",canBeClosed:"%s\u79d2\u540e\u53ef\u5173\u95ed\u5e7f\u544a"}},e),{html:"?string",video:"?string",url:"?string",playDuration:"number",totalDuration:"number",muted:"?boolean",i18n:{close:"string",countdown:"string",detail:"string",canBeClosed:"string"}})}function o(e){console.warn("Artplayer Ads:",e)}function a(e){const n=[];let l=!1;function t(e,t){if(!l){n.push(t);try{e()}finally{l&&t()}}}return{on(n,i){const r=()=>{l||i()};t(()=>{e.on(n,r)},()=>{e.off(n,r)})},dom(e,n,i){const r=()=>{l||i()};t(()=>e.addEventListener(n,r),()=>e.removeEventListener(n,r))},dispose(){if(!l){l=!0;for(const l of n.splice(0).reverse())try{l()}catch(e){o(e)}}}}}const s=(e,n)=>n.replace("%s",String(e));function d(e,n,l,t){const i=a(e),r=a(e),d=e.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,l){let t,i=0,r=!1,o=!1,a=!1;function s(){r&&o&&!a&&void 0===t&&(t=setTimeout(()=>{t=void 0,!a&&o&&(i+=1,n(i),a||(i>=e()?l():s()))},1e3))}function d(){o=!1,void 0!==t&&clearTimeout(t),t=void 0}return{start(){r||a||(r=!0,o=!0,s())},play(){r&&!a&&(o=!0,s())},pause:d,stop(){a=!0,d()}}}(()=>n.totalDuration,e=>null==p?void 0:p.render(e),w),m=()=>"active"===c&&!e.isDestroy,h=()=>"destroyed"===c||e.isDestroy;function b(e){try{null==e||e.pause()}catch(n){o(n)}}function x(e,n,l){try{Promise.resolve(e()).then(()=>{m()||null==l||l()},n).catch(o)}catch(t){n(t)}}function w(){if("ended"!==c&&"destroyed"!==c&&!e.isDestroy){if(c="ended",v.stop(),i.dispose(),y){if(x(()=>e.play(),e=>{"destroyed"!==c&&o(e)}),h())return;if(b(null==p?void 0:p.video),h())return;null==p||p.hide()}h()||e.emit("artplayerPluginAds:skip",n)}}function D(){const e=null==p?void 0:p.video;if(b(e),e)try{e.removeAttribute("src"),e.load()}catch(n){o(n)}try{null==u||u.remove()}catch(n){o(n)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),i.dispose(),r.dispose(),D())}function O(){if("waiting"===c&&!e.isDestroy){c="active",y=!0;try{if(p=function(e,n,l,t,i){const{append:r,query:o,setStyle:a}=t,d=r(e,'<div class="artplayer-plugin-ads"></div>');i(d);const u=r(d,l.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${l.html}</div>`),p=l.video?u:null,c=r(d,'<div class="artplayer-plugin-ads-loading"></div>');r(c,n.loading);const y=r(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=o(".artplayer-plugin-ads-close",y),g=o(".artplayer-plugin-ads-countdown",y),v=r(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${l.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=o(".artplayer-plugin-ads-detail",v),h=o(".artplayer-plugin-ads-muted",v),b=o(".artplayer-plugin-ads-fullscreen",v);let x=l.playDuration<=0;function w(){a(n.volume,"display",(null==p?void 0:p.muted)?"none":"inline-flex"),a(n.volumeClose,"display",(null==p?void 0:p.muted)?"inline-flex":"none")}function D(e){const n=l.playDuration-e;x=0===e?l.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=x?l.i18n.close:s(n,l.i18n.canBeClosed),g.innerHTML=s(l.totalDuration-e,l.i18n.countdown)}return l.playDuration>=l.totalDuration&&a(f,"display","none"),l.url||a(m,"display","none"),p?(r(h,n.volume),r(h,n.volumeClose),p.muted=Boolean(l.muted),w()):a(h,"display","none"),r(b,n.fullscreenOn),r(b,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){a(y,"display","flex"),a(v,"display","flex"),a(c,"display","none")},hide(){a(d,"display","none")},fullscreen(e){a(n.fullscreenOn,"display",e?"none":"inline-flex"),a(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{x&&n.skip()}),e.dom(u,"click",n.click),l.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(b,"click",n.fullscreen)}}}(d.$player,l,n,t,e=>{u=e,d.$ads=e}),!m())return void D();if(e.pause(),!m())return;const r=p,a=u.ownerDocument;r.fullscreen(e.fullscreen),r.bind(i,{skip:w,click(){n.url&&(a.defaultView||window).open(n.url),m()&&e.emit("artplayerPluginAds:click",n)},fullscreenLine truncated
|
||||
!function(e,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(e.artplayerPluginAds=n(),define(function(){return e.artplayerPluginAds})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginAds=n()}(this,function(){"use strict";var e=Object.defineProperty,n=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,l=Object.prototype.propertyIsEnumerable,i=(n,t,l)=>t in n?e(n,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):n[t]=l;function r(e,r){return r(((e,r)=>{for(var o in r||(r={}))t.call(r,o)&&i(e,o,r[o]);if(n)for(var o of n(r))l.call(r,o)&&i(e,o,r[o]);return e})({html:"",video:"",url:"",playDuration:5,totalDuration:10,muted:!1,i18n:{close:"\u5173\u95ed\u5e7f\u544a",countdown:"%s\u79d2",detail:"\u67e5\u770b\u8be6\u60c5",canBeClosed:"%s\u79d2\u540e\u53ef\u5173\u95ed\u5e7f\u544a"}},e),{html:"?string",video:"?string",url:"?string",playDuration:"number",totalDuration:"number",muted:"?boolean",i18n:{close:"string",countdown:"string",detail:"string",canBeClosed:"string"}})}function o(e){console.warn("Artplayer Ads:",e)}function a(e){const n=[];let t=!1;function l(e,l){if(!t){n.push(l);try{e()}finally{t&&l()}}}return{on(n,i){const r=()=>{t||i()};l(()=>{e.on(n,r)},()=>{e.off(n,r)})},dom(e,n,i){const r=()=>{t||i()};l(()=>e.addEventListener(n,r),()=>e.removeEventListener(n,r))},dispose(){if(!t){t=!0;for(const t of n.splice(0).reverse())try{t()}catch(e){o(e)}}}}}const s=(e,n)=>n.replace("%s",String(e));function d(e,n,t,l){const i=a(e),r=a(e),d=e.template;let u,p,c="waiting",y=!1,f=!1,g=!1;const v=function(e,n,t){let l,i=0,r=!1,o=!1,a=!1;function s(){r&&o&&!a&&void 0===l&&(l=setTimeout(()=>{l=void 0,!a&&o&&(i+=1,n(i),a||(i>=e()?t():s()))},1e3))}function d(){o=!1,void 0!==l&&clearTimeout(l),l=void 0}return{start(){r||a||(r=!0,o=!0,s())},play(){r&&!a&&(o=!0,s())},pause:d,stop(){a=!0,d()}}}(()=>n.totalDuration,e=>null==p?void 0:p.render(e),w),m=()=>"active"===c&&!e.isDestroy,h=()=>"destroyed"===c||e.isDestroy;function b(e){try{null==e||e.pause()}catch(n){o(n)}}function x(e,n,t){try{Promise.resolve(e()).then(()=>{m()||null==t||t()},n).catch(o)}catch(l){n(l)}}function w(){if("ended"!==c&&"destroyed"!==c&&!e.isDestroy){if(c="ended",v.stop(),i.dispose(),y){if(x(()=>e.play(),e=>{"destroyed"!==c&&o(e)}),h())return;if(b(null==p?void 0:p.video),h())return;null==p||p.hide()}h()||e.emit("artplayerPluginAds:skip",n)}}function D(){const e=null==p?void 0:p.video;if(b(e),e)try{e.removeAttribute("src"),e.load()}catch(n){o(n)}try{null==u||u.remove()}catch(n){o(n)}finally{d.$ads===u&&delete d.$ads}}function k(){"destroyed"!==c&&(c="destroyed",v.stop(),i.dispose(),r.dispose(),D())}function O(){if("waiting"===c&&!e.isDestroy){c="active",y=!0;try{if(p=function(e,n,t,l,i){const{append:r,query:o,setStyle:a}=l,d=r(e,'<div class="artplayer-plugin-ads"></div>');i(d);const u=r(d,t.video?'<video class="artplayer-plugin-ads-video" loop playsInline></video>':`<div class="artplayer-plugin-ads-html">${t.html}</div>`),p=t.video?u:null,c=r(d,'<div class="artplayer-plugin-ads-loading"></div>');r(c,n.loading);const y=r(d,'<div class="artplayer-plugin-ads-timer">\n <div class="artplayer-plugin-ads-close"></div>\n <div class="artplayer-plugin-ads-countdown"></div>\n </div>'),f=o(".artplayer-plugin-ads-close",y),g=o(".artplayer-plugin-ads-countdown",y),v=r(d,`<div class="artplayer-plugin-ads-control">\n <div class="artplayer-plugin-ads-detail">${t.i18n.detail}</div>\n <div class="artplayer-plugin-ads-muted"></div>\n <div class="artplayer-plugin-ads-fullscreen"></div>\n </div>`),m=o(".artplayer-plugin-ads-detail",v),h=o(".artplayer-plugin-ads-muted",v),b=o(".artplayer-plugin-ads-fullscreen",v);let x=t.playDuration<=0;function w(){a(n.volume,"display",(null==p?void 0:p.muted)?"none":"inline-flex"),a(n.volumeClose,"display",(null==p?void 0:p.muted)?"inline-flex":"none")}function D(e){const n=t.playDuration-e;x=0===e?t.playDuration<=0:n<1||Number.isNaN(n),f.innerHTML=x?t.i18n.close:s(n,t.i18n.canBeClosed),g.innerHTML=s(t.totalDuration-e,t.i18n.countdown)}return t.playDuration>=t.totalDuration&&a(f,"display","none"),t.url||a(m,"display","none"),p?(r(h,n.volume),r(h,n.volumeClose),p.muted=Boolean(t.muted),w()):a(h,"display","none"),r(b,n.fullscreenOn),r(b,n.fullscreenOff),D(0),{root:d,video:p,render:D,ready(){a(y,"display","flex"),a(v,"display","flex"),a(c,"display","none")},hide(){a(d,"display","none")},fullscreen(e){a(n.fullscreenOn,"display",e?"none":"inline-flex"),a(n.fullscreenOff,"display",e?"inline-flex":"none")},bind(e,n){e.dom(f,"click",()=>{x&&n.skip()}),e.dom(u,"click",n.click),t.url&&e.dom(m,"click",n.click),p&&e.dom(h,"click",()=>{p.muted=!p.muted,w()}),e.dom(b,"click",n.fullscreen)}}}(d.$player,t,n,l,e=>{u=e,d.$ads=e}),!m())return void D();if(e.pause(),!m())return;const r=p,a=u.ownerDocument;r.fullscreen(e.fullscreen),r.bind(i,{skip:w,click(){n.url&&(a.defaultView||window).open(n.url),m()&&e.emit("artplayerPluginAds:click",n)},fullscreenLine truncated
|
||||
@@ -390,6 +390,7 @@ function artplayerPluginAds(input) {
|
||||
return createSession(art, option, { volume, volumeClose, fullscreenOn, fullscreenOff, loading }, constructor.utils);
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginAds, { default: artplayerPluginAds });
|
||||
if (typeof document !== "undefined" && !document.getElementById("artplayer-plugin-ads")) {
|
||||
const element = document.createElement("style");
|
||||
element.id = "artplayer-plugin-ads";
|
||||
@@ -397,5 +398,5 @@ if (typeof document !== "undefined" && !document.getElementById("artplayer-plugi
|
||||
document.head.appendChild(element);
|
||||
}
|
||||
export {
|
||||
artplayerPluginAds as default
|
||||
index as default
|
||||
};
|
||||
@@ -19,19 +19,49 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-plugin-ads.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ads.mjs",
|
||||
"require": "./dist/artplayer-plugin-ads.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ads.d.mts",
|
||||
"default": "./dist/artplayer-plugin-ads.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ads.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ads.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-plugin-ads.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ads.legacy.js",
|
||||
"require": "./dist/artplayer-plugin-ads.legacy.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ads.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ads.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ads.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ads.legacy.js"
|
||||
}
|
||||
},
|
||||
"./runtime": {
|
||||
"import": {
|
||||
"types": "./types/runtime/index.d.mts",
|
||||
"default": "./dist/artplayer-plugin-ads.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/runtime/index.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ads.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-plugin-ads.js",
|
||||
"module": "./dist/artplayer-plugin-ads.mjs",
|
||||
"types": "./types/artplayer-plugin-ads.d.ts",
|
||||
"legacy": "./dist/artplayer-plugin-ads.legacy.js",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-plugin-ads.d.ts"
|
||||
],
|
||||
"runtime": [
|
||||
"types/runtime/index.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { normalizeOptions } from './options'
|
||||
import { createSession } from './session'
|
||||
import style from './style.less?inline'
|
||||
|
||||
export default function artplayerPluginAds(input?: Input) {
|
||||
function artplayerPluginAds(input?: Input) {
|
||||
return (art: Artplayer): Result => {
|
||||
// Legacy core declarations type instance.constructor as Function. Check the
|
||||
// actual capabilities before using its existing static validator/utilities.
|
||||
@@ -17,6 +17,8 @@ export default function artplayerPluginAds(input?: Input) {
|
||||
}
|
||||
}
|
||||
|
||||
export default Object.assign(artplayerPluginAds, { default: artplayerPluginAds })
|
||||
|
||||
if (typeof document !== 'undefined' && !document.getElementById('artplayer-plugin-ads')) {
|
||||
const element = document.createElement('style')
|
||||
element.id = 'artplayer-plugin-ads'
|
||||
|
||||
@@ -2,6 +2,8 @@ import type Artplayer from 'artplayer'
|
||||
import type { Input, Options } from './types'
|
||||
|
||||
export function normalizeOptions(input: Input | undefined, validate: typeof Artplayer.validator): Options {
|
||||
// The legacy declaration accepts strings, but this unchanged validator proves
|
||||
// numeric runtime durations before the result crosses the declaration boundary.
|
||||
return validate({
|
||||
html: '',
|
||||
video: '',
|
||||
@@ -24,5 +26,5 @@ export function normalizeOptions(input: Input | undefined, validate: typeof Artp
|
||||
totalDuration: 'number',
|
||||
muted: '?boolean',
|
||||
i18n: { close: 'string', countdown: 'string', detail: 'string', canBeClosed: 'string' },
|
||||
})
|
||||
}) as Options
|
||||
}
|
||||
@@ -1,31 +1,18 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
import type { CompatOption, Option, Result as PublicResult, Translations as PublicTranslations } from '../types/artplayer-plugin-ads'
|
||||
|
||||
export interface Translations {
|
||||
close: string
|
||||
countdown: string
|
||||
detail: string
|
||||
canBeClosed: string
|
||||
}
|
||||
export type Translations = PublicTranslations
|
||||
|
||||
export interface Options {
|
||||
html?: string
|
||||
video?: string
|
||||
url?: string
|
||||
export interface Options extends Option {
|
||||
playDuration: number
|
||||
totalDuration: number
|
||||
muted?: boolean
|
||||
i18n: Translations
|
||||
}
|
||||
|
||||
export type Input = Partial<Options>
|
||||
export type Input = CompatOption
|
||||
export type Host = Pick<Artplayer, 'on' | 'off' | 'emit' | 'play' | 'pause' | 'template' | 'icons' | 'fullscreen' | 'isDestroy'>
|
||||
export type Utilities = Pick<typeof Artplayer.utils, 'append' | 'query' | 'setStyle'>
|
||||
export type Icons = Pick<Artplayer['icons'], 'volume' | 'volumeClose' | 'fullscreenOn' | 'fullscreenOff' | 'loading'>
|
||||
export type Cleanup = () => void
|
||||
|
||||
export interface Result {
|
||||
name: 'artplayerPluginAds'
|
||||
skip: Cleanup
|
||||
pause: Cleanup
|
||||
play: Cleanup
|
||||
}
|
||||
export type Result = PublicResult
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": { "allowJs": false },
|
||||
"include": ["src/**/*.ts", "types/**/*.d.ts", "../../types/assets.d.ts"]
|
||||
"include": ["src/**/*.ts", "types/**/*.d.ts", "types/**/*.d.mts", "types/**/*.d.cts", "../../types/assets.d.ts"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
|
||||
import artplayerPluginAds = require('./artplayer-plugin-ads.js')
|
||||
|
||||
export = artplayerPluginAds
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from './artplayer-plugin-ads.js'
|
||||
export type { Callable, CompatOption, Factory, LegacyOption, Option, Result, RuntimeCallable, RuntimeFactory, Translations, WorkspaceOption } from './artplayer-plugin-ads.js'
|
||||
+81
-43
@@ -1,51 +1,89 @@
|
||||
/* eslint-disable ts/no-redeclare -- The callable export and public type namespace intentionally merge. */
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
interface Option {
|
||||
/**
|
||||
* 广告源文本,支持视频链接、图片链接、HTML文本
|
||||
*/
|
||||
source: string
|
||||
declare namespace artplayerPluginAds {
|
||||
interface Translations {
|
||||
close: string
|
||||
countdown: string
|
||||
detail: string
|
||||
canBeClosed: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 知名广告的类型:'video' | 'image' | 'html'
|
||||
*/
|
||||
type: 'video' | 'image' | 'html'
|
||||
/** Implemented options. Video takes precedence over HTML. */
|
||||
interface Option {
|
||||
html?: string
|
||||
video?: string
|
||||
url?: string
|
||||
/** Seconds before the close button becomes available. @default 5 */
|
||||
playDuration?: number
|
||||
/** Total advertisement duration in seconds. @default 10 */
|
||||
totalDuration?: number
|
||||
/** Initial ad-video mute state. @default false */
|
||||
muted?: boolean
|
||||
/** All four fields replace the default translation object together. */
|
||||
i18n?: Translations
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告必看的时长,单位为秒
|
||||
*/
|
||||
playDuration?: number
|
||||
/** Historical published declaration. String durations still fail runtime validation. */
|
||||
interface LegacyOption extends Omit<Option, 'totalDuration'> {
|
||||
/** @deprecated Incorrect in the old declaration; use a numeric duration. */
|
||||
totalDuration?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告总的时长,单位为秒
|
||||
*/
|
||||
totalDuration?: number
|
||||
/** Historical unpublished workspace declaration; these fields are not runtime aliases. */
|
||||
interface WorkspaceOption extends Option {
|
||||
/** @deprecated Ignored by the runtime. Use html or video instead. */
|
||||
source: string
|
||||
/** @deprecated Ignored by the runtime. Images are supplied through html. */
|
||||
type: 'video' | 'image' | 'html'
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频广告是否默认静音
|
||||
*/
|
||||
muted?: boolean
|
||||
/** Input acceptance for both historical declaration families. */
|
||||
interface CompatOption extends Omit<Option, 'totalDuration'> {
|
||||
/** @deprecated The string branch exists for old types only and is rejected at runtime. */
|
||||
totalDuration?: number | string
|
||||
/** @deprecated Ignored by the runtime. Use html or video instead. */
|
||||
source?: string
|
||||
/** @deprecated Ignored by the runtime. Images are supplied through html. */
|
||||
type?: 'video' | 'image' | 'html'
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginAds'
|
||||
/** Complete once; before initialization this cancels the pending preroll. */
|
||||
skip: () => void
|
||||
/** Pause only the countdown, leaving ad video playback unchanged. */
|
||||
pause: () => void
|
||||
/** Resume only the countdown without adding extra timers. */
|
||||
play: () => void
|
||||
}
|
||||
|
||||
interface Callable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** @deprecated Compatibility with erroneous old string-duration declarations only. */
|
||||
(option: LegacyOption): (art: Artplayer) => Result
|
||||
(option: WorkspaceOption): (art: Artplayer) => Result
|
||||
(option?: CompatOption): (art: Artplayer) => Result
|
||||
/** Required final signature keeps Parameters extraction free of top-level undefined. */
|
||||
(option: CompatOption): (art: Artplayer) => Result
|
||||
}
|
||||
|
||||
interface Factory extends Callable {
|
||||
/** Same function; supports historical require(package).default calls. */
|
||||
readonly default: Callable
|
||||
}
|
||||
|
||||
interface RuntimeCallable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
|
||||
/** Accurate typing for the identical implementation at /runtime. */
|
||||
interface RuntimeFactory extends RuntimeCallable {
|
||||
readonly default: RuntimeCallable
|
||||
}
|
||||
}
|
||||
|
||||
interface Ads {
|
||||
name: 'artplayerPluginAds'
|
||||
|
||||
/**
|
||||
* 跳过广告
|
||||
*/
|
||||
skip: () => void
|
||||
|
||||
/**
|
||||
* 暂停广告
|
||||
*/
|
||||
pause: () => void
|
||||
|
||||
/**
|
||||
* 播放广告
|
||||
*/
|
||||
play: () => void
|
||||
}
|
||||
|
||||
declare const artplayerPluginAds: (option: Option) => (art: Artplayer) => Ads
|
||||
|
||||
export default artplayerPluginAds
|
||||
declare const artplayerPluginAds: artplayerPluginAds.Factory
|
||||
export = artplayerPluginAds
|
||||
export as namespace artplayerPluginAds
|
||||
@@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
|
||||
import artplayerPluginAds = require('./index.js')
|
||||
|
||||
export = artplayerPluginAds
|
||||
@@ -0,0 +1,2 @@
|
||||
export type { Option, Result, RuntimeFactory, Translations } from '../artplayer-plugin-ads.js'
|
||||
export { default } from './index.js'
|
||||
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable ts/no-redeclare -- The callable export and public type namespace intentionally merge. */
|
||||
import type * as Definition from '../artplayer-plugin-ads'
|
||||
|
||||
declare const artplayerPluginAds: Definition.RuntimeFactory
|
||||
declare namespace artplayerPluginAds {
|
||||
type Option = Definition.Option
|
||||
type Result = Definition.Result
|
||||
type RuntimeFactory = Definition.RuntimeFactory
|
||||
type Translations = Definition.Translations
|
||||
}
|
||||
export = artplayerPluginAds
|
||||
@@ -37,6 +37,19 @@ Node 测试已接入 test:unit,因此 ci:check 会执行。浏览器测试独
|
||||
`ARTPLAYER_ADS_ARTIFACT` 给浏览器指定单个 script 产物,默认按仓库 Vite 配置从源码
|
||||
内存构建。路径无效直接失败,不自动回退。实际发布 bundle 总是先校验归档及成员哈希。
|
||||
|
||||
## 04 类型与编辑器验证
|
||||
|
||||
`refactor/scripts/ads-types.test.mjs` 验证五种类型消费模式、十个无效用法,以及实际
|
||||
npm 和冻结工作区声明。普通调用通过不代表 scalar 提取兼容;诊断测试明确复现
|
||||
旧字段读取在候选上的 TS2322。用户明确接受此项修正后,ADS-TYPE-01 标为
|
||||
accepted-with-scope;保留诊断并编译迁移示例,不把批准范围扩大到其他 API。
|
||||
|
||||
`yarn test:ads-types-package` 在工作区外安装实际 tarball,验证安装字节、冻结离线
|
||||
重装、五种类型模式和 Node exports。`test/browser/ads-editor-types.spec.js` 从
|
||||
本地 docs 加载真实 Monaco 和生成声明,执行其编译结果并检查 default 工厂身份。
|
||||
`test/ads.test.js` 增加候选 `.default === factory` 及同步取消断言,覆盖三种产物。
|
||||
这些检查不替代全部历史子路径、媒体/设备与发布验收。
|
||||
|
||||
## 边界与文件职责
|
||||
|
||||
- `test/helpers/ads.js`:来源加载、冻结 bundle 的 VM、可控时钟和最小 ArtPlayer 宿主。
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-ADS-04",
|
||||
"status": "done",
|
||||
"sourceCommit": "39e2d4cd",
|
||||
"inputs": [
|
||||
{
|
||||
"file": "package.json",
|
||||
"sha256LF": "7d56a87d087e96cb34465201cdf5c0926b5714a215f749e22bba96f908a64711"
|
||||
},
|
||||
{
|
||||
"file": "scripts/build-ts.js",
|
||||
"sha256LF": "0453b9a9ac25f5be99e9ecfe222c6ae1d16540374b2b6c9b7529a54bdd50f91a"
|
||||
},
|
||||
{
|
||||
"file": "scripts/plugin-editor-types.mjs",
|
||||
"sha256LF": "6ecf896e084a19e5a9a9e7aca65a1108d96d842e3e2da61a05755d74a8f7f9c8"
|
||||
},
|
||||
{
|
||||
"file": "scripts/typecheck.mjs",
|
||||
"sha256LF": "9e439c300afa6e37813db312478a3ac3f47d0bb363e615ec49c67cd6ecdbb883"
|
||||
},
|
||||
{
|
||||
"file": "test/types/ads.ts",
|
||||
"sha256LF": "16b333c61f7a57f9d8ba8e6fab6e40aba761c4dc746e99d6b38e122315b0dde3"
|
||||
},
|
||||
{
|
||||
"file": "test/types/ads-source.ts",
|
||||
"sha256LF": "02221946a6cb726045c4910a40027ff8e0a6e011426443e28e11762bc1cdb52e"
|
||||
},
|
||||
{
|
||||
"file": "test/ads.test.js",
|
||||
"sha256LF": "0b5406a5aca3cd69672343f00492f6fcb22db17ec39056f1d89fb5cdd467a7d6"
|
||||
},
|
||||
{
|
||||
"file": "test/browser/ads-editor-types.spec.js",
|
||||
"sha256LF": "e55c8674e93b04342e6ca0325a411b74fa4cf8d331e57e571e555c4c1e706b80"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/ads-types.test.mjs",
|
||||
"sha256LF": "ddecd08afe4f04740c3c915e3a54c88d94fecd4796cbe8e1c668d69c46322685"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/ads-package-types.mjs",
|
||||
"sha256LF": "846af87f5f7f0c84fcbccecd4b3dfd6e2c1946ce47dfe7122760fe5f25cf5b6a"
|
||||
},
|
||||
{
|
||||
"file": "refactor/fixtures/consumers/ads-published.ts",
|
||||
"sha256LF": "0b8e5bb9bb74aebcf309b7af8942a7cbe6036de14f07fac121959eeb7d7d3910"
|
||||
},
|
||||
{
|
||||
"file": "refactor/fixtures/consumers/ads-workspace.ts",
|
||||
"sha256LF": "aee3b18b9c8f566a71e1e9d31a2ce8cdeae5664b246806bceed66bc97e18129e"
|
||||
},
|
||||
{
|
||||
"file": "docs/assets/ts/artplayer-plugin-ads.d.ts",
|
||||
"sha256LF": "bf128828d5b104fc844a4f134a02162e57472d8c927a50df7b3878f4c5ed7652"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/package.json",
|
||||
"sha256LF": "9a7d0a2e917902bb64282566e96123244a81aa4859a82c6b1c711d639660a9bf"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/tsconfig.json",
|
||||
"sha256LF": "583e4ac1afa99e3a29c7f899df9fdeefcbd14d3847d69143e3fa6a8facac6d99"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/countdown.ts",
|
||||
"sha256LF": "7dab796a3d8fb757da7f87e4396403f0687af52d0876cf74ebb2b8db7c2e752a"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/index.ts",
|
||||
"sha256LF": "7b0e155a6371c71a5c3f5ba0e9e95737648a2b73b0cf124536de8b6f79a92371"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/options.ts",
|
||||
"sha256LF": "3ce2ad78b9f0288314fa75adf14776adf10f2205789268e9599a6d551b3b8b4f"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/resources.ts",
|
||||
"sha256LF": "ba0d5b5f25d79929a39a9cda6b80b9e60a2b746e20312aa658d84fbb9be6562f"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/session.ts",
|
||||
"sha256LF": "59540b169b7bb348ddd1d773368717cbd6c4d3b1b28305c1c759fea6aef8d827"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/style.less",
|
||||
"sha256LF": "0eeed3f29ccafc6836f5bc4338f58f784edd114d2b100fda7fc395329031a406"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/types.ts",
|
||||
"sha256LF": "219d5b06bdbece09b88640fe6f2bc27c44dd34eff5909cec9f7405a9dced759a"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/src/view.ts",
|
||||
"sha256LF": "7d2cba7a3e20ce02288627723ebc9256d61cee313c3a7d00a31373b7f250eb91"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.cts",
|
||||
"sha256LF": "ccd2d5f47bb94f00cad3950d8fa8670f6fd46b384876b0d1c0325ff209a1a1d1"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.mts",
|
||||
"sha256LF": "f8c5b9843d0992c2f01200581b8d1c166ba4d3423ccc87496c0a4c0bae5a9c5b"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts",
|
||||
"sha256LF": "a347e5a3e244ff0e58a28cad7c0e84adcdbadbef4dfe2128c525e6d317e7e0bb"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/runtime/index.d.cts",
|
||||
"sha256LF": "d97fbe931948bce1eb4ab3be7780f096a4b0095db31c1d82b4df1f80ab5c2d90"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/runtime/index.d.mts",
|
||||
"sha256LF": "5f1f7a86be47bd72571e2665f37007c7c250ea0b774806c4864221bd7bef5bc5"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/types/runtime/index.d.ts",
|
||||
"sha256LF": "ddaa0c124b4d1d2b673f1305bbe625dda83d4296eb7d0a9ede202201bd483b8e"
|
||||
}
|
||||
],
|
||||
"artifacts": [
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js",
|
||||
"sha256": "bc718001050bbe73f0fb450fbdffdf127dd2b0839e19fbd473fe0856e6cdae76",
|
||||
"bytes": 7276,
|
||||
"copy": {
|
||||
"file": "docs/compiled/artplayer-plugin-ads.js",
|
||||
"sha256": "bc718001050bbe73f0fb450fbdffdf127dd2b0839e19fbd473fe0856e6cdae76",
|
||||
"bytes": 7276
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js",
|
||||
"sha256": "3f60e8a5d9fc93affff4ecc97b4b4035f743ab076da6baa05e3ea50052141fa6",
|
||||
"bytes": 7715,
|
||||
"copy": {
|
||||
"file": "docs/compiled/artplayer-plugin-ads.legacy.js",
|
||||
"sha256": "3f60e8a5d9fc93affff4ecc97b4b4035f743ab076da6baa05e3ea50052141fa6",
|
||||
"bytes": 7715
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs",
|
||||
"sha256": "e04ac6e56f2f9170fd56f40f0a01c4ed990de6dc6abf5f176d2df4e340f23b6a",
|
||||
"bytes": 13059,
|
||||
"copy": {
|
||||
"file": "docs/compiled/artplayer-plugin-ads.mjs",
|
||||
"sha256": "e04ac6e56f2f9170fd56f40f0a01c4ed990de6dc6abf5f176d2df4e340f23b6a",
|
||||
"bytes": 13059
|
||||
}
|
||||
}
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"file": "refactor/.cache/ads04-types-first.log",
|
||||
"sha256": "c18abde08ebfb2e58d076c2be9537ede0f26b98697822dec1380e68c259336e3",
|
||||
"bytes": 490
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads04-types-final.log",
|
||||
"sha256": "adf2534bbbb1e480c6697889554333c4db37dbf488c3dae84b35c91f25082bab",
|
||||
"bytes": 481
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads04-build.log",
|
||||
"sha256": "b13f278c4875a97015e66b17287b83c8e59d539a453136f9389c1fc03c30e282",
|
||||
"bytes": 1438
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads04-artifacts-node.log",
|
||||
"sha256": "56b6327c75b2e231e5a6f5c4ed31cc715fc8459f17d2770cc5a9f70fc3405509",
|
||||
"bytes": 21421
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads04-lint-final.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"bytes": 0
|
||||
}
|
||||
],
|
||||
"node": {
|
||||
"passed": 190,
|
||||
"failed": 0,
|
||||
"scope": "Source, three candidate builds, both frozen implementations. Four new candidate default identity contracts."
|
||||
},
|
||||
"ci": {
|
||||
"file": "refactor/.cache/ads04-ci.log",
|
||||
"sha256": "89a90190c53142bf8b46a75d7cea7f77e0a64739ec4a0e40d44e15d965c82f9d",
|
||||
"bytes": 87364,
|
||||
"passed": 834,
|
||||
"groups": [
|
||||
771,
|
||||
14,
|
||||
49
|
||||
],
|
||||
"productionTS": 269,
|
||||
"note": "Full local CI before final approval documentation and migration fixture additions; final focused type tests and isolated installation rerun afterwards. No runtime changes afterwards."
|
||||
},
|
||||
"installed": {
|
||||
"report": {
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/report.json",
|
||||
"sha256": "790e857b2de783682ce920528f8e434900027c031e58389e6168d661270f99b5",
|
||||
"bytes": 27243
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/artplayer-pack.log",
|
||||
"sha256": "2bfac87c586ecfd7b5bcd526df4ea5b029ecf6aa64cce74aa7d40655a7fb608f",
|
||||
"bytes": 109
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/artplayer-plugin-ads-pack.log",
|
||||
"sha256": "40c8eba25889d42ff743390b7a30a180dc0d799cc61f37425a3a17099df35855",
|
||||
"bytes": 120
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/artplayer-plugin-ads.tgz",
|
||||
"sha256": "61fc2065715603a6fec0385ea6f2c583c72a06717c60e590b644714fa40142da",
|
||||
"bytes": 13099
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/artplayer.tgz",
|
||||
"sha256": "2139af017670a4f30f667bf5fdc1aa680f3e03b9c8d3cb767fc92d6345aedff5",
|
||||
"bytes": 280395
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/frozen-install.log",
|
||||
"sha256": "6d8a95fad69ac94a55d176b472fc3a0f72783092e49b7e683156b6717510f4a8",
|
||||
"bytes": 56
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/install.log",
|
||||
"sha256": "c3b09f4c65bfb4a1bf31ddd2e3328c22988cf52a31e36c31ea2de4d3132a89c6",
|
||||
"bytes": 142
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/report.json",
|
||||
"sha256": "790e857b2de783682ce920528f8e434900027c031e58389e6168d661270f99b5",
|
||||
"bytes": 27243
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/runtime-exports.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"bytes": 0
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ads-package-types-ViT0Rk/yarn.lock",
|
||||
"sha256": "dfbe3a9bb57cda8f2a56e56be62eb86f4621357bfe809404c53d8627c6f50012",
|
||||
"bytes": 1142
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"file": "refactor/.cache/ads04-isolated-approved.log",
|
||||
"sha256": "5fff64be2b2a5842114383e1f179d82bafe85935bd37f383653416f6c5eeea65",
|
||||
"bytes": 623
|
||||
}
|
||||
},
|
||||
"browsers": [
|
||||
{
|
||||
"name": "editor-browser",
|
||||
"stats": {
|
||||
"startTime": "2026-09-12T00:13:32.234Z",
|
||||
"duration": 5034.381,
|
||||
"expected": 3,
|
||||
"skipped": 0,
|
||||
"unexpected": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
"report": {
|
||||
"file": "refactor/.cache/ads04-editor-browser-report.json",
|
||||
"sha256": "dc368e26442ebd5ba13c248420f3ec0f0a6ab131bb0b202f2c235b3db45e5e92",
|
||||
"bytes": 33504
|
||||
},
|
||||
"log": {
|
||||
"file": "refactor/.cache/ads04-editor-browser.log",
|
||||
"sha256": "5e883ca8658726669d00b22cf288437588dae0a571c7c13a1527fdd2c18de80f",
|
||||
"bytes": 1624
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"file": "refactor/.cache/ads04-editor-browser-results/.last-run.json",
|
||||
"sha256": "91d1c43004802cd49950d78eb11c8fa7d05da8ffffe219a8b13b2f561bc00903",
|
||||
"bytes": 45
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "editor-legacy-browser",
|
||||
"stats": {
|
||||
"startTime": "2026-09-12T00:18:00.519Z",
|
||||
"duration": 6241.242,
|
||||
"expected": 3,
|
||||
"skipped": 0,
|
||||
"unexpected": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
"report": {
|
||||
"file": "refactor/.cache/ads04-editor-legacy-browser-report.json",
|
||||
"sha256": "a4986570a83a82c1ef1d3649d41b49c02cd242bf0bed48905b218d838d754afd",
|
||||
"bytes": 33696
|
||||
},
|
||||
"log": {
|
||||
"file": "refactor/.cache/ads04-editor-legacy-browser.log",
|
||||
"sha256": "565c6e84a7fdf4c5be7763aa618319f2e79f9b85370d461a78fac770e5092503",
|
||||
"bytes": 1624
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"file": "refactor/.cache/ads04-editor-legacy-browser-results/.last-run.json",
|
||||
"sha256": "91d1c43004802cd49950d78eb11c8fa7d05da8ffffe219a8b13b2f561bc00903",
|
||||
"bytes": 45
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "browser-main",
|
||||
"stats": {
|
||||
"startTime": "2026-09-12T00:15:13.125Z",
|
||||
"duration": 152809.649,
|
||||
"expected": 144,
|
||||
"skipped": 0,
|
||||
"unexpected": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
"report": {
|
||||
"file": "refactor/.cache/ads04-browser-main-report.json",
|
||||
"sha256": "3db67f4bc41458cdb0d5f6e51d981656111fe6010378aa135f5fc1c3a8955fff",
|
||||
"bytes": 9439737
|
||||
},
|
||||
"log": {
|
||||
"file": "refactor/.cache/ads04-browser-main.log",
|
||||
"sha256": "987573115af5eeaf03ea33d355238a1c12fd17ae0dea9e01836d151d5931c42b",
|
||||
"bytes": 25421
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"file": "refactor/.cache/ads04-browser-main-results/.last-run.json",
|
||||
"sha256": "91d1c43004802cd49950d78eb11c8fa7d05da8ffffe219a8b13b2f561bc00903",
|
||||
"bytes": 45
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"acceptedDifference": {
|
||||
"id": "ADS-TYPE-01",
|
||||
"date": "2026-09-12",
|
||||
"decision": "接受这项类型推导修正,写清迁移说明并继续",
|
||||
"scope": "Only scalar inference correction between conflicting published/workspace declarations. Old calls accepted; string durations still rejected at runtime; migration examples compiled.",
|
||||
"publishedDiagnostics": [
|
||||
2322
|
||||
],
|
||||
"workspaceDiagnostics": [
|
||||
2322,
|
||||
2322
|
||||
],
|
||||
"compilers": [
|
||||
"4.3.5",
|
||||
"5.9.3"
|
||||
]
|
||||
},
|
||||
"unresolved": [
|
||||
"Full legacy media matrix from 03 was not rerun for 04: 04 checks all three formats in Node, main media 144 and main/legacy editor 3 each.",
|
||||
"Native fullscreen, real hidden-page/device behavior and complete historical distribution remain 05/06.",
|
||||
"No push, tag, merge or npm publication."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# PKG-ADS-04 类型与导入兼容
|
||||
|
||||
本步完成公开类型迁移;整个 Ads 发布验收未完成。起点 `39e2d4cd`。
|
||||
|
||||
## 实际改动
|
||||
|
||||
- 公开声明补齐真实 html/video/url/i18n、同步 Result 与四种输入形状。
|
||||
根入口以 export= 和类型命名空间保存旧 CommonJS/全局模式,增加 ESM/CJS 声明桥。
|
||||
- `/runtime` 指向同一 JS 文件,只提供准确参数;根入口保留两套历史输入接受面。
|
||||
src/types 复用公开形状,options 中现有 validator 后才断言归一化结果,无字符串转换。
|
||||
- 入口增加 `.default === factory`,兼容实际 npm 的 require(pkg).default 调用和
|
||||
工作区 callable 调用;未模仿旧模块对象不可调用、Object.keys 等 namespace 反射形状。
|
||||
- 编辑器生成器显式支持纯类型 namespace,拒绝未知导入和不支持的节点;实际 Monaco
|
||||
从 docs/assets/js/vs 加载、编译并运行当前生成声明及真实 bundle。
|
||||
- 新增 `yarn test:ads-types-package`:临时打包、工作区外离线安装、冻结重装、所有成员
|
||||
哈希验证、五种编译模式与 Node require/import 同一运行时验证。未增加依赖或锁文件变化。
|
||||
|
||||
## API-09 / API-11 已确认差异
|
||||
|
||||
实际 npm 1.0.6 的 totalDuration 声明为 string;未发布工作区 2.1.0 为 number,
|
||||
并要求 source/type。两者与实际运行时字段不同。普通调用和配置对象赋值已同时验证,
|
||||
但无法把同一属性既准确推导为 string 又推导为 number。
|
||||
|
||||
当前兼容输入使 `Parameters<typeof ads>[0].totalDuration` 读取为
|
||||
`number | string | undefined`。原发布消费者把它赋给 `string | undefined` 会产生
|
||||
TS2322;原工作区把 source 赋给 string、duration 赋给 number | undefined 也会产生
|
||||
TS2322。这是真实兼容差异,不能因为旧输入调用通过而称所有旧 TS 程序无改动兼容。
|
||||
`LegacyOption` / `WorkspaceOption` 和准确 `/runtime` 可表达各自形状,但要求用户
|
||||
修改类型写法本身不算无改动兼容。
|
||||
|
||||
2026-09-12,向用户提供上述具体影响和验证结果后,用户明确答复:
|
||||
“接受这项类型推导修正,写清迁移说明并继续”。ADS-TYPE-01 据此标为
|
||||
accepted-with-scope,仅接受两套历史声明矛盾导致的 scalar 推导修正;不扩大到其他
|
||||
API、运行时行为或发布豁免。README 提供准确 Option、/runtime、数字字段及泛型
|
||||
输入收窄迁移说明,类型测试也编译同样的安全读取模式。
|
||||
|
||||
`refactor/scripts/ads-types.test.mjs` 从校验过的发布 tarball 和冻结 Git 声明读取旧类型,
|
||||
同一普通消费者在旧/新声明通过;额外 scalar 消费者先在旧声明通过,再精确断言候选
|
||||
1/2 个 TS2322。此诊断测试持续保留;差异的接受依据为上述用户明确决策,测试本身
|
||||
只证明实际影响范围。
|
||||
|
||||
## 验证与接续
|
||||
|
||||
执行结果与输入哈希见 [执行证据](../baselines/ads-types-validation.json)。
|
||||
源码/三格式 Node、五模式类型、安装及编辑器测试均单独列出;全屏/真隐藏页/设备、
|
||||
完整历史 dist 路径和最终发布门槛继续由05/06承接。已有 DASH 风险保持开放。
|
||||
|
||||
实际模块地图与维护命令见 [包内架构](../../packages/artplayer-plugin-ads/ARCHITECTURE.md)。
|
||||
撤销本步时同时撤销入口 .default、公开声明/桥、manifest、生成产物和消费者测试;
|
||||
保留03生命周期修复。按任务独立本地提交,无推送、tag、合并或发布。
|
||||
@@ -0,0 +1,8 @@
|
||||
import ads from 'artplayer-plugin-ads'
|
||||
|
||||
const option: Parameters<typeof ads>[0] = { html: '<b>ad</b>', video: 'ad.mp4', url: '/ad', totalDuration: '10', playDuration: 2, muted: true }
|
||||
const plugin = ads(option)
|
||||
const resultName: ReturnType<typeof plugin>['name'] = 'artplayerPluginAds'
|
||||
const result: ReturnType<typeof plugin> = { name: resultName, skip() {}, pause() {}, play() {} }
|
||||
const returns: void[] = [result.skip(), result.pause(), result.play()]
|
||||
void [returns, plugin]
|
||||
@@ -0,0 +1,8 @@
|
||||
import ads from 'artplayer-plugin-ads'
|
||||
|
||||
const option: Parameters<typeof ads>[0] = { source: 'ad.mp4', type: 'video', totalDuration: 10, playDuration: 2, muted: true }
|
||||
const plugin = ads(option)
|
||||
const resultName: ReturnType<typeof plugin>['name'] = 'artplayerPluginAds'
|
||||
const result: ReturnType<typeof plugin> = { name: resultName, skip() {}, pause() {}, play() {} }
|
||||
const returns: void[] = [result.skip(), result.pause(), result.play()]
|
||||
void [returns, plugin]
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 136 / doing 4 / blocked 0 / done 77 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 135 / doing 4 / blocked 0 / done 78 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
| PKG-ADS-01 | artplayer-plugin-ads<br>核对包契约与历史用法 | BASE-05 | 核对 npm 1.0.0~1.0.6 与未发布工作区 2.1.0 的真实参数、类型、结果、倒计时、分发和旧核心来源 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
|
||||
| PKG-ADS-02 | artplayer-plugin-ads<br>建立特有行为与错误测试 | PKG-ADS-01, ENG-03, ENG-05 | 固定 npm 1.0.6 与工作区契约;HTML/视频/图片 HTML、跳过阈值、计时链、工厂复用、多实例、过早/重复方法、播放拒绝与销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
|
||||
| PKG-ADS-03 | artplayer-plugin-ads<br>整理内部职责与资源 | PKG-ADS-02, CORE-10, CORE-13 | 配置/广告状态/计时/视图/主视频恢复分离,保留产品规则;资源与版本检查改用经验证的旧核心能力边界 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
|
||||
| PKG-ADS-04 | artplayer-plugin-ads<br>迁移自有源码和公开类型 | PKG-ADS-03, ENG-04, ENG-06, CORE-07 | 兼容真实 html/video/url/i18n、旧 export= 与工作区 source/type 类型接受面;时长 string/number 漂移与未实现别名单独处置 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
|
||||
| PKG-ADS-04 | artplayer-plugin-ads<br>迁移自有源码和公开类型 | PKG-ADS-03, ENG-04, ENG-06, CORE-07 | 兼容真实 html/video/url/i18n、旧 export= 与工作区 source/type 类型接受面;时长 string/number 漂移与未实现别名单独处置 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
|
||||
| PKG-ADS-05 | artplayer-plugin-ads<br>验证新旧核心和组合 | PKG-ADS-04, CORE-22 | 本地广告 fixture、多实例、正常结束/跳过/销毁;核验历史关联核心 4.5.5、发布 5.4.1 与候选核心的实际归档/能力/媒体组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
|
||||
| PKG-ADS-06 | artplayer-plugin-ads<br>验证分发并同步文档 | PKG-ADS-05, ENG-07 | ads.js/README/声明一致;旧 require(pkg).default、原 dist 路径、当前 callable/ESM/legacy 的隔离 tarball 及浏览器验收 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
|
||||
|
||||
@@ -487,3 +487,4 @@
|
||||
- PKG-ADS-01: [记录](baselines/ads-release.json) [记录](baselines/ads-contract.md) [记录](baselines/ads-contract-validation.json) [记录](changes/2026-09-12-PKG-ADS-01-contract.md) [记录](scripts/ads-contract.test.mjs)
|
||||
- PKG-ADS-02: [记录](changes/2026-09-12-PKG-ADS-02-tests.md) [记录](ads-validation.md) [记录](baselines/ads-validation.json)
|
||||
- PKG-ADS-03: [记录](changes/2026-09-12-PKG-ADS-03-lifecycle.md) [记录](ads-validation.md) [记录](baselines/ads-lifecycle-validation.json)
|
||||
- PKG-ADS-04: [记录](changes/2026-09-12-PKG-ADS-04-types.md) [记录](baselines/ads-types-validation.json)
|
||||
@@ -1,5 +1,20 @@
|
||||
# 进度与证据
|
||||
|
||||
## 最新完成:Ads-04 类型与导入兼容
|
||||
|
||||
公开声明补齐 html/video/url/i18n,增加 CJS/ESM 类型桥和同一运行时的准确 /runtime
|
||||
类型入口。保留两套历史输入的普通调用,并补齐 require(pkg).default 自引用兼容。
|
||||
严格消费者、历史声明对照、隔离安装和真实 Monaco 编译运行已经接入可重跑测试。
|
||||
|
||||
同一 Parameters 提取类型无法同时维持发布版 string 和工作区 number 时长字段;
|
||||
普通调用通过,但旧 scalar 读取分别复现1/2个 TS2322。用户已明确接受这项类型
|
||||
推导修正并要求迁移说明,ADS-TYPE-01 据此 accepted-with-scope,不扩展为其他豁免。
|
||||
完整 CI 834 项通过,三格式 Node190、main真实媒体144、main/legacy编辑器各3项通过;
|
||||
最终安装包五模式和迁移示例继续严格验证。04完成,下一步05真实页面与媒体/UI验收。
|
||||
当前217项:78 done、4 doing、135 todo。详细结果见
|
||||
[实施记录](changes/2026-09-12-PKG-ADS-04-types.md) 和
|
||||
[验证证据](baselines/ads-types-validation.json)。
|
||||
|
||||
## 最新完成:Ads-03 源码拆分与生命周期修复
|
||||
|
||||
Ads 原入口拆为 7 个严格 TS 模块,职责为配置、类型、计时、资源、视图、session 和入口。
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
| DASH-TYPE-01 | resolved / 源码/产物事实 | DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口 | PKG-DASH-04 |
|
||||
| DASH-DEMO-01 | open / 源码/产物事实 | DASH 示例仅新 SDK 且重复安装会累积销毁回调 | PKG-DASH-06 |
|
||||
| DASH-SEEK-01 | open / 已复现 | dash.js 4.5.2 空裁剪漏更新缓冲量,稳定暂停 seek 后调度停滞 | PKG-DASH-05, REVIEW-02 |
|
||||
| ADS-TYPE-01 | open / 源码/产物事实 | Ads 声明丢失真实 html/video/url,旧 totalDuration 为 string,当前 source/type 未实现 | PKG-ADS-02, PKG-ADS-04 |
|
||||
| ADS-TYPE-01 | accepted-with-scope / 已复现 | Ads 声明丢失真实 html/video/url,旧 totalDuration 为 string,当前 source/type 未实现 | PKG-ADS-02, PKG-ADS-04 |
|
||||
| ADS-DIST-01 | open / 已复现 | Ads npm 1.0.6 default namespace 与未发布 2.1.0 callable/exports 分发不同 | PKG-ADS-04, PKG-ADS-06, REL-01 |
|
||||
| ADS-CORE-01 | open / 源码/产物事实 | Ads 旧发布关联核心 4.5.5,当前源码新增 >=5 门槛和 document 事件桥依赖 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 |
|
||||
| ADS-LIFE-01 | open / 已复现 | Ads 计时链、重复/过早调用、工厂复用与销毁缺乏资源边界 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 |
|
||||
|
||||
+37
-9
@@ -2,7 +2,7 @@
|
||||
"schemaVersion": 1,
|
||||
"task": "BASE-07",
|
||||
"sourceCommit": "e01f39ff",
|
||||
"updatedAt": "2026-09-11",
|
||||
"updatedAt": "2026-09-12",
|
||||
"note": "Open means closure evidence is outstanding; source-observed and unverified are not reproduced runtime bugs. A source issue already mitigated in the workspace still requires candidate regression evidence. No accepted-with-scope item is implied by baseline task completion.",
|
||||
"items": [
|
||||
{
|
||||
@@ -2985,20 +2985,48 @@
|
||||
{
|
||||
"id": "ADS-TYPE-01",
|
||||
"title": "Ads 声明丢失真实 html/video/url,旧 totalDuration 为 string,当前 source/type 未实现",
|
||||
"confirmation": "source-observed",
|
||||
"status": "open",
|
||||
"owners": ["PKG-ADS-02", "PKG-ADS-04"],
|
||||
"evidence": ["refactor/baselines/ads-release.json", "refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts", "packages/artplayer-plugin-ads/src/index.ts"],
|
||||
"compatibleResolution": "同时保护实际 npm 字段/旧声明消费者和当前类型接受面;真实数字时长、i18n 与 source/type 别名的行为差异单独登记,不伪称旧实现支持别名。",
|
||||
"closureCriteria": "两套旧消费者、可选工厂、合法运行字段和错误参数有严格正反例,声明与真实方法一致。"
|
||||
"confirmation": "reproduced",
|
||||
"status": "accepted-with-scope",
|
||||
"owners": [
|
||||
"PKG-ADS-02",
|
||||
"PKG-ADS-04"
|
||||
],
|
||||
"evidence": [
|
||||
"refactor/baselines/ads-release.json",
|
||||
"refactor/baselines/ads-contract.md",
|
||||
"packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts",
|
||||
"packages/artplayer-plugin-ads/src/index.ts",
|
||||
"refactor/changes/2026-09-12-PKG-ADS-04-types.md",
|
||||
"refactor/scripts/ads-types.test.mjs"
|
||||
],
|
||||
"compatibleResolution": "保留真实html/video/url/i18n及两套历史输入的普通调用;string时长仍在运行时拒绝、source/type仍不实现。用户2026-09-12明确接受Parameters字段推导变宽,README和编译用例提供准确Option、/runtime及收窄迁移;不扩大到其他API差异。",
|
||||
"closureCriteria": "两套旧消费者、可选工厂、合法运行字段和错误参数有严格正反例,声明与真实方法一致。",
|
||||
"resolutionRationale": "用户在收到具体TS2322影响和两套矛盾声明证据后答复“接受这项类型推导修正,写清迁移说明并继续”。五模式、冻结声明、隔离包和真实编辑器通过;诊断测试继续保留,不声称所有历史TS程序无改动兼容。",
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-12-PKG-ADS-04-types.md",
|
||||
"refactor/baselines/ads-types-validation.json",
|
||||
"packages/artplayer-plugin-ads/README.md",
|
||||
"refactor/scripts/ads-types.test.mjs",
|
||||
"test/types/ads.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ADS-DIST-01",
|
||||
"title": "Ads npm 1.0.6 default namespace 与未发布 2.1.0 callable/exports 分发不同",
|
||||
"confirmation": "reproduced",
|
||||
"status": "open",
|
||||
"owners": ["PKG-ADS-04", "PKG-ADS-06", "REL-01"],
|
||||
"evidence": ["refactor/baselines/ads-release.json", "refactor/baselines/ads-contract.md", "refactor/scripts/ads-contract.test.mjs", "packages/artplayer-plugin-ads/package.json"],
|
||||
"owners": [
|
||||
"PKG-ADS-04",
|
||||
"PKG-ADS-06",
|
||||
"REL-01"
|
||||
],
|
||||
"evidence": [
|
||||
"refactor/baselines/ads-release.json",
|
||||
"refactor/baselines/ads-contract.md",
|
||||
"refactor/scripts/ads-contract.test.mjs",
|
||||
"packages/artplayer-plugin-ads/package.json",
|
||||
"refactor/baselines/ads-types-validation.json"
|
||||
],
|
||||
"compatibleResolution": "保留旧 require(pkg).default、global 与有效 dist 路径,同时支持当前 callable/ESM/legacy。工作区 2.1.0 不冒充实际已发布版本,正式目标仍按计划 3.0.0。",
|
||||
"closureCriteria": "工作区外真实 tarball 的旧/新导入、浏览器与声明入口全部验证,版本占用再次核实。"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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'
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:ads-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/ads-package-types-'))
|
||||
console.log(`Ads isolated package type evidence: ${output}`)
|
||||
const consumer = consumerDirectory()
|
||||
try {
|
||||
const packages = []
|
||||
const published = JSON.parse(fs.readFileSync(path.join(workspace, 'refactor/baselines/ads-release.json'))).release
|
||||
for (const name of ['artplayer', 'artplayer-plugin-ads']) {
|
||||
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 files = packedFiles(archive)
|
||||
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
|
||||
checkFiles(manifest, files, name === published.name ? Object.keys(published.files) : [])
|
||||
packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
|
||||
}
|
||||
writeJson(path.join(consumer, 'package.json'), { name: 'ads-isolated-type-consumer', private: true, dependencies: Object.fromEntries(packages.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, '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, '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, 'yarn.lock'), lock)
|
||||
for (const pkg of packages) {
|
||||
const root = path.join(consumer, 'node_modules', pkg.name)
|
||||
assert.equal(fs.realpathSync(root), root, 'Installed package is 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 matrix = []
|
||||
const runtimeFile = path.join(consumer, 'exports.cjs')
|
||||
fs.writeFileSync(runtimeFile, `const assert = require('node:assert/strict');
|
||||
const ads = require('artplayer-plugin-ads'); const legacy = require('artplayer-plugin-ads/legacy'); const runtime = require('artplayer-plugin-ads/runtime');
|
||||
for (const factory of [ads, legacy, runtime]) { assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory({ html: 'ad' }), 'function'); }
|
||||
assert.equal(runtime, ads);
|
||||
Promise.all(['artplayer-plugin-ads', 'artplayer-plugin-ads/runtime'].map(name => import(name))).then(([root, precise]) => { assert.equal(root.default, precise.default); assert.equal(root.default.default, root.default); }).catch(error => { console.error(error); process.exitCode = 1; });`)
|
||||
fs.writeFileSync(path.join(output, 'runtime-exports.log'), run([runtimeFile], consumer))
|
||||
for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, 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 }
|
||||
const source = fs.readFileSync(path.join(workspace, 'test/types/ads.ts'), 'utf8')
|
||||
const filename = path.join(consumer, `current.${extension}`)
|
||||
const legacy = path.join(consumer, `legacy.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/ads-published.ts'), legacy)
|
||||
const workspaceFixture = path.join(consumer, `workspace.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/ads-workspace.ts'), workspaceFixture)
|
||||
const files = [filename, legacy, workspaceFixture]
|
||||
if (mode === 'nodenext-cjs') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import ads = require('artplayer-plugin-ads'); import legacy = require('artplayer-plugin-ads/legacy'); import runtime = require('artplayer-plugin-ads/runtime');
|
||||
const option: ads.Option = { html: 'ad', totalDuration: 5 }; ads(option); ads.default(option); legacy(); legacy.default(option); runtime(option); runtime.default(option);`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
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 isolated consumer: ${actual}`)
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
assert.deepEqual(diagnostics, [], `${compiler.version} ${mode}`)
|
||||
const invalid = compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''))
|
||||
assert.equal(invalid.length, 10, 'Installed package must reject all invalid uses')
|
||||
matrix.push({ compiler: compiler.version, mode, diagnostics, invalid })
|
||||
}
|
||||
const report = { suite: 'ads-isolated-package-types', introducedBy: 'PKG-ADS-04', scope: 'Packed current core and Ads, offline Yarn install and frozen reinstall outside workspace; five type modes and Node export identity. Historical scalar inference correction accepted with scope by maintainer; not complete media or distribution release validation.', packages, matrix }
|
||||
writeJson(path.join(output, 'report.json'), report)
|
||||
console.log(`Ads installed type matrix passed: ${matrix.length} modes, ten invalid uses rejected in each; ${output}`)
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,88 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Check published and workspace declaration consumers independently.
|
||||
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 { ensureArchive, hash, readMember, refactorDir } from './releases.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
|
||||
test('Ads root, legacy and accurate runtime types support five compiler modes and reject invalid consumers', () => {
|
||||
const source = fs.readFileSync('test/types/ads.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)
|
||||
}
|
||||
const commonjs = `import ads = require('artplayer-plugin-ads'); import legacy = require('artplayer-plugin-ads/legacy'); import runtime = require('artplayer-plugin-ads/runtime');
|
||||
const option: ads.Option = { html: 'ad', totalDuration: 5 }; ads(option); ads.default(option); legacy(option); legacy.default(option); runtime(option); runtime.default(option);`
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', commonjs), [])
|
||||
})
|
||||
|
||||
test('Ads historical calls compile against frozen declarations; approved scalar extraction differences remain explicit', async () => {
|
||||
const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/ads-release.json')))
|
||||
const member = 'package/types/artplayer-plugin-ads.d.ts'
|
||||
const published = readMember(await ensureArchive(baseline.release), member)
|
||||
assert.equal(hash(published), baseline.release.files[member])
|
||||
const oldPath = 'packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts'
|
||||
const workspace = execFileSync('git', ['show', `${baseline.sourceCommit}:${oldPath}`]).toString().replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(workspace), baseline.source[oldPath])
|
||||
const directory = fs.mkdtempSync(path.join(refactorDir, '.cache/ads-legacy-types-'))
|
||||
try {
|
||||
for (const [family, declaration, extraction] of [
|
||||
['published', published, `declare const input: Parameters<typeof ads>[0]; const duration: string | undefined = input.totalDuration; void duration;`],
|
||||
['workspace', workspace, `declare const input: Parameters<typeof ads>[0]; const source: string = input.source; const duration: number | undefined = input.totalDuration; void [source, duration];`],
|
||||
]) {
|
||||
const source = fs.readFileSync(path.join(refactorDir, `fixtures/consumers/ads-${family}.ts`), 'utf8')
|
||||
fs.writeFileSync(path.join(directory, 'legacy.d.ts'), declaration)
|
||||
const filename = path.join(directory, 'consumer.ts')
|
||||
for (const compiler of [ts, compat]) {
|
||||
fs.writeFileSync(filename, `${source}\n${extraction}`.replace('from \'artplayer-plugin-ads\'', 'from \'./legacy\''))
|
||||
const program = compiler.createProgram([filename], { 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'] })
|
||||
assert.deepEqual(compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') })), [], `${family} / ${compiler.version}`)
|
||||
assert(program.getSourceFiles().some(file => path.resolve(file.fileName) === path.join(directory, 'legacy.d.ts')))
|
||||
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', source), [])
|
||||
const differences = checkConsumer(compiler, 'node10-commonjs', `${source}\n${extraction}`)
|
||||
assert.deepEqual(differences.map(item => item.code), family === 'published' ? [2322] : [2322, 2322], 'ADS-TYPE-01 accepted with scope: preserve the exact approved scalar inference differences')
|
||||
}
|
||||
for (const [compiler, mode] of modes)
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${family} / ${compiler.version} / ${mode}`)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
const resolved = fs.realpathSync(directory)
|
||||
assert.equal(path.dirname(resolved), fs.realpathSync(path.join(refactorDir, '.cache')))
|
||||
assert(path.basename(resolved).startsWith('ads-legacy-types-'))
|
||||
fs.rmSync(resolved, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('Ads editor generation retains public namespaces and rejects unknown declaration shapes', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts', 'utf8')
|
||||
const generated = generatePluginEditorDeclaration(source, 'artplayerPluginAds')
|
||||
const file = 'docs/assets/ts/artplayer-plugin-ads.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 = `const option: artplayerPluginAds.Option = { html: 'ad', totalDuration: 5 };
|
||||
const factory: (art: Artplayer) => artplayerPluginAds.Result = artplayerPluginAds(option);
|
||||
artplayerPluginAds.default(option);
|
||||
// @ts-expect-error A DOM element is not HTML text.
|
||||
artplayerPluginAds({ html: document.body });
|
||||
// @ts-expect-error Complete translations are required.
|
||||
artplayerPluginAds({ i18n: { close: 'Close' } });
|
||||
void factory;`
|
||||
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)
|
||||
}
|
||||
assert.throws(() => generatePluginEditorDeclaration(`import type Unknown from 'unknown';\n${source}`, 'artplayerPluginAds'), /Unsupported CommonJS plugin editor import/)
|
||||
assert.throws(() => generatePluginEditorDeclaration(source.replace('export as namespace artplayerPluginAds', ''), 'artplayerPluginAds'), /Incomplete CommonJS/)
|
||||
assert.throws(() => generatePluginEditorDeclaration(source.replace('interface Translations {', 'const unexpected: string; interface Translations {'), 'artplayerPluginAds'), /only public type declarations/)
|
||||
})
|
||||
+5
-2
@@ -2590,11 +2590,14 @@
|
||||
"ENG-06",
|
||||
"CORE-07"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "H",
|
||||
"deliverable": "兼容真实 html/video/url/i18n、旧 export= 与工作区 source/type 类型接受面;时长 string/number 漂移与未实现别名单独处置",
|
||||
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"changes/2026-09-12-PKG-ADS-04-types.md",
|
||||
"baselines/ads-types-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-ADS-05",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 已实现的类型检查与迁移入口
|
||||
|
||||
PKG-ADS-04 已协调公开声明:根入口保留历史输入,/runtime 使用准确输入,同一 JS
|
||||
实现不进行字符串时长转换。CJS/ESM 桥、五种消费者模式、实际旧声明对照与真实 Monaco
|
||||
生成运行已接入。`ads-source.ts` 检查源码与公开工厂的赋值关系;`ads.ts` 包含十个
|
||||
无效用法。`yarn test:ads-types-package` 在工作区外验证 tarball 安装后的同一矩阵。
|
||||
`ads-types.test.mjs` 额外复现旧 scalar 提取变宽的 TS2322;用户明确接受这项推导修正,
|
||||
已记录迁移说明,不扩大为其他发布豁免。详情见 [类型记录](changes/2026-09-12-PKG-ADS-04-types.md)。
|
||||
|
||||
PKG-HLS-04 增加 HLS 的 5 个严格 TS 源模块,公开声明保留历史 .d.ts 并增加 CJS/ESM 条件桥。
|
||||
test/types/hls-control.ts 覆盖配置省略、旧 Parameters 提取、默认字段和 SDK 泛型、同步返回,
|
||||
hls-source.ts 检查源码工厂与公开工厂可相互赋值。hls-types.test.mjs 对五组编译模式检查正反例,
|
||||
|
||||
+1
-1
@@ -43,7 +43,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'].includes(name)
|
||||
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds'].includes(name)
|
||||
const code = semanticPlugin
|
||||
? generatePluginEditorDeclaration(source, name)
|
||||
: `${source.replace(reg, '')}\nexport = ${name};\nexport as namespace ${name};\n`
|
||||
|
||||
@@ -8,6 +8,8 @@ export function generatePluginEditorDeclaration(code, name) {
|
||||
assert(/^[a-z_$][\w$]*$/i.test(name), 'Invalid plugin global')
|
||||
const source = ts.createSourceFile('plugin.d.ts', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
||||
assert.equal(source.parseDiagnostics.length, 0, 'Invalid plugin declaration syntax')
|
||||
if (source.statements.some(node => ts.isExportAssignment(node) && node.isExportEquals))
|
||||
return generateCommonJSPluginEditor(source, name)
|
||||
const factory = ts.factory
|
||||
const definitions = []
|
||||
const aliases = []
|
||||
@@ -56,6 +58,49 @@ export function generatePluginEditorDeclaration(code, name) {
|
||||
return `// Generated from the package public declaration by yarn build:ts. Do not edit.\n/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */\n${statements.map(node => printer.printNode(ts.EmitHint.Unspecified, node, source)).join('\n')}\n`
|
||||
}
|
||||
|
||||
function generateCommonJSPluginEditor(source, name) {
|
||||
const statements = []
|
||||
let namespace = false
|
||||
let callable = false
|
||||
let exported = false
|
||||
let global = false
|
||||
for (const node of source.statements) {
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
assert(node.importClause?.isTypeOnly && !node.importClause.namedBindings && node.importClause.name?.text === 'Artplayer' && node.moduleSpecifier.text === 'artplayer', 'Unsupported CommonJS plugin editor import')
|
||||
continue
|
||||
}
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
assert.equal(node.name.text, name, 'Unexpected plugin namespace')
|
||||
assert(node.body && ts.isModuleBlock(node.body) && node.body.statements.every(item => ts.isInterfaceDeclaration(item) || ts.isTypeAliasDeclaration(item)), 'Editor namespace must contain only public type declarations')
|
||||
namespace = true
|
||||
}
|
||||
else if (ts.isVariableStatement(node)) {
|
||||
const definitions = node.declarationList.declarations
|
||||
assert.equal(definitions.length, 1)
|
||||
const definition = definitions[0]
|
||||
assert.equal(definition.name.text, name)
|
||||
assert(ts.isTypeReferenceNode(definition.type) && ts.isQualifiedName(definition.type.typeName)
|
||||
&& definition.type.typeName.left.text === name && definition.type.typeName.right.text === 'Factory', 'Editor export must use its public Factory interface')
|
||||
callable = true
|
||||
}
|
||||
else if (ts.isExportAssignment(node)) {
|
||||
assert(node.isExportEquals && node.expression.text === name, 'Unexpected CommonJS plugin export')
|
||||
exported = true
|
||||
}
|
||||
else if (ts.isNamespaceExportDeclaration(node)) {
|
||||
assert.equal(node.name.text, name)
|
||||
global = true
|
||||
}
|
||||
else {
|
||||
assert.fail('Unsupported CommonJS plugin editor declaration')
|
||||
}
|
||||
statements.push(node)
|
||||
}
|
||||
assert(namespace && callable && exported && global, 'Incomplete CommonJS plugin editor declaration')
|
||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
|
||||
return `// Generated from the package public declaration by yarn build:ts. Do not edit.\n/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */\n${statements.map(node => printer.printNode(ts.EmitHint.Unspecified, node, source)).join('\n')}\n`
|
||||
}
|
||||
|
||||
export function checkPluginEditorDeclaration(code, core, consumer = '', compiler = ts) {
|
||||
const folder = fileURLToPath(new URL('../refactor/.cache/plugin-editor-semantic/', import.meta.url))
|
||||
const sources = new Map([
|
||||
|
||||
@@ -73,7 +73,7 @@ export function runTypechecks() {
|
||||
console.log(`Strict project passed: ${relative(config)} (${result.files.length} root files)`)
|
||||
}
|
||||
for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'bundler-esm'], [ts, 'nodenext-esm'], [compat, 'node10-commonjs']]) {
|
||||
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
|
||||
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'test/types/ads.ts', 'refactor/fixtures/consumers/ads-published.ts', 'refactor/fixtures/consumers/ads-workspace.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
}
|
||||
if (mode === 'nodenext-cjs')
|
||||
|
||||
@@ -8,6 +8,20 @@ const skips = host => host.calls.filter(call => call[0] === 'emit' && call[1] ==
|
||||
const clicks = host => host.calls.filter(call => call[0] === 'emit' && call[1] === 'artplayerPluginAds:click')
|
||||
const contentPlays = host => host.calls.filter(call => call[0] === 'content.play')
|
||||
|
||||
for (const { name, factory } of implementations.filter(item => !item.historical)) {
|
||||
test(`${name}: historical default and callable entry share one factory and synchronous contract`, (t) => {
|
||||
const env = adsEnvironment(t)
|
||||
assert.equal(factory.default, factory)
|
||||
const host = env.host()
|
||||
const result = factory.default({ html: 'ad' })(host.art)
|
||||
assert.equal(result.name, 'artplayerPluginAds')
|
||||
assert.equal(result.skip(), undefined)
|
||||
host.start()
|
||||
assert.equal(host.node(), undefined)
|
||||
assert.equal(env.timers.size, 0)
|
||||
})
|
||||
}
|
||||
|
||||
for (const { name, factory } of implementations) {
|
||||
test(`${name}: lazy synchronous factory starts once after ready and either playback event`, (t) => {
|
||||
const env = adsEnvironment(t)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { compilePackage } from '../helpers/load.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
test('Monaco compiles Ads namespaces and executes callable and historical default factories', async ({ page }, testInfo) => {
|
||||
const code = process.env.ARTPLAYER_ADS_ARTIFACT
|
||||
? fs.readFileSync(process.env.ARTPLAYER_ADS_ARTIFACT, 'utf8')
|
||||
: await compilePackage('artplayer-plugin-ads', 'umd')
|
||||
const declarations = Object.fromEntries(['artplayer', 'artplayer-plugin-ads'].map(name => [name, hash(fs.readFileSync(new URL(`../../docs/assets/ts/${name}.d.ts`, import.meta.url)))]))
|
||||
await testInfo.attach('ads-editor-inputs', { contentType: 'application/json', body: JSON.stringify({ candidate: process.env.ARTPLAYER_ADS_ARTIFACT || 'workspace source build', sha256: hash(code), declarations }) })
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
await page.addScriptTag({ content: code })
|
||||
await page.addScriptTag({ url: '/assets/js/vs/loader.js' })
|
||||
const result = await page.evaluate(async () => {
|
||||
window.require.config({ paths: { vs: '/assets/js/vs' } })
|
||||
await new Promise((resolve, reject) => window.require(['vs/editor/editor.main'], resolve, reject))
|
||||
const api = window.monaco
|
||||
const types = api.languages.typescript
|
||||
types.typescriptDefaults.setCompilerOptions({ target: types.ScriptTarget.ES2020, strict: true, skipLibCheck: false, noEmit: false, types: [], allowNonTsExtensions: true })
|
||||
const libraries = await Promise.all(['artplayer', 'artplayer-plugin-ads'].map(async (name) => {
|
||||
const uri = `file:///${name}.d.ts`
|
||||
const source = await (await fetch(`/assets/ts/${name}.d.ts`)).text()
|
||||
return { uri, handle: types.typescriptDefaults.addExtraLib(source, uri) }
|
||||
}))
|
||||
const model = api.editor.createModel(`
|
||||
const adsOption: artplayerPluginAds.Option = { html: '<b>ad</b>', totalDuration: 5,
|
||||
i18n: { close: 'Close', countdown: '%s seconds', detail: 'Details', canBeClosed: '%s seconds' } };
|
||||
const typedFactory: (art: Artplayer) => artplayerPluginAds.Result = artplayerPluginAds(adsOption);
|
||||
const defaultFactory = artplayerPluginAds.default(adsOption);
|
||||
const historical: Parameters<typeof artplayerPluginAds>[0] = { html: 'ad', totalDuration: '10' };
|
||||
const workspace: artplayerPluginAds.WorkspaceOption = { source: 'ad.mp4', type: 'video' };
|
||||
const oldFactory = artplayerPluginAds(historical);
|
||||
const workspaceFactory = artplayerPluginAds(workspace);
|
||||
`, 'typescript', api.Uri.parse('file:///ads-consumer.ts'))
|
||||
const invalid = api.editor.createModel('artplayerPluginAds({ totalDuration: false })', 'typescript', api.Uri.parse('file:///ads-invalid.ts'))
|
||||
try {
|
||||
const worker = await (await types.getTypeScriptWorker())(model.uri, invalid.uri)
|
||||
const syntax = await worker.getSyntacticDiagnostics(model.uri.toString())
|
||||
const semantic = await worker.getSemanticDiagnostics(model.uri.toString())
|
||||
const declarations = (await Promise.all(libraries.map(lib => worker.getSemanticDiagnostics(lib.uri)))).flat()
|
||||
const bad = await worker.getSemanticDiagnostics(invalid.uri.toString())
|
||||
if (syntax.length || semantic.length || declarations.length)
|
||||
return { syntax, semantic, declarations, invalid: bad.map(item => item.code), executed: false }
|
||||
const output = await worker.getEmitOutput(model.uri.toString())
|
||||
const script = output.outputFiles.find(file => file.name.endsWith('.js'))
|
||||
if (!script)
|
||||
throw new Error('Monaco did not emit the Ads fixture')
|
||||
// eslint-disable-next-line no-new-func -- Exercise the local editor Run action with its real emitted code.
|
||||
const executed = new Function(`${script.text}\nreturn [typedFactory, defaultFactory, oldFactory, workspaceFactory].every(value => typeof value === 'function') && artplayerPluginAds.default === artplayerPluginAds`)()
|
||||
return { syntax, semantic, declarations, invalid: bad.map(item => item.code), executed }
|
||||
}
|
||||
finally {
|
||||
model.dispose()
|
||||
invalid.dispose()
|
||||
for (const library of libraries)
|
||||
library.handle.dispose()
|
||||
}
|
||||
})
|
||||
expect(result.syntax).toEqual([])
|
||||
expect(result.semantic).toEqual([])
|
||||
expect(result.declarations).toEqual([])
|
||||
expect(result.invalid).toEqual([2769])
|
||||
expect(result.executed).toBe(true)
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { Factory, RuntimeFactory } from 'artplayer-plugin-ads'
|
||||
import factory from '../../packages/artplayer-plugin-ads/src/index'
|
||||
|
||||
const compatible: Factory = factory
|
||||
const precise: RuntimeFactory = factory
|
||||
const acceptsCompatible: typeof factory = compatible
|
||||
void [compatible, precise, acceptsCompatible]
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { CompatOption, Factory, LegacyOption, Option, Result, WorkspaceOption } from 'artplayer-plugin-ads'
|
||||
import Artplayer from 'artplayer'
|
||||
import ads from 'artplayer-plugin-ads'
|
||||
import legacy from 'artplayer-plugin-ads/legacy'
|
||||
import precise from 'artplayer-plugin-ads/runtime'
|
||||
|
||||
const option: Option = { video: 'ad.mp4', html: '<b>ad</b>', url: '/details', totalDuration: 10, muted: true, i18n: { close: 'Close', countdown: '%s seconds', detail: 'Details', canBeClosed: 'Wait %s seconds' } }
|
||||
const oldPublished: LegacyOption = { html: 'ad', totalDuration: '10' }
|
||||
const oldWorkspace: WorkspaceOption = { source: 'ad.mp4', type: 'video', totalDuration: 10 }
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', plugins: [ads(), ads(option), legacy(option), precise(option)] })
|
||||
ads(oldPublished)
|
||||
ads(oldWorkspace)
|
||||
ads.default(option)
|
||||
legacy.default(option)
|
||||
precise.default(option)
|
||||
ads(undefined)
|
||||
precise(undefined)
|
||||
const result: Result = ads(option)(art)
|
||||
const methods: void[] = [result.skip(), result.play(), result.pause()]
|
||||
const factory: Factory = ads
|
||||
const input: CompatOption = oldWorkspace
|
||||
const inferredOld: Parameters<typeof ads>[0] = oldPublished
|
||||
const inferredWorkspace: Parameters<typeof ads>[0] = oldWorkspace
|
||||
const raw: Parameters<typeof precise>[0] = option
|
||||
const numericDuration: number | undefined = option.totalDuration
|
||||
function readCompatibleDuration(value: CompatOption): number | undefined {
|
||||
if (value.totalDuration !== undefined && typeof value.totalDuration !== 'number')
|
||||
throw new TypeError('Ads requires numeric duration')
|
||||
return value.totalDuration
|
||||
}
|
||||
|
||||
// @ts-expect-error The precise entry rejects the historical duration typo.
|
||||
precise({ totalDuration: '10' })
|
||||
// @ts-expect-error source/type are not implemented runtime aliases.
|
||||
precise({ source: 'ad.mp4', type: 'video' })
|
||||
// @ts-expect-error Durations are not boolean in either declaration family.
|
||||
ads({ totalDuration: false })
|
||||
// @ts-expect-error No declaration accepted a string skip threshold.
|
||||
ads({ playDuration: '1' })
|
||||
// @ts-expect-error HTML is text, not a DOM node.
|
||||
ads({ html: document.body })
|
||||
// @ts-expect-error Translation objects replace all four fields together.
|
||||
ads({ i18n: { close: 'Close' } })
|
||||
// @ts-expect-error The plugin methods are synchronous.
|
||||
const promise: Promise<void> = result.skip()
|
||||
// @ts-expect-error Plugin name retains its literal spelling.
|
||||
const name: Result['name'] = 'ads'
|
||||
// @ts-expect-error Historical workspace type remains a fixed union.
|
||||
ads({ source: 'ad', type: 'audio' })
|
||||
// @ts-expect-error Historical string durations cannot become accurate numeric options.
|
||||
const wrong: Option = oldPublished
|
||||
void [methods, factory, input, inferredOld, inferredWorkspace, raw, numericDuration, readCompatibleDuration, promise, name, wrong]
|
||||
Reference in new issue
Block a user