diff --git a/AGENTS.md b/AGENTS.md index c12c93dd8..352bf4088 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -240,7 +240,8 @@ When editing proxy packages: The reference implementation for adaptive selector UI is: -- `packages/artplayer-plugin-hls-control/src/index.js` +- `packages/artplayer-plugin-hls-control/src/index.ts` +- `packages/artplayer-plugin-dash-control/src/index.ts` (SDK 4.x/5.x capability adapter) If adding HLS-like quality/audio selection elsewhere: diff --git a/docs/assets/ts/artplayer-plugin-dash-control.d.ts b/docs/assets/ts/artplayer-plugin-dash-control.d.ts index 39a52d129..4aaa0fcf7 100644 --- a/docs/assets/ts/artplayer-plugin-dash-control.d.ts +++ b/docs/assets/ts/artplayer-plugin-dash-control.d.ts @@ -1,17 +1,46 @@ -interface Config { - control?: boolean - setting?: boolean - title?: string - auto?: string - getName?: (level: object) => string +// 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 artplayerPluginDashControlDefinitions { + export interface QualityLevel { + height: number + width?: number + id?: string | number + qualityIndex?: number + bitrate?: number + bitrateInKbit?: number + } + export interface AudioTrack { + id?: string | number + index?: number + lang?: string + } + export interface Config { + control?: boolean + setting?: boolean + title?: string + auto?: string + /** Called without a receiver or index, with the original SDK object. */ + getName?: (item: Item) => string + } + export interface Option { + quality?: Config + audio?: Config + } + export interface Result { + name: 'artplayerPluginDashControl' + update: () => void + } + export function artplayerPluginDashControl(option?: Option): (art: Artplayer) => Result + // Preserve the required last signature for historical Parameters[0] consumers. + export function artplayerPluginDashControl(option: Option): (art: Artplayer) => Result } - -declare const artplayerPluginDashControl: (option: { quality?: Config, audio?: Config }) => (art: Artplayer) => { - name: 'artplayerPluginDashControl' - update: () => void +declare const artplayerPluginDashControl: typeof artplayerPluginDashControlDefinitions.artplayerPluginDashControl +declare namespace artplayerPluginDashControl { + export type QualityLevel = artplayerPluginDashControlDefinitions.QualityLevel + export type AudioTrack = artplayerPluginDashControlDefinitions.AudioTrack + export type Config = artplayerPluginDashControlDefinitions.Config + export type Option = artplayerPluginDashControlDefinitions.Option + export type Result = artplayerPluginDashControlDefinitions.Result } - -export default artplayerPluginDashControl - export = artplayerPluginDashControl export as namespace artplayerPluginDashControl; diff --git a/docs/compiled/artplayer-plugin-dash-control.js b/docs/compiled/artplayer-plugin-dash-control.js index 2c9950667..62b1417ef 100644 --- a/docs/compiled/artplayer-plugin-dash-control.js +++ b/docs/compiled/artplayer-plugin-dash-control.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){if(!o||r.get(n)!==o)return;r.delete(n);const l=t[n];"controls"===n&&l.cache?.get&&l.cache.get(e)?.option?.onSelect!==o||"setting"===n&&l.find&&l.find(e)?.onSelect!==o||l.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,s,u){if(!s)return void i();const a={};o=a;const f=()=>a===o&&u(),d=e=>f()?(s.select(e,f),f()?(t.notice.show=`${s.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:s.html,style:{padding:"0 10px"},selector:s.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:s.html,html:s.title,icon:n,width:200,selector:s.selector,onSelect:d})):l("setting"))},clear:i}}return function(n={}){return o=>{const{$video:r}=o.template,{errorHandle:l}=o.constructor.utils;let i=!1,c=0;const s=e(o,"dash-quality",''),u=e(o,"dash-audio",''),a=[];function f(t=()=>!0){let e;for(const o of[s.clear,u.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function d(){if(i||o.isDestroy)return;const e=++c,a=o.dash,d=()=>!i&&!o.isDestroy&&e===c&&o.dash===a,h=()=>d()&&a.getVideoElement()===r&&d();try{if(l(a.getVideoElement()===r,'Cannot find instance of DASH from "art.dash"'),!d())return;const e=n.quality||{},o=function(e,n,o){const r=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),l=r.levels();if(!o()||!l?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>`${t.height}p`),s=r.current(l);if(!o())return null;const u=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!o())return null;const a=!u&&s?c(s):i;if(!o())return null;const f=[];for(const[t,h]of l.entries()){const e=c(h);if(!o())return null;f.push({html:e,...r.item(h,t,s,u)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:i,value:"auto",default:u}),{html:a,title:n.title||"Quality",selector:d,select:r.select}}(a,e,h);if(!h())return;if(s.update(e,o,h),!h())return;const i=n.audio||{},c=function(e,n,o){const r=e.getTracksFor("audio");if(!o()||!r?.length)return null;const l=n.auto||"Auto",i=n.getName||(t=>t.lang||t.id),c=e.getCurrentTrackFor("audio")||r[0];if(!o())return null;const s=c?i(c):l;if(!o())return null;const u=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=t.filter(t=>["id","index","lang"].every(n=>null==e[n]||t[n]===e[n]));return 1===n.length?n[0]:void 0}(r,c),a=[];for(const t of r){const e=i(t);if(!o())return null;a.push({html:e,value:t,default:t===u})}const f=t(a);return{html:s,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(a,i,h);h()&&u.update(i,c,h)}catch(g){if(d()){const t=++c;try{f(()=>c===t)}catch(p){console.warn("ArtPlayer DASH cleanup failed:",p)}}throw g}}function h(){if(i)return;let t;i=!0,c++;const e=[f,...a.splice(0).map(([t,e])=>()=>o.off(t,e))];for(const o of e)try{o()}catch(n){t||(t=n)}if(t)throw t}try{for(const t of[["ready",d],["restart",d],["destroy",h]]){if(i)break;a.push(t),o.on(...t)}}catch(g){try{h()}catch(p){console.warn("ArtPlayer DASH subscription cleanup failed:",p)}throw g}return{name:"artplayerPluginDashControl",update:d}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){if(!o||r.get(n)!==o)return;r.delete(n);const l=t[n];"controls"===n&&l.cache?.get&&l.cache.get(e)?.option?.onSelect!==o||"setting"===n&&l.find&&l.find(e)?.onSelect!==o||l.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,s,u){if(!s)return void i();const a={};o=a;const f=()=>a===o&&u(),d=e=>f()?(s.select(e,f),f()?(t.notice.show=`${s.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:s.html,style:{padding:"0 10px"},selector:s.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:s.html,html:s.title,icon:n,width:200,selector:s.selector,onSelect:d})):l("setting"))},clear:i}}return function(n={}){return o=>{const r=o,{$video:l}=r.template,{errorHandle:i}=r.constructor.utils;let c=!1,s=0;const u=e(r,"dash-quality",''),a=e(r,"dash-audio",''),f=[];function d(t=()=>!0){let e;for(const o of[u.clear,a.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function h(){if(c||r.isDestroy)return;const e=++s,o=r.dash,f=()=>!c&&!r.isDestroy&&e===s&&r.dash===o,h=()=>f()&&o.getVideoElement()===l&&f();try{if(i(o.getVideoElement()===l,'Cannot find instance of DASH from "art.dash"'),!f())return;const e=n.quality||{},r=function(e,n,o){const r=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),l=r.levels();if(!o()||!l?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>`${t.height}p`),s=r.current(l);if(!o())return null;const u=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!o())return null;const a=!u&&s?c(s):i;if(!o())return null;const f=[];for(const[t,h]of l.entries()){const e=c(h);if(!o())return null;f.push({html:e,...r.item(h,t,s,u)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:i,value:"auto",default:u}),{html:a,title:n.title||"Quality",selector:d,select:r.select}}(o,e,h);if(!h())return;if(u.update(e,r,h),!h())return;const c=n.audio||{},s=function(e,n,o){const r=e.getTracksFor("audio");if(!o()||!r?.length)return null;const l=n.auto||"Auto",i=n.getName||(t=>t.lang||t.id),c=e.getCurrentTrackFor("audio")||r[0];if(!o())return null;const s=c?i(c):l;if(!o())return null;const u=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(r,c),a=[];for(const t of r){const e=i(t);if(!o())return null;a.push({html:e,value:t,default:t===u})}const f=t(a);return{html:s,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(o,c,h);h()&&a.update(c,s,h)}catch(g){if(f()){const t=++s;try{d(()=>s===t)}catch(p){console.warn("ArtPlayer DASH cleanup failed:",p)}}throw g}}function g(){if(c)return;let t;c=!0,s++;const e=[d,...f.splice(0).map(([t,e])=>()=>r.off(t,e))];for(const o of e)try{o()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",h],["restart",h],["destroy",g]];for(const e of t){if(c)break;f.push(e),r.on(...e)}}catch(p){try{g()}catch(y){console.warn("ArtPlayer DASH subscription cleanup failed:",y)}throw p}return{name:"artplayerPluginDashControl",update:h}}}}); diff --git a/docs/compiled/artplayer-plugin-dash-control.legacy.js b/docs/compiled/artplayer-plugin-dash-control.legacy.js index a270906b6..284833af3 100644 --- a/docs/compiled/artplayer-plugin-dash-control.legacy.js +++ b/docs/compiled/artplayer-plugin-dash-control.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,l=(t,l)=>{for(var i in l||(l={}))n.call(l,i)&&r(t,i,l[i]);if(e)for(var i of e(l))o.call(l,i)&&r(t,i,l[i]);return t};function i(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){var l,i,c,u;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(l=s.cache)?void 0:l.get)&&(null==(c=null==(i=s.cache.get(e))?void 0:i.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(u=s.find(e))?void 0:u.onSelect)!==o||s.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,u,s){if(!u)return void i();const a={};o=a;const f=()=>a===o&&s(),d=e=>f()?(u.select(e,f),f()?(t.notice.show=`${u.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:u.html,style:{padding:"0 10px"},selector:u.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:u.html,html:u.title,icon:n,width:200,selector:u.selector,onSelect:d})):l("setting"))},clear:i}}return function(t={}){return e=>{const{$video:n}=e.template,{errorHandle:o}=e.constructor.utils;let r=!1,u=0;const s=c(e,"dash-quality",''),a=c(e,"dash-audio",''),f=[];function d(t=()=>!0){let e;for(const o of[s.clear,a.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function h(){if(r||e.isDestroy)return;const c=++u,f=e.dash,h=()=>!r&&!e.isDestroy&&c===u&&e.dash===f,p=()=>h()&&f.getVideoElement()===n&&h();try{if(o(f.getVideoElement()===n,'Cannot find instance of DASH from "art.dash"'),!h())return;const e=t.quality||{},r=function(t,e,n){const o=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),r=o.levels();if(!n()||!(null==r?void 0:r.length))return null;const c=e.auto||"Auto",u=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const a=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!a&&s?u(s):c;if(!n())return null;const d=[];for(const[i,p]of r.entries()){const t=u(p);if(!n())return null;d.push(l({html:t},o.item(p,i,s,a)))}const h=i(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:a}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(f,e,p);if(!p())return;if(s.update(e,r,p),!p())return;const c=t.audio||{},u=function(t,e,n){const o=t.getTracksFor("audio");if(!n()||!(null==o?void 0:o.length))return null;const r=e.auto||"Auto",l=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const u=c?l(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=t.filter(t=>["id","index","lang"].every(n=>null==e[n]||t[n]===e[n]));return 1===n.length?n[0]:void 0}(o,c),a=[];for(const i of o){const t=l(i);if(!n())return null;a.push({html:t,value:i,default:i===s})}const f=i(a);return{html:u,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(f,c,p);p()&&a.update(c,u,p)}catch(g){if(h()){const t=++u;try{d(()=>u===t)}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw g}}function p(){if(r)return;let t;r=!0,u++;const n=[d,...f.splice(0).map(([t,n])=>()=>e.off(t,n))];for(const e of n)try{e()}catch(o){t||(t=o)}if(t)throw t}try{for(const t of[["ready",h],["restart",h],["destroy",p]]){if(r)break;f.push(t),e.on(...t)}}catch(g){try{p()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw g}return{name:"artplayerPluginDashControl",update:h}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,l=(t,l)=>{for(var i in l||(l={}))n.call(l,i)&&r(t,i,l[i]);if(e)for(var i of e(l))o.call(l,i)&&r(t,i,l[i]);return t};function i(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){var l,i,c,u;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(l=s.cache)?void 0:l.get)&&(null==(c=null==(i=s.cache.get(e))?void 0:i.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(u=s.find(e))?void 0:u.onSelect)!==o||s.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,u,s){if(!u)return void i();const a={};o=a;const f=()=>a===o&&s(),d=e=>f()?(u.select(e,f),f()?(t.notice.show=`${u.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:u.html,style:{padding:"0 10px"},selector:u.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:u.html,html:u.title,icon:n,width:200,selector:u.selector,onSelect:d})):l("setting"))},clear:i}}return function(t={}){return e=>{const n=e,{$video:o}=n.template,{errorHandle:r}=n.constructor.utils;let u=!1,s=0;const a=c(n,"dash-quality",''),f=c(n,"dash-audio",''),d=[];function h(t=()=>!0){let e;for(const o of[a.clear,f.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(u||n.isDestroy)return;const e=++s,c=n.dash,d=()=>!u&&!n.isDestroy&&e===s&&n.dash===c,p=()=>d()&&c.getVideoElement()===o&&d();try{if(r(c.getVideoElement()===o,'Cannot find instance of DASH from "art.dash"'),!d())return;const e=t.quality||{},n=function(t,e,n){const o=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),r=o.levels();if(!n()||!(null==r?void 0:r.length))return null;const c=e.auto||"Auto",u=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const a=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!a&&s?u(s):c;if(!n())return null;const d=[];for(const[i,p]of r.entries()){const t=u(p);if(!n())return null;d.push(l({html:t},o.item(p,i,s,a)))}const h=i(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:a}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(c,e,p);if(!p())return;if(a.update(e,n,p),!p())return;const u=t.audio||{},s=function(t,e,n){const o=t.getTracksFor("audio");if(!n()||!(null==o?void 0:o.length))return null;const r=e.auto||"Auto",l=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const u=c?l(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(o,c),a=[];for(const i of o){const t=l(i);if(!n())return null;a.push({html:t,value:i,default:i===s})}const f=i(a);return{html:u,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,u,p);p()&&f.update(u,s,p)}catch(g){if(d()){const t=++s;try{h(()=>s===t)}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw g}}function g(){if(u)return;let t;u=!0,s++;const e=[h,...d.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(o){t||(t=o)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",g]];for(const e of t){if(u)break;d.push(e),n.on(...e)}}catch(v){try{g()}catch(y){console.warn("ArtPlayer DASH subscription cleanup failed:",y)}throw v}return{name:"artplayerPluginDashControl",update:p}}}}); diff --git a/docs/compiled/artplayer-plugin-dash-control.mjs b/docs/compiled/artplayer-plugin-dash-control.mjs index a0c85dbe8..09be10ebd 100644 --- a/docs/compiled/artplayer-plugin-dash-control.mjs +++ b/docs/compiled/artplayer-plugin-dash-control.mjs @@ -77,7 +77,8 @@ function selectedTrack(tracks, current) { return current; if (current.id == null && current.index == null) return void 0; - const matching = tracks.filter((track) => ["id", "index", "lang"].every((key) => current[key] == null || track[key] === current[key])); + const fields = ["id", "index", "lang"]; + const matching = tracks.filter((track) => fields.every((key) => current[key] == null || track[key] === current[key])); return matching.length === 1 ? matching[0] : void 0; } function audioModel(dash, config, active) { @@ -170,7 +171,8 @@ function createMenu(art, name, icon) { } const $quality = ''; function artplayerPluginDashControl(option = {}) { - return (art) => { + return (player) => { + const art = player; const { $video } = art.template; const { errorHandle } = art.constructor.utils; let closed = false; @@ -244,7 +246,8 @@ function artplayerPluginDashControl(option = {}) { throw failure; } try { - for (const entry of [["ready", update], ["restart", update], ["destroy", destroy]]) { + const entries = [["ready", update], ["restart", update], ["destroy", destroy]]; + for (const entry of entries) { if (closed) break; subscriptions.push(entry); diff --git a/package.json b/package.json index e32558ece..d20209a58 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,8 @@ "test:package": "node scripts/package-check.mjs", "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" + "check:types": "node scripts/build-types.mjs --check", + "test:dash-types-package": "node refactor/scripts/dash-package-types.mjs" }, "browserslist": "last 1 Chrome version", "devDependencies": { diff --git a/packages/artplayer-plugin-dash-control/.npmignore b/packages/artplayer-plugin-dash-control/.npmignore index 4439c6874..2b272d565 100644 --- a/packages/artplayer-plugin-dash-control/.npmignore +++ b/packages/artplayer-plugin-dash-control/.npmignore @@ -2,4 +2,5 @@ node_modules .DS_Store yarn.lock package-lock.json -src \ No newline at end of file +src +tsconfig.json diff --git a/packages/artplayer-plugin-dash-control/ARCHITECTURE.md b/packages/artplayer-plugin-dash-control/ARCHITECTURE.md index 7e622ea0e..190408dac 100644 --- a/packages/artplayer-plugin-dash-control/ARCHITECTURE.md +++ b/packages/artplayer-plugin-dash-control/ARCHITECTURE.md @@ -8,18 +8,20 @@ destroys it, changes its source, or adds a runtime dependency on dash.js. ## Module map -| File | Responsibility | -| --- | --- | -| `src/index.js` | Deferred installation, media identity, update revisions, event subscription and cleanup | -| `src/sdk.js` | 4.x qualityIndex vs 5.x representation ID access and manual/Auto selection | -| `src/mapping.js` | Names, current item matching, duplicate labels and selector ordering | -| `src/menu.js` | Existing control/setting registries, menu ownership and guarded selection callbacks | -| `src/audio.svg`, `src/quality.svg` | Existing setting icons | -| `types/artplayer-plugin-dash-control.d.ts` | Existing public declarations; precise TS migration follows in PKG-DASH-04 | +| File | Responsibility | +| ------------------------------------------ | --------------------------------------------------------------------------------------- | +| `src/index.ts` | Deferred installation, media identity, update revisions, event subscription and cleanup | +| `src/sdk.ts` | 4.x qualityIndex vs 5.x representation ID access and manual/Auto selection | +| `src/mapping.ts` | Names, current item matching, duplicate labels and selector ordering | +| `src/menu.ts` | Existing control/setting registries, menu ownership and guarded selection callbacks | +| `src/audio.svg`, `src/quality.svg` | Existing setting icons | +| `types/artplayer-plugin-dash-control.d.ts` | Public options, generic SDK inputs and callable result | Dependencies flow from the entry into mapping/menu, and from mapping into the SDK adapter. Mapping has no DOM dependency. Menu receives a model and validity callback; -it does not choose SDK versions. This is the structural step, still in JavaScript. +it does not choose SDK versions. All five owned modules are checked with strict +TypeScript, noUncheckedIndexedAccess, and skipLibCheck=false; there are no remaining +owned JavaScript modules in this package. ## Selection and lifetime @@ -54,6 +56,36 @@ Keep `dash-quality`/`dash-audio`, selector shape, right control placement/paddin setting width/icons, defaults and unbound getName calls compatible. Do not use a filtered representation array index or absoluteIndex as the SDK 5 selection key. +## Types and compatibility + +`src/types.ts` describes the narrow SDK and registry surfaces, menu models, event +names, labels, and cleanup callbacks. SDK keys may be absent in malformed external +data; the internal types retain that possibility instead of asserting every value +exists. SDK methods are optional by generation. Non-null assertions in `sdk.ts` +preserve the selected method family's historical synchronous failure when a caller +provides an incomplete SDK. They do not validate the whole SDK at runtime. + +The entry casts only the externally augmented ArtPlayer boundary to the narrow host. +The non-empty track check protects the fallback item. Quality sorting casts apply +before the synthetic Auto row is appended, and the manual selection key cast follows +the Auto branch check. These assertions do not generate runtime conversions or mask +the controlled error/identity tests. Changes to SDK capability detection belong in +`sdk.ts`, not in a global `any` or ambient module escape. + +Public `Config`, `Option`, `QualityLevel`, `AudioTrack`, and `Result` are named types. +Default callbacks infer known SDK fields; explicit generic parameters or callback +annotations accept a caller's more detailed SDK interfaces. Original object-based +formatters remain valid. The optional overload supports omitted/undefined options; +the last required overload preserves historical `Parameters[0]`. +Formatters still receive exactly one argument and return a string; update returns +void synchronously. No global augmentation claims every ArtPlayer has a DASH SDK. + +The historical `.d.ts` path stays available. `.d.cts` exports a callable CommonJS +value with a type namespace; `.d.mts` bridges ESM default/named types. Runtime root +and legacy paths are unchanged. `typesVersions` supports old Node10 type resolution +for the legacy subpath. The online editor declaration is generated by `yarn build:ts` +and checked standalone against both TS 4.3.5 and 5.9.3, then consumed by real Monaco. + ## Validation and next work Run at the workspace root with pinned Node and Yarn: @@ -62,6 +94,9 @@ Run at the workspace root with pinned Node and Yarn: yarn test:dash-control yarn build artplayer-plugin-dash-control yarn test:browser test/browser/dash-control.spec.js --trace on +yarn test:browser test/browser/dash-editor-types.spec.js --trace on +yarn build:ts +yarn test:dash-types-package yarn ci:check ``` @@ -71,6 +106,14 @@ contracts and lifecycle tests; use the operating system's path delimiter. and result/trace directory before running another suite because the output is shared. Tests use frozen npm 1.1.0, a frozen pre-refactor 5.x implementation, and candidates. +`test/types/dash-source.ts` checks source/public factory compatibility; +`test/types/dash-control.ts` covers module modes and rejects invalid calls. The +legacy fixture is checked against the actual published declaration and candidate. +`test:dash-types-package` packs the current core and DASH with Yarn, installs the +tarballs offline outside the workspace, repeats a frozen install and checks all +public type modes without resolving any workspace source. It also verifies that +implementation files/config do not leak into the archive. This is type acceptance; +the full installed runtime and SDK release matrix still belongs to PKG-DASH-06/05. Browser tests exercise real old/new core DOM, menu clicks, native MP4 and cleanup, with controlled SDK methods. They do not load dash.js or test MPD/ABR decoding. PKG-DASH-05 must verify pinned dash.js 4.5.2 and 5.2.1 with real adaptive media and diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js index 2c9950667..62b1417ef 100644 --- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js +++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){if(!o||r.get(n)!==o)return;r.delete(n);const l=t[n];"controls"===n&&l.cache?.get&&l.cache.get(e)?.option?.onSelect!==o||"setting"===n&&l.find&&l.find(e)?.onSelect!==o||l.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,s,u){if(!s)return void i();const a={};o=a;const f=()=>a===o&&u(),d=e=>f()?(s.select(e,f),f()?(t.notice.show=`${s.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:s.html,style:{padding:"0 10px"},selector:s.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:s.html,html:s.title,icon:n,width:200,selector:s.selector,onSelect:d})):l("setting"))},clear:i}}return function(n={}){return o=>{const{$video:r}=o.template,{errorHandle:l}=o.constructor.utils;let i=!1,c=0;const s=e(o,"dash-quality",''),u=e(o,"dash-audio",''),a=[];function f(t=()=>!0){let e;for(const o of[s.clear,u.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function d(){if(i||o.isDestroy)return;const e=++c,a=o.dash,d=()=>!i&&!o.isDestroy&&e===c&&o.dash===a,h=()=>d()&&a.getVideoElement()===r&&d();try{if(l(a.getVideoElement()===r,'Cannot find instance of DASH from "art.dash"'),!d())return;const e=n.quality||{},o=function(e,n,o){const r=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),l=r.levels();if(!o()||!l?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>`${t.height}p`),s=r.current(l);if(!o())return null;const u=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!o())return null;const a=!u&&s?c(s):i;if(!o())return null;const f=[];for(const[t,h]of l.entries()){const e=c(h);if(!o())return null;f.push({html:e,...r.item(h,t,s,u)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:i,value:"auto",default:u}),{html:a,title:n.title||"Quality",selector:d,select:r.select}}(a,e,h);if(!h())return;if(s.update(e,o,h),!h())return;const i=n.audio||{},c=function(e,n,o){const r=e.getTracksFor("audio");if(!o()||!r?.length)return null;const l=n.auto||"Auto",i=n.getName||(t=>t.lang||t.id),c=e.getCurrentTrackFor("audio")||r[0];if(!o())return null;const s=c?i(c):l;if(!o())return null;const u=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=t.filter(t=>["id","index","lang"].every(n=>null==e[n]||t[n]===e[n]));return 1===n.length?n[0]:void 0}(r,c),a=[];for(const t of r){const e=i(t);if(!o())return null;a.push({html:e,value:t,default:t===u})}const f=t(a);return{html:s,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(a,i,h);h()&&u.update(i,c,h)}catch(g){if(d()){const t=++c;try{f(()=>c===t)}catch(p){console.warn("ArtPlayer DASH cleanup failed:",p)}}throw g}}function h(){if(i)return;let t;i=!0,c++;const e=[f,...a.splice(0).map(([t,e])=>()=>o.off(t,e))];for(const o of e)try{o()}catch(n){t||(t=n)}if(t)throw t}try{for(const t of[["ready",d],["restart",d],["destroy",h]]){if(i)break;a.push(t),o.on(...t)}}catch(g){try{h()}catch(p){console.warn("ArtPlayer DASH subscription cleanup failed:",p)}throw g}return{name:"artplayerPluginDashControl",update:d}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){if(!o||r.get(n)!==o)return;r.delete(n);const l=t[n];"controls"===n&&l.cache?.get&&l.cache.get(e)?.option?.onSelect!==o||"setting"===n&&l.find&&l.find(e)?.onSelect!==o||l.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,s,u){if(!s)return void i();const a={};o=a;const f=()=>a===o&&u(),d=e=>f()?(s.select(e,f),f()?(t.notice.show=`${s.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:s.html,style:{padding:"0 10px"},selector:s.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:s.html,html:s.title,icon:n,width:200,selector:s.selector,onSelect:d})):l("setting"))},clear:i}}return function(n={}){return o=>{const r=o,{$video:l}=r.template,{errorHandle:i}=r.constructor.utils;let c=!1,s=0;const u=e(r,"dash-quality",''),a=e(r,"dash-audio",''),f=[];function d(t=()=>!0){let e;for(const o of[u.clear,a.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function h(){if(c||r.isDestroy)return;const e=++s,o=r.dash,f=()=>!c&&!r.isDestroy&&e===s&&r.dash===o,h=()=>f()&&o.getVideoElement()===l&&f();try{if(i(o.getVideoElement()===l,'Cannot find instance of DASH from "art.dash"'),!f())return;const e=n.quality||{},r=function(e,n,o){const r=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),l=r.levels();if(!o()||!l?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>`${t.height}p`),s=r.current(l);if(!o())return null;const u=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!o())return null;const a=!u&&s?c(s):i;if(!o())return null;const f=[];for(const[t,h]of l.entries()){const e=c(h);if(!o())return null;f.push({html:e,...r.item(h,t,s,u)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:i,value:"auto",default:u}),{html:a,title:n.title||"Quality",selector:d,select:r.select}}(o,e,h);if(!h())return;if(u.update(e,r,h),!h())return;const c=n.audio||{},s=function(e,n,o){const r=e.getTracksFor("audio");if(!o()||!r?.length)return null;const l=n.auto||"Auto",i=n.getName||(t=>t.lang||t.id),c=e.getCurrentTrackFor("audio")||r[0];if(!o())return null;const s=c?i(c):l;if(!o())return null;const u=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(r,c),a=[];for(const t of r){const e=i(t);if(!o())return null;a.push({html:e,value:t,default:t===u})}const f=t(a);return{html:s,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(o,c,h);h()&&a.update(c,s,h)}catch(g){if(f()){const t=++s;try{d(()=>s===t)}catch(p){console.warn("ArtPlayer DASH cleanup failed:",p)}}throw g}}function g(){if(c)return;let t;c=!0,s++;const e=[d,...f.splice(0).map(([t,e])=>()=>r.off(t,e))];for(const o of e)try{o()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",h],["restart",h],["destroy",g]];for(const e of t){if(c)break;f.push(e),r.on(...e)}}catch(p){try{g()}catch(y){console.warn("ArtPlayer DASH subscription cleanup failed:",y)}throw p}return{name:"artplayerPluginDashControl",update:h}}}}); diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js index a270906b6..284833af3 100644 --- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js +++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,l=(t,l)=>{for(var i in l||(l={}))n.call(l,i)&&r(t,i,l[i]);if(e)for(var i of e(l))o.call(l,i)&&r(t,i,l[i]);return t};function i(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){var l,i,c,u;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(l=s.cache)?void 0:l.get)&&(null==(c=null==(i=s.cache.get(e))?void 0:i.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(u=s.find(e))?void 0:u.onSelect)!==o||s.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,u,s){if(!u)return void i();const a={};o=a;const f=()=>a===o&&s(),d=e=>f()?(u.select(e,f),f()?(t.notice.show=`${u.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:u.html,style:{padding:"0 10px"},selector:u.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:u.html,html:u.title,icon:n,width:200,selector:u.selector,onSelect:d})):l("setting"))},clear:i}}return function(t={}){return e=>{const{$video:n}=e.template,{errorHandle:o}=e.constructor.utils;let r=!1,u=0;const s=c(e,"dash-quality",''),a=c(e,"dash-audio",''),f=[];function d(t=()=>!0){let e;for(const o of[s.clear,a.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function h(){if(r||e.isDestroy)return;const c=++u,f=e.dash,h=()=>!r&&!e.isDestroy&&c===u&&e.dash===f,p=()=>h()&&f.getVideoElement()===n&&h();try{if(o(f.getVideoElement()===n,'Cannot find instance of DASH from "art.dash"'),!h())return;const e=t.quality||{},r=function(t,e,n){const o=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),r=o.levels();if(!n()||!(null==r?void 0:r.length))return null;const c=e.auto||"Auto",u=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const a=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!a&&s?u(s):c;if(!n())return null;const d=[];for(const[i,p]of r.entries()){const t=u(p);if(!n())return null;d.push(l({html:t},o.item(p,i,s,a)))}const h=i(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:a}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(f,e,p);if(!p())return;if(s.update(e,r,p),!p())return;const c=t.audio||{},u=function(t,e,n){const o=t.getTracksFor("audio");if(!n()||!(null==o?void 0:o.length))return null;const r=e.auto||"Auto",l=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const u=c?l(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=t.filter(t=>["id","index","lang"].every(n=>null==e[n]||t[n]===e[n]));return 1===n.length?n[0]:void 0}(o,c),a=[];for(const i of o){const t=l(i);if(!n())return null;a.push({html:t,value:i,default:i===s})}const f=i(a);return{html:u,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(f,c,p);p()&&a.update(c,u,p)}catch(g){if(h()){const t=++u;try{d(()=>u===t)}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw g}}function p(){if(r)return;let t;r=!0,u++;const n=[d,...f.splice(0).map(([t,n])=>()=>e.off(t,n))];for(const e of n)try{e()}catch(o){t||(t=o)}if(t)throw t}try{for(const t of[["ready",h],["restart",h],["destroy",p]]){if(r)break;f.push(t),e.on(...t)}}catch(g){try{p()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw g}return{name:"artplayerPluginDashControl",update:h}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,l=(t,l)=>{for(var i in l||(l={}))n.call(l,i)&&r(t,i,l[i]);if(e)for(var i of e(l))o.call(l,i)&&r(t,i,l[i]);return t};function i(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function l(n,o=r.get(n)){var l,i,c,u;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(l=s.cache)?void 0:l.get)&&(null==(c=null==(i=s.cache.get(e))?void 0:i.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(u=s.find(e))?void 0:u.onSelect)!==o||s.remove(e)}function i(){let t;o=void 0;for(const[n,o]of[...r])try{l(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,u,s){if(!u)return void i();const a={};o=a;const f=()=>a===o&&s(),d=e=>f()?(u.select(e,f),f()?(t.notice.show=`${u.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:u.html,style:{padding:"0 10px"},selector:u.selector,onSelect:d})):l("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:u.html,html:u.title,icon:n,width:200,selector:u.selector,onSelect:d})):l("setting"))},clear:i}}return function(t={}){return e=>{const n=e,{$video:o}=n.template,{errorHandle:r}=n.constructor.utils;let u=!1,s=0;const a=c(n,"dash-quality",''),f=c(n,"dash-audio",''),d=[];function h(t=()=>!0){let e;for(const o of[a.clear,f.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(u||n.isDestroy)return;const e=++s,c=n.dash,d=()=>!u&&!n.isDestroy&&e===s&&n.dash===c,p=()=>d()&&c.getVideoElement()===o&&d();try{if(r(c.getVideoElement()===o,'Cannot find instance of DASH from "art.dash"'),!d())return;const e=t.quality||{},n=function(t,e,n){const o=function(t){const e="function"===typeof t.getRepresentationsByType;return{levels:()=>e?t.getRepresentationsByType("video"):t.getBitrateInfoListFor("video"),current(n){if(e)return t.getCurrentRepresentationForType("video");const o=t.getQualityFor("video");return n.find(t=>t.qualityIndex===o)},item:(t,n,o,r)=>e?{value:n,id:t.id,default:!r&&null!=o&&t.id===o.id}:{value:t.qualityIndex,default:!r&&null!=o&&t.qualityIndex===o.qualityIndex},select(n,o){const r="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:r}}}}),!r&&o()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),r=o.levels();if(!n()||!(null==r?void 0:r.length))return null;const c=e.auto||"Auto",u=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const a=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!a&&s?u(s):c;if(!n())return null;const d=[];for(const[i,p]of r.entries()){const t=u(p);if(!n())return null;d.push(l({html:t},o.item(p,i,s,a)))}const h=i(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:a}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(c,e,p);if(!p())return;if(a.update(e,n,p),!p())return;const u=t.audio||{},s=function(t,e,n){const o=t.getTracksFor("audio");if(!n()||!(null==o?void 0:o.length))return null;const r=e.auto||"Auto",l=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const u=c?l(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(o,c),a=[];for(const i of o){const t=l(i);if(!n())return null;a.push({html:t,value:i,default:i===s})}const f=i(a);return{html:u,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,u,p);p()&&f.update(u,s,p)}catch(g){if(d()){const t=++s;try{h(()=>s===t)}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw g}}function g(){if(u)return;let t;u=!0,s++;const e=[h,...d.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(o){t||(t=o)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",g]];for(const e of t){if(u)break;d.push(e),n.on(...e)}}catch(v){try{g()}catch(y){console.warn("ArtPlayer DASH subscription cleanup failed:",y)}throw v}return{name:"artplayerPluginDashControl",update:p}}}}); diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs index a0c85dbe8..09be10ebd 100644 --- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs +++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs @@ -77,7 +77,8 @@ function selectedTrack(tracks, current) { return current; if (current.id == null && current.index == null) return void 0; - const matching = tracks.filter((track) => ["id", "index", "lang"].every((key) => current[key] == null || track[key] === current[key])); + const fields = ["id", "index", "lang"]; + const matching = tracks.filter((track) => fields.every((key) => current[key] == null || track[key] === current[key])); return matching.length === 1 ? matching[0] : void 0; } function audioModel(dash, config, active) { @@ -170,7 +171,8 @@ function createMenu(art, name, icon) { } const $quality = ''; function artplayerPluginDashControl(option = {}) { - return (art) => { + return (player) => { + const art = player; const { $video } = art.template; const { errorHandle } = art.constructor.utils; let closed = false; @@ -244,7 +246,8 @@ function artplayerPluginDashControl(option = {}) { throw failure; } try { - for (const entry of [["ready", update], ["restart", update], ["destroy", destroy]]) { + const entries = [["ready", update], ["restart", update], ["destroy", destroy]]; + for (const entry of entries) { if (closed) break; subscriptions.push(entry); diff --git a/packages/artplayer-plugin-dash-control/package.json b/packages/artplayer-plugin-dash-control/package.json index b337d2fda..92b095b9a 100644 --- a/packages/artplayer-plugin-dash-control/package.json +++ b/packages/artplayer-plugin-dash-control/package.json @@ -19,19 +19,36 @@ ], "exports": { ".": { - "types": "./types/artplayer-plugin-dash-control.d.ts", - "import": "./dist/artplayer-plugin-dash-control.mjs", - "require": "./dist/artplayer-plugin-dash-control.js" + "import": { + "types": "./types/artplayer-plugin-dash-control.d.mts", + "default": "./dist/artplayer-plugin-dash-control.mjs" + }, + "require": { + "types": "./types/artplayer-plugin-dash-control.d.cts", + "default": "./dist/artplayer-plugin-dash-control.js" + } }, "./legacy": { - "types": "./types/artplayer-plugin-dash-control.d.ts", - "import": "./dist/artplayer-plugin-dash-control.legacy.js", - "require": "./dist/artplayer-plugin-dash-control.legacy.js" + "import": { + "types": "./types/artplayer-plugin-dash-control.d.cts", + "default": "./dist/artplayer-plugin-dash-control.legacy.js" + }, + "require": { + "types": "./types/artplayer-plugin-dash-control.d.cts", + "default": "./dist/artplayer-plugin-dash-control.legacy.js" + } } }, "main": "./dist/artplayer-plugin-dash-control.js", "module": "./dist/artplayer-plugin-dash-control.mjs", "types": "./types/artplayer-plugin-dash-control.d.ts", "legacy": "./dist/artplayer-plugin-dash-control.legacy.js", - "browserslist": "last 1 Chrome version" + "browserslist": "last 1 Chrome version", + "typesVersions": { + "*": { + "legacy": [ + "types/artplayer-plugin-dash-control.d.ts" + ] + } + } } diff --git a/packages/artplayer-plugin-dash-control/src/index.js b/packages/artplayer-plugin-dash-control/src/index.ts similarity index 69% rename from packages/artplayer-plugin-dash-control/src/index.js rename to packages/artplayer-plugin-dash-control/src/index.ts index 897fb28a0..d120dc332 100644 --- a/packages/artplayer-plugin-dash-control/src/index.js +++ b/packages/artplayer-plugin-dash-control/src/index.ts @@ -1,20 +1,25 @@ +import type Artplayer from 'artplayer' +import type { AudioTrack, Option, QualityLevel, Result } from '../types/artplayer-plugin-dash-control' +import type { AudioItem, Cleanup, EventName, Host, QualityItem, Valid } from './types' import $audio from './audio.svg?raw' import { audioModel, qualityModel } from './mapping' import { createMenu } from './menu' import $quality from './quality.svg?raw' -export default function artplayerPluginDashControl(option = {}) { - return (art) => { +export default function artplayerPluginDashControl(option: Option = {}) { + return (player: Artplayer): Result => { + // art.dash is caller-owned; update keeps the historical SDK/media validation boundary. + const art = player as unknown as Host const { $video } = art.template const { errorHandle } = art.constructor.utils let closed = false let revision = 0 - const quality = createMenu(art, 'dash-quality', $quality) - const audio = createMenu(art, 'dash-audio', $audio) - const subscriptions = [] + const quality = createMenu(art, 'dash-quality', $quality) + const audio = createMenu>(art, 'dash-audio', $audio) + const subscriptions: [EventName, Cleanup][] = [] - function clear(current = () => true) { - let failure + function clear(current: Valid = () => true): void { + let failure: unknown for (const cleanup of [quality.clear, audio.clear]) { if (!current()) break @@ -29,11 +34,11 @@ export default function artplayerPluginDashControl(option = {}) { throw failure } - function update() { + function update(): void { if (closed || art.isDestroy) return const version = ++revision - const dash = art.dash + const dash = art.dash! const current = () => !closed && !art.isDestroy && version === revision && art.dash === dash const valid = () => current() && dash.getVideoElement() === $video && current() try { @@ -66,12 +71,12 @@ export default function artplayerPluginDashControl(option = {}) { } } - function destroy() { + function destroy(): void { if (closed) return closed = true revision++ - let failure + let failure: unknown const actions = [clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))] for (const action of actions) { try { @@ -86,7 +91,8 @@ export default function artplayerPluginDashControl(option = {}) { } try { - for (const entry of [['ready', update], ['restart', update], ['destroy', destroy]]) { + const entries: [EventName, Cleanup][] = [['ready', update], ['restart', update], ['destroy', destroy]] + for (const entry of entries) { if (closed) break subscriptions.push(entry) diff --git a/packages/artplayer-plugin-dash-control/src/mapping.js b/packages/artplayer-plugin-dash-control/src/mapping.ts similarity index 57% rename from packages/artplayer-plugin-dash-control/src/mapping.js rename to packages/artplayer-plugin-dash-control/src/mapping.ts index 88b4b71dd..47b845b90 100644 --- a/packages/artplayer-plugin-dash-control/src/mapping.js +++ b/packages/artplayer-plugin-dash-control/src/mapping.ts @@ -1,7 +1,9 @@ +import type { Config } from '../types/artplayer-plugin-dash-control' +import type { AudioFields, AudioItem, Dash, Label, MenuModel, QualityFields, QualityItem, SelectorItem, Valid } from './types' import { qualityAdapter } from './sdk' -function uniqueLabels(items) { - const seen = new Map() +function uniqueLabels(items: Item[]): Item[] { + const seen = new Map() return items.filter((item) => { if (item.html === undefined) return true @@ -16,13 +18,13 @@ function uniqueLabels(items) { }) } -export function qualityModel(dash, config, active) { +export function qualityModel(dash: Dash, config: Config, active: Valid): MenuModel | null { const adapter = qualityAdapter(dash) const levels = adapter.levels() if (!active() || !levels?.length) return null const auto = config.auto || 'Auto' - const getName = config.getName || (level => `${level.height}p`) + const getName = config.getName || ((level: QualityFields) => `${level.height}p`) const selected = adapter.current(levels) if (!active()) return null @@ -32,7 +34,7 @@ export function qualityModel(dash, config, active) { const html = !automatic && selected ? getName(selected) : auto if (!active()) return null - const items = [] + const items: QualityItem[] = [] for (const [index, level] of levels.entries()) { const label = getName(level) if (!active()) @@ -40,34 +42,37 @@ export function qualityModel(dash, config, active) { items.push({ html: label, ...adapter.item(level, index, selected, automatic) }) } const selector = uniqueLabels(items) - .sort((left, right) => right.value - left.value) + // The synthetic Auto item is appended after sorting SDK numeric keys. + .sort((left, right) => (right.value as number) - (left.value as number)) selector.push({ html: auto, value: 'auto', default: automatic }) return { html, title: config.title || 'Quality', selector, select: adapter.select } } -function selectedTrack(tracks, current) { +function selectedTrack(tracks: Track[], current: Track): Track | undefined { if (tracks.includes(current)) return current if (current.id == null && current.index == null) return undefined - const matching = tracks.filter(track => ['id', 'index', 'lang'].every(key => current[key] == null || track[key] === current[key])) + const fields = ['id', 'index', 'lang'] as const + const matching = tracks.filter(track => fields.every(key => current[key] == null || track[key] === current[key])) return matching.length === 1 ? matching[0] : undefined } -export function audioModel(dash, config, active) { +export function audioModel(dash: Dash, config: Config, active: Valid): MenuModel> | null { const tracks = dash.getTracksFor('audio') if (!active() || !tracks?.length) return null const auto = config.auto || 'Auto' - const getName = config.getName || (track => track.lang || track.id) - const current = dash.getCurrentTrackFor('audio') || tracks[0] + const getName = config.getName || ((track: AudioFields) => track.lang || track.id) + // The non-empty list check above protects the fallback item. + const current = dash.getCurrentTrackFor('audio') || tracks[0]! if (!active()) return null const html = current ? getName(current) : auto if (!active()) return null const selected = selectedTrack(tracks, current) - const items = [] + const items: AudioItem[] = [] for (const track of tracks) { const label = getName(track) if (!active()) diff --git a/packages/artplayer-plugin-dash-control/src/menu.js b/packages/artplayer-plugin-dash-control/src/menu.ts similarity index 74% rename from packages/artplayer-plugin-dash-control/src/menu.js rename to packages/artplayer-plugin-dash-control/src/menu.ts index 447709a90..64e2b3db8 100644 --- a/packages/artplayer-plugin-dash-control/src/menu.js +++ b/packages/artplayer-plugin-dash-control/src/menu.ts @@ -1,8 +1,10 @@ -export function createMenu(art, name, icon) { - let current - const owned = new Map() +import type { DisplayConfig, Label, MenuHost, MenuModel, SelectorItem, Valid } from './types' - function remove(surface, callback = owned.get(surface)) { +export function createMenu(art: MenuHost, name: string, icon: string) { + let current: object | undefined + const owned = new Map<'controls' | 'setting', (item: Item) => Label>() + + function remove(surface: 'controls' | 'setting', callback = owned.get(surface)): void { if (!callback || owned.get(surface) !== callback) return owned.delete(surface) @@ -14,9 +16,9 @@ export function createMenu(art, name, icon) { registry.remove(name) } - function clear() { + function clear(): void { current = undefined - let failure + let failure: unknown for (const [surface, callback] of [...owned]) { try { remove(surface, callback) @@ -29,7 +31,7 @@ export function createMenu(art, name, icon) { throw failure } - function update(config, model, active) { + function update(config: DisplayConfig, model: MenuModel | null, active: Valid): void { if (!model) { clear() return @@ -37,7 +39,7 @@ export function createMenu(art, name, icon) { const state = {} current = state const valid = () => state === current && active() - const onSelect = (item) => { + const onSelect = (item: Item): Label => { if (!valid()) return item.html model.select(item, valid) diff --git a/packages/artplayer-plugin-dash-control/src/sdk.js b/packages/artplayer-plugin-dash-control/src/sdk.ts similarity index 54% rename from packages/artplayer-plugin-dash-control/src/sdk.js rename to packages/artplayer-plugin-dash-control/src/sdk.ts index 49dfc0a6c..3f18db72b 100644 --- a/packages/artplayer-plugin-dash-control/src/sdk.js +++ b/packages/artplayer-plugin-dash-control/src/sdk.ts @@ -1,11 +1,14 @@ -export function qualityAdapter(dash) { +import type { Dash, QualityAdapter } from './types' + +export function qualityAdapter(dash: Dash): QualityAdapter { + // Select one SDK method family. Invalid/incomplete SDKs keep synchronous method errors. const modern = typeof dash.getRepresentationsByType === 'function' return { - levels: () => modern ? dash.getRepresentationsByType('video') : dash.getBitrateInfoListFor('video'), + levels: () => modern ? dash.getRepresentationsByType!('video') : dash.getBitrateInfoListFor!('video'), current(levels) { if (modern) - return dash.getCurrentRepresentationForType('video') - const quality = dash.getQualityFor('video') + return dash.getCurrentRepresentationForType!('video') + const quality = dash.getQualityFor!('video') return levels.find(level => level.qualityIndex === quality) }, item(level, index, selected, automatic) { @@ -18,9 +21,9 @@ export function qualityAdapter(dash) { dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: automatic } } } }) if (!automatic && valid()) { if (modern) - dash.setRepresentationForTypeById('video', item.id) + dash.setRepresentationForTypeById!('video', item.id) else - dash.setQualityFor('video', item.value) + dash.setQualityFor!('video', item.value as number | undefined) } }, } diff --git a/packages/artplayer-plugin-dash-control/src/types.ts b/packages/artplayer-plugin-dash-control/src/types.ts new file mode 100644 index 000000000..7e73ec882 --- /dev/null +++ b/packages/artplayer-plugin-dash-control/src/types.ts @@ -0,0 +1,99 @@ +import type { Config } from '../types/artplayer-plugin-dash-control' + +export type Label = string | number | undefined +export type Identifier = string | number +export type Valid = () => boolean +export type Cleanup = () => void +export type EventName = 'ready' | 'restart' | 'destroy' + +export interface QualityFields { + height?: number + id?: Identifier + qualityIndex?: number +} + +export interface AudioFields { + id?: Identifier + index?: number + lang?: string +} + +export interface Dash { + getVideoElement: () => object | null + getSettings: () => { streaming: { abr: { autoSwitchBitrate: { video: boolean } } } } + updateSettings: (settings: { streaming: { abr: { autoSwitchBitrate: { video: boolean } } } }) => unknown + getRepresentationsByType?: (type: 'video') => (Level & QualityFields)[] | null | undefined + getCurrentRepresentationForType?: (type: 'video') => (Level & QualityFields) | null | undefined + setRepresentationForTypeById?: (type: 'video', id: Identifier | undefined) => unknown + getBitrateInfoListFor?: (type: 'video') => (Level & QualityFields)[] | null | undefined + getQualityFor?: (type: 'video') => number + setQualityFor?: (type: 'video', index: number | undefined) => unknown + getTracksFor: (type: 'audio') => (Track & AudioFields)[] | null | undefined + getCurrentTrackFor: (type: 'audio') => (Track & AudioFields) | null | undefined + setCurrentTrack: (track: Track & AudioFields) => unknown +} + +export interface SelectorItem { + html: Label + default: boolean +} + +export interface QualityItem extends SelectorItem { + value: number | 'auto' | undefined + id?: Identifier +} + +export interface AudioItem extends SelectorItem { + value: Track & AudioFields +} + +export interface QualityAdapter { + levels: () => (Level & QualityFields)[] | null | undefined + current: (levels: (Level & QualityFields)[]) => (Level & QualityFields) | null | undefined + item: (level: Level & QualityFields, index: number, selected: (Level & QualityFields) | null | undefined, automatic: boolean) => Omit + select: (item: QualityItem, valid: Valid) => void +} + +export interface MenuModel { + html: Label + title: string + selector: Item[] + select: (item: Item, valid: Valid) => unknown +} + +export interface MenuOption { + name: string + html: Label + selector: Item[] + onSelect: (item: Item) => Label + position?: 'right' + style?: { padding: string } + tooltip?: Label + icon?: string + width?: number +} + +export interface Registry { + update: (option: MenuOption) => unknown + remove: (name: string) => unknown + check: (item: SelectorItem) => unknown + cache?: { get?: (name: string) => { option?: { onSelect?: unknown } } | undefined } + find?: (name: string) => { onSelect?: unknown } | undefined +} + +export interface MenuHost { + controls: Registry + setting: Registry + notice: { show: string } +} + +export interface Host extends MenuHost { + template: { $video: object } + constructor: { utils: { errorHandle: (condition: unknown, message: string) => void } } + isDestroy?: boolean + dash?: Dash + on: (name: EventName, callback: Cleanup) => unknown + off: (name: EventName, callback: Cleanup) => unknown +} + +export type DisplayConfig = Pick diff --git a/packages/artplayer-plugin-dash-control/tsconfig.json b/packages/artplayer-plugin-dash-control/tsconfig.json new file mode 100644 index 000000000..bba301755 --- /dev/null +++ b/packages/artplayer-plugin-dash-control/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "allowJs": false }, + "include": ["src/**/*.ts", "types/**/*.d.ts", "types/**/*.d.mts", "types/**/*.d.cts", "../../types/assets.d.ts"] +} diff --git a/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.cts b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.cts new file mode 100644 index 000000000..372198133 --- /dev/null +++ b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.cts @@ -0,0 +1,14 @@ +/* eslint-disable ts/no-redeclare -- CommonJS callable export and named-type namespace intentionally merge. */ +import type * as Definition from './artplayer-plugin-dash-control.js' + +declare const artplayerPluginDashControl: typeof Definition.default + +declare namespace artplayerPluginDashControl { + type QualityLevel = Definition.QualityLevel + type AudioTrack = Definition.AudioTrack + type Config = Definition.Config + type Option = Definition.Option + type Result = Definition.Result +} + +export = artplayerPluginDashControl diff --git a/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.mts b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.mts new file mode 100644 index 000000000..764ac96f7 --- /dev/null +++ b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.mts @@ -0,0 +1,2 @@ +export { default } from './artplayer-plugin-dash-control.cjs' +export type { AudioTrack, Config, Option, QualityLevel, Result } from './artplayer-plugin-dash-control.js' diff --git a/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts index 281889c78..832a1b35a 100644 --- a/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts +++ b/packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts @@ -1,16 +1,41 @@ import type Artplayer from 'artplayer' -interface Config { +export interface QualityLevel { + height: number + width?: number + id?: string | number + qualityIndex?: number + bitrate?: number + bitrateInKbit?: number +} + +export interface AudioTrack { + id?: string | number + index?: number + lang?: string +} + +export interface Config { control?: boolean setting?: boolean title?: string auto?: string - getName?: (level: object) => string + /** Called without a receiver or index, with the original SDK object. */ + getName?: (item: Item) => string } -declare const artplayerPluginDashControl: (option: { quality?: Config, audio?: Config }) => (art: Artplayer) => { +export interface Option { + quality?: Config + audio?: Config +} + +export interface Result { name: 'artplayerPluginDashControl' update: () => void } +declare function artplayerPluginDashControl(option?: Option): (art: Artplayer) => Result +// Preserve the required last signature for historical Parameters[0] consumers. +declare function artplayerPluginDashControl(option: Option): (art: Artplayer) => Result + export default artplayerPluginDashControl diff --git a/refactor/baselines/dash-types-validation.json b/refactor/baselines/dash-types-validation.json new file mode 100644 index 000000000..7b43ccc4f --- /dev/null +++ b/refactor/baselines/dash-types-validation.json @@ -0,0 +1,1004 @@ +{ + "schemaVersion": 1, + "task": "PKG-DASH-04", + "baseCommit": "7de04573", + "scope": "Strict owned TS, public/old declarations, generated real Monaco types, formal main/legacy DOM+native MP4 and isolated tarball public types. Actual dash.js/MPD/ABR, physical devices and complete installed runtime release acceptance remain separate.", + "inputs": [ + { + "file": "packages/artplayer-plugin-dash-control/src/index.ts", + "sha256LF": "5f803005e23d424f3fce0ddd5677eea54a048b052891c3c65947c2fe57088afa" + }, + { + "file": "packages/artplayer-plugin-dash-control/src/mapping.ts", + "sha256LF": "d3a3c65f3879cb1322fac55d78e5e9527d1994f7b4052ba9743412cb08c1757e" + }, + { + "file": "packages/artplayer-plugin-dash-control/src/menu.ts", + "sha256LF": "87e94b9a7a44a2211676eb088009725b48c4d714bc0ee88b1e3151fb770548f5" + }, + { + "file": "packages/artplayer-plugin-dash-control/src/sdk.ts", + "sha256LF": "9afd4dbc949648d7f9bb00d57dba04c71505cdd23312813f60c83a5f5506dc69" + }, + { + "file": "packages/artplayer-plugin-dash-control/src/types.ts", + "sha256LF": "4c22680e6413d981867ebe86c4173dd5f91c0eebe3ebe775ef54aa1fe92ded98" + }, + { + "file": "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "sha256LF": "3d78575e9d8ffdf4d9ce79ddd9a4101faada4026c0bcad3a33065d98ab873290" + }, + { + "file": "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.cts", + "sha256LF": "20cbbeed90e0cd7d7b536b945dd01f31679e4325854aa36b732cb8e6ccc70d02" + }, + { + "file": "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.mts", + "sha256LF": "f52ae616c501be0685a14cb4e48252c88a67c9d4b0a9c1138921de89521f6b81" + }, + { + "file": "packages/artplayer-plugin-dash-control/tsconfig.json", + "sha256LF": "583e4ac1afa99e3a29c7f899df9fdeefcbd14d3847d69143e3fa6a8facac6d99" + }, + { + "file": "packages/artplayer-plugin-dash-control/package.json", + "sha256LF": "0757055e9d84e95a3fe2278b86aefad75ddf6e3a902a2e010645f82d9936a158" + }, + { + "file": "packages/artplayer-plugin-dash-control/.npmignore", + "sha256LF": "145c5fa0341195e21cf13df3423a057282f8ea374e9a1b3065e2280e1ce1c5c1" + }, + { + "file": "test/types/dash-control.ts", + "sha256LF": "c6552137c332a9c05bf31c3299e18814cb32976f1a23efb9939e4cae9df2d6a5" + }, + { + "file": "test/types/dash-source.ts", + "sha256LF": "ba8b9b24cca2047edd8e9597780f974d5d36c1e2af8c417af273ee8e82b472f6" + }, + { + "file": "refactor/fixtures/consumers/dash-control-legacy.ts", + "sha256LF": "b9aa4acf17d18820824814a59c3b4ab94c719570791a476164252be3741ebe9c" + }, + { + "file": "refactor/scripts/dash-types.test.mjs", + "sha256LF": "812166d0b34863f896ba89e8cb2df90cbe296955da5835debaad54d777d37fc2" + }, + { + "file": "refactor/scripts/dash-package-types.mjs", + "sha256LF": "de5323c07adf5c45e005c26d7f88eef9f30384d04310a498959a69769936d504" + }, + { + "file": "scripts/build-ts.js", + "sha256LF": "c364c032572d89ac8edef582744304ec789dd60cc16a0073f836963ab03651b1" + }, + { + "file": "scripts/typecheck.mjs", + "sha256LF": "b5476f248f54ab05e6b6cb100eefb284273c61ce08b6615af089c68617384134" + }, + { + "file": "test/browser/dash-editor-types.spec.js", + "sha256LF": "d1821a159da83d9b3647aa02061adaaa0870fbd6ae41d5c5088c7b3876dca592" + }, + { + "file": "docs/assets/ts/artplayer-plugin-dash-control.d.ts", + "sha256LF": "4b803c2c6cb55fbe3c60bc94ac1c3e0267bba1ae032da5ed56bba9eb27574688" + } + ], + "artifacts": [ + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js", + "sha256": "9ae800d1e8b3179d5c89baa567b5990cc1e9abd1b9d1cc6ee5c67a8e74e764fd", + "bytes": 5489 + }, + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js", + "sha256": "59e6545eabdf8f60e6897494de4faa472406ea8062eff2bdeb0ec2f17491aa49", + "bytes": 5944 + }, + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs", + "sha256": "42538d13a39d219ba2fbf7f2812440b6096dd7a143e20e4ec1d2cbcdd7a308ab", + "bytes": 9807 + } + ], + "reports": [ + { + "file": "refactor/.cache/dash04-main/report.json", + "sha256": "d1caa56074b8cdb02a077089b650cbc64ffce23f65ab4282e0c9cbf0ef78841f", + "bytes": 15881081, + "stats": { + "startTime": "2026-09-11T21:08:03.045Z", + "duration": 68554.279, + "expected": 90, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "results": { + "directory": "refactor/.cache/dash04-main/results", + "files": 91, + "sha256": "b6bbaec02c63b6eac396c4c8606fcb2f43cb99946e5671ef4d361424424b4ba1", + "algorithm": "SHA256 of JSON.stringify(sorted relative path and raw SHA256 pairs), UTF-8, slash paths" + }, + "plugin": { + "published": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-dash-control/1.1.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-dash-control/-/artplayer-plugin-dash-control-1.1.0.tgz", + "integrity": "sha512-EssRkzYXWcln3a6nAzi4ESacBhzMih8s0bzdjXuvBBCh2UFmvK99ITD5E+0cuU8i5ZzIQGQSdce1/gANTrX0BQ==", + "sha256": "f4047a569d99b6a7de57b3719b7ad40109f2a1d360566a840f50ab32aacefcd9", + "archiveBytes": 3385, + "registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96", + "files": { + "package/README.md": "98982f1123ff9a11f5d3d9a52da6e8fea9cd0989f7465605c27b0f9a46f2dbcd", + "package/dist/artplayer-plugin-dash-control.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.legacy.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.mjs": "e32bf08c33a481781883067dcc639f0495c9dcb8a88b91be13736370f4932fc4", + "package/package.json": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "package/types/artplayer-plugin-dash-control.d.ts": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba" + }, + "manifest": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "description": "Dash control plugin for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.mjs", + "require": "./dist/artplayer-plugin-dash-control.js" + }, + "./legacy": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.legacy.js", + "require": "./dist/artplayer-plugin-dash-control.legacy.js" + } + }, + "main": "./dist/artplayer-plugin-dash-control.js", + "module": "./dist/artplayer-plugin-dash-control.mjs", + "types": "./types/artplayer-plugin-dash-control.d.ts", + "legacy": "./dist/artplayer-plugin-dash-control.legacy.js", + "browserslist": "last 1 Chrome version" + } + }, + "sourceSHA256": "9ae800d1e8b3179d5c89baa567b5990cc1e9abd1b9d1cc6ee5c67a8e74e764fd", + "candidate": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js", + "scope": "Real ArtPlayer DOM and native MP4 with controlled SDK methods. No dash.js instance, MPD, ABR or adaptive media validation." + }, + "editor": { + "candidate": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js", + "sha256": "9ae800d1e8b3179d5c89baa567b5990cc1e9abd1b9d1cc6ee5c67a8e74e764fd", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-plugin-dash-control": "4b803c2c6cb55fbe3c60bc94ac1c3e0267bba1ae032da5ed56bba9eb27574688" + } + }, + "engines": [ + { + "project": "chromium", + "version": "153.0.8010.12", + "platform": "win32" + }, + { + "project": "firefox", + "version": "155.0", + "platform": "win32" + }, + { + "project": "webkit", + "version": "26.6", + "platform": "win32" + } + ], + "manifest": { + "kind": "artplayer-browser-test", + "resources": { + "/candidate/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040 + }, + "/compiled/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040, + "aliasOf": "/candidate/artplayer.js" + }, + "/uncompiled/artplayer/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040, + "aliasOf": "/candidate/artplayer.js" + }, + "/candidate/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529 + }, + "/compiled/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/uncompiled/artplayer-plugin-chapter/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/published/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "/published/artplayer-plugin-chapter.js": { + "source": { + "kind": "npm-release", + "version": "1.1.0", + "integrity": "sha512-ji66Lr2K2S+OjFMuMby3yq+pefWlOC51Sbpop/DIr2N6w3F0smrF+YZbshN1Dy8/Tx1TYpT7N6WkkZWy5Jix1w==", + "member": "package/dist/artplayer-plugin-chapter.js" + }, + "sha256": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "bytes": 4973 + }, + "/test/pattern.mp4": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.mp4" + }, + "sha256": "0ad1d7ad286aec6e0fa9e27fc1e487e7277df2e603e30040fb942365763d0233", + "bytes": 251538 + }, + "/test/pattern.webm": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.webm" + }, + "sha256": "9d3c2b3dec9581a599de2d6732992ef7846790e0d1368e6e131bd339b39e984f", + "bytes": 162073 + }, + "/test/audio-tone.m4a": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/audio-tone.m4a" + }, + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "/test/declaration-cues.vtt": { + "source": { + "kind": "test-subtitles", + "file": "test/browser/media/declaration-cues.vtt" + }, + "sha256": "5d149d4e641ce41cbb83376835c03daf1fb89f3b9f568fa59e064d51e2b57704", + "bytes": 54 + }, + "/test/thumbnail-grid.svg": { + "source": { + "kind": "test-thumbnail-grid", + "file": "test/browser/media/thumbnail-grid.svg" + }, + "sha256": "bb3f1ec9895d37def77ca4b192d3cd73577a2668558cc99de1588f511d65d3e6", + "bytes": 14969 + }, + "/test/legacy-safe-area.js": { + "source": { + "kind": "frozen-own-source", + "file": "test/helpers/legacy-safe-area.js", + "commit": "ccf77c4e" + }, + "sha256": "ce0368618ef6363b78271b8bcc7973abd0478e797cc464ddd912929e6dfa501a", + "bytes": 795 + }, + "/assets/sample/video.mp4": { + "source": { + "kind": "docs-media", + "file": "docs/assets/sample/video.mp4" + }, + "sha256": "30a2de667ba267b6bc605edcee1c4bd728c048ed76f500ea0e73d1f1a6f8f980", + "bytes": 5818706 + }, + "/test/performance.js": { + "source": { + "kind": "adapted-baseline-fixture", + "file": "refactor/fixtures/performance.js" + }, + "sha256": "2318f39bdb5f3a3348d755cb4335907ae4adc944be3a435839c1bba201e81500", + "bytes": 8994 + } + } + } + }, + { + "file": "refactor/.cache/dash04-legacy/report.json", + "sha256": "8dcabd3fa7ec4a85da9c674baec92afe4f077c274f8d73bc8b5b3a6df960c41c", + "bytes": 15808265, + "stats": { + "startTime": "2026-09-11T21:10:33.393Z", + "duration": 67117.341, + "expected": 84, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "results": { + "directory": "refactor/.cache/dash04-legacy/results", + "files": 85, + "sha256": "0396c2c655adf364a419c751dd4ddd8ef4dbe304580e033d54c704403348d618", + "algorithm": "SHA256 of JSON.stringify(sorted relative path and raw SHA256 pairs), UTF-8, slash paths" + }, + "plugin": { + "published": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-dash-control/1.1.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-dash-control/-/artplayer-plugin-dash-control-1.1.0.tgz", + "integrity": "sha512-EssRkzYXWcln3a6nAzi4ESacBhzMih8s0bzdjXuvBBCh2UFmvK99ITD5E+0cuU8i5ZzIQGQSdce1/gANTrX0BQ==", + "sha256": "f4047a569d99b6a7de57b3719b7ad40109f2a1d360566a840f50ab32aacefcd9", + "archiveBytes": 3385, + "registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96", + "files": { + "package/README.md": "98982f1123ff9a11f5d3d9a52da6e8fea9cd0989f7465605c27b0f9a46f2dbcd", + "package/dist/artplayer-plugin-dash-control.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.legacy.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.mjs": "e32bf08c33a481781883067dcc639f0495c9dcb8a88b91be13736370f4932fc4", + "package/package.json": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "package/types/artplayer-plugin-dash-control.d.ts": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba" + }, + "manifest": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "description": "Dash control plugin for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.mjs", + "require": "./dist/artplayer-plugin-dash-control.js" + }, + "./legacy": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.legacy.js", + "require": "./dist/artplayer-plugin-dash-control.legacy.js" + } + }, + "main": "./dist/artplayer-plugin-dash-control.js", + "module": "./dist/artplayer-plugin-dash-control.mjs", + "types": "./types/artplayer-plugin-dash-control.d.ts", + "legacy": "./dist/artplayer-plugin-dash-control.legacy.js", + "browserslist": "last 1 Chrome version" + } + }, + "sourceSHA256": "59e6545eabdf8f60e6897494de4faa472406ea8062eff2bdeb0ec2f17491aa49", + "candidate": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js", + "scope": "Real ArtPlayer DOM and native MP4 with controlled SDK methods. No dash.js instance, MPD, ABR or adaptive media validation." + }, + "editor": null, + "engines": [ + { + "project": "chromium", + "version": "153.0.8010.12", + "platform": "win32" + }, + { + "project": "firefox", + "version": "155.0", + "platform": "win32" + }, + { + "project": "webkit", + "version": "26.6", + "platform": "win32" + } + ], + "manifest": { + "kind": "artplayer-browser-test", + "resources": { + "/candidate/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040 + }, + "/compiled/artplayer.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040, + "aliasOf": "/candidate/artplayer.js" + }, + "/uncompiled/artplayer/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "df495dd386d32ea96be5006e1bbfda1ea91cfeb32aa21758197b46a155ebe0dc", + "bytes": 378040, + "aliasOf": "/candidate/artplayer.js" + }, + "/candidate/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529 + }, + "/compiled/artplayer-plugin-chapter.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/uncompiled/artplayer-plugin-chapter/index.js": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer-plugin-chapter\\src\\index.ts" + }, + "sha256": "1a8eb891d3690e2526e78a2e53183a25367b1d02a5fd349d821548edb731ee3a", + "bytes": 8529, + "aliasOf": "/candidate/artplayer-plugin-chapter.js" + }, + "/published/artplayer.js": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "/published/artplayer-plugin-chapter.js": { + "source": { + "kind": "npm-release", + "version": "1.1.0", + "integrity": "sha512-ji66Lr2K2S+OjFMuMby3yq+pefWlOC51Sbpop/DIr2N6w3F0smrF+YZbshN1Dy8/Tx1TYpT7N6WkkZWy5Jix1w==", + "member": "package/dist/artplayer-plugin-chapter.js" + }, + "sha256": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "bytes": 4973 + }, + "/test/pattern.mp4": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.mp4" + }, + "sha256": "0ad1d7ad286aec6e0fa9e27fc1e487e7277df2e603e30040fb942365763d0233", + "bytes": 251538 + }, + "/test/pattern.webm": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/pattern.webm" + }, + "sha256": "9d3c2b3dec9581a599de2d6732992ef7846790e0d1368e6e131bd339b39e984f", + "bytes": 162073 + }, + "/test/audio-tone.m4a": { + "source": { + "kind": "generated-media", + "file": "test/browser/media/audio-tone.m4a" + }, + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "/test/declaration-cues.vtt": { + "source": { + "kind": "test-subtitles", + "file": "test/browser/media/declaration-cues.vtt" + }, + "sha256": "5d149d4e641ce41cbb83376835c03daf1fb89f3b9f568fa59e064d51e2b57704", + "bytes": 54 + }, + "/test/thumbnail-grid.svg": { + "source": { + "kind": "test-thumbnail-grid", + "file": "test/browser/media/thumbnail-grid.svg" + }, + "sha256": "bb3f1ec9895d37def77ca4b192d3cd73577a2668558cc99de1588f511d65d3e6", + "bytes": 14969 + }, + "/test/legacy-safe-area.js": { + "source": { + "kind": "frozen-own-source", + "file": "test/helpers/legacy-safe-area.js", + "commit": "ccf77c4e" + }, + "sha256": "ce0368618ef6363b78271b8bcc7973abd0478e797cc464ddd912929e6dfa501a", + "bytes": 795 + }, + "/assets/sample/video.mp4": { + "source": { + "kind": "docs-media", + "file": "docs/assets/sample/video.mp4" + }, + "sha256": "30a2de667ba267b6bc605edcee1c4bd728c048ed76f500ea0e73d1f1a6f8f980", + "bytes": 5818706 + }, + "/test/performance.js": { + "source": { + "kind": "adapted-baseline-fixture", + "file": "refactor/fixtures/performance.js" + }, + "sha256": "2318f39bdb5f3a3348d755cb4335907ae4adc944be3a435839c1bba201e81500", + "bytes": 8994 + } + } + } + } + ], + "packageTypes": { + "file": "refactor/.cache/dash-package-types-s2YGLZ/report.json", + "sha256": "917054a4e82e48809cff682c6478d37b9b18fd4255f7bc16a85d51fed131013e", + "bytes": 28197, + "report": { + "task": "PKG-DASH-04", + "scope": "Packed current core and DASH, offline Yarn install and frozen reinstall outside workspace; isolated public type acceptance only, not complete package runtime/SDK/media release validation.", + "packages": [ + { + "name": "artplayer", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-s2YGLZ\\artplayer.tgz", + "sha256": "2139af017670a4f30f667bf5fdc1aa680f3e03b9c8d3cb767fc92d6345aedff5", + "files": { + "package/ARCHITECTURE.md": "7ebedaee4d06925e0f7bf2ad4a2e6093aa416c91ea4d6154015854baf507e95e", + "package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09", + "package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7", + "package/dist/artplayer.cts": "0e91e907d021db02a68004603e6190505fdc0806d873ebb6af171aa089a81da7", + "package/dist/artplayer.js": "dc1460b77abdd401158e098b25a1abf280c53bb92d401fc387a4864dc7b18357", + "package/dist/artplayer.legacy.js": "49781a25f3d680c7964eec066b792966da71f03ccc3e7936cd5e0e9563e83008", + "package/dist/artplayer.mjs": "ad4e69b1a7a9584ab79204b871f51f4ae6561dc2dece2c64fa9c4884acd36154", + "package/dist/artplayer.mts": "f6292596a9ccb1d43a6d6a12c35f859b1889ad15924c5ae0f9f280941aade313", + "package/dist/artplayer.ts": "26a42ac66811e70f68a8ebe62cbf304eb1993451b3e658961be98e7106162527", + "package/dist/component.ts": "aa1c43be67322781b4b355b143bc69d9f29d22e578e4974d6a13e957a7c0334f", + "package/dist/config.ts": "c4fd8681bf32b10c743e3e561c532520c8f32e43fd765524850b15e55cb91c72", + "package/dist/cssVar.ts": "e027d82fb4f01c9ce7559cf679b2d5ee1962d3f7e5b4dd64ca21f472b933efb6", + "package/dist/emitter.ts": "9c62551cdcc6dd3966bed8a7a85dd477435db977c91a294d2ac8f162f9592537", + "package/dist/events.ts": "af191f862af4ec8933b09113b8c074603ae254a8b70460d0f3006646cd74ae42", + "package/dist/i18n.ts": "34b0c684668044f7600c80b9d3979f8fb730138007b7cb4e7034326b340156fb", + "package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb", + "package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8", + "package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f", + "package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac", + "package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a", + "package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7", + "package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4", + "package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9", + "package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03", + "package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816", + "package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db", + "package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3", + "package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664", + "package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5", + "package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787", + "package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765", + "package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c", + "package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918", + "package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf", + "package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec", + "package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a", + "package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f", + "package/dist/icons.ts": "0d8e8e52e52130dd4a29bab551dd7a2a954797e42e92a6e068c85d583e23e6a4", + "package/dist/language.cts": "2248040d86a8e34626755c4ecf53ea0174706c653073cf0e15884a030d173120", + "package/dist/language.mts": "c391e863012c77769e661afbf8fa949e9373aa88d17f58cbba7f5696edb8077f", + "package/dist/legacy/language.ts": "c452ffd764ff9b29b819b3475c42395d2ea7a33560d8c186ef5df3844b9524c3", + "package/dist/option.ts": "850e5264219b8b438c80694402225b690b131786677a5da0d22739ec2352d263", + "package/dist/playback.ts": "1ceaed3aa1cef511fe8b20f4874647f6065c723ad805b742a0a9247e16bb1bb5", + "package/dist/player.ts": "f1e50257df2ea592d0a0e1f36078dda77eaf2cf3b3d795307501396562d99f28", + "package/dist/plugin.ts": "2a57e148fda00ee687da5036b890b0f611cb94bb403a1ed52602179060259d35", + "package/dist/quality.ts": "7ed6ed78cea52991b2f6ddd2bd3b7bd8c5146fdfff7486fe8b814c3e632f1567", + "package/dist/runtime.cts": "52cfff1daeb6841a90212c8fa8d46ca46d6650bb7ed1b59b62115d854b8ad652", + "package/dist/runtime.mts": "1b09bc0ee0f0ee2f788689eaff441bdeda1fab0ce783e71b61b9792d81072b03", + "package/dist/runtime.ts": "ec4b1d56993eb482a77eb0f94b701047549d009e6ba6433f27e7d464fef30c2b", + "package/dist/runtime/component.ts": "840213299c942fe058eb5436977ceec066b9dccda24c55ae9f464b399c979e39", + "package/dist/runtime/construction.ts": "24cc5cda1f614314db382271a9b0d5583b781894cfdf6c548700b1b5270467ee", + "package/dist/runtime/events.ts": "52848f57d35eb1037cf74c8877521edf99a015bcd77f30ef6bbcba1807a99bfe", + "package/dist/runtime/media.ts": "8828f80953aec3e95328cefe691661e8897cb27cb309483cf806ec45a8549ae3", + "package/dist/runtime/option.ts": "bb672e49ffe74a6fc78543217f6de80d7d738890146df0857c709d4dc283e4ce", + "package/dist/runtime/player.ts": "a4a24794e26cfb9e4310bf0f459a7a407e85bf81b4f3a4feb6294331af8036fb", + "package/dist/runtime/plugin.ts": "9ad482f2a77914a91450419680b8af6b314b926f0831c2f7f6d7c466690f95c9", + "package/dist/runtime/services.ts": "8e4648d8c61bff47343f9e1c2b995af69fc00947264b826109ceadcbaf553254", + "package/dist/runtime/setting.ts": "c48d20957b57f79f985ef76a2950cc8a5cfa25dafa8ec5939e562661778e6218", + "package/dist/runtime/subtitle.ts": "13241c4f9a87517aa2cf7b3e0bb8cedaa12fa0b4d3f8934285797583b752557e", + "package/dist/runtime/template.ts": "da9975513a1938e7f386f4f65f718f0fc595164bfeac8f0cd99a3e97a54919a1", + "package/dist/runtime/utils.ts": "cab9916f5224a3b047812b9f759cc9cc62105662599744067a6955020ab36916", + "package/dist/setting.ts": "d12a75b656fff70cbb30be4bffec46d9c4273636e8722bce971a4d2b188712a1", + "package/dist/subtitle.ts": "d25b470e47d6a9bd696d9c1ad8adb58e56a208f05695186d513bc8c43c2e8acf", + "package/dist/template.ts": "68aed9e97d006d6be4fabb8504ca84dafab770b0925f4d6e7319e67c82f21b0d", + "package/dist/utils.ts": "494ec447ab467a07ddd7a2c2abf778b3fce69fd28a09aaa8ced9759f8de2e3f6", + "package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954", + "package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0", + "package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0", + "package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef", + "package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e", + "package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8", + "package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32", + "package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e", + "package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b", + "package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c", + "package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830", + "package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5", + "package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960", + "package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd", + "package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e", + "package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81", + "package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6", + "package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870", + "package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177", + "package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3", + "package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34", + "package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3", + "package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08", + "package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff", + "package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f", + "package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4", + "package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31", + "package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8", + "package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d", + "package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c", + "package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd", + "package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e", + "package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7", + "package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153", + "package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe", + "package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b", + "package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09", + "package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce", + "package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8", + "package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712" + } + }, + { + "name": "artplayer-plugin-dash-control", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-s2YGLZ\\artplayer-plugin-dash-control.tgz", + "sha256": "d230246d579e11f932b03f5121c6fab28d189a3d9fd823658b4b932f25687df2", + "files": { + "package/ARCHITECTURE.md": "801deace1aceac30ec478ffa06e62cbde3fb3259fefc559b4eb6e95613f628e8", + "package/README.md": "46371d4dcdc474488359e3949183a203e54804607de2a23c91689fa577c7b3b0", + "package/dist/artplayer-plugin-dash-control.js": "9ae800d1e8b3179d5c89baa567b5990cc1e9abd1b9d1cc6ee5c67a8e74e764fd", + "package/dist/artplayer-plugin-dash-control.legacy.js": "59e6545eabdf8f60e6897494de4faa472406ea8062eff2bdeb0ec2f17491aa49", + "package/dist/artplayer-plugin-dash-control.mjs": "42538d13a39d219ba2fbf7f2812440b6096dd7a143e20e4ec1d2cbcdd7a308ab", + "package/package.json": "0757055e9d84e95a3fe2278b86aefad75ddf6e3a902a2e010645f82d9936a158", + "package/types/artplayer-plugin-dash-control.d.cts": "20cbbeed90e0cd7d7b536b945dd01f31679e4325854aa36b732cb8e6ccc70d02", + "package/types/artplayer-plugin-dash-control.d.mts": "f52ae616c501be0685a14cb4e48252c88a67c9d4b0a9c1138921de89521f6b81", + "package/types/artplayer-plugin-dash-control.d.ts": "9a520baf6c7108454d488b4ed1fd07e9c3c0683c82cb7ae85f7cbaac805949e9" + } + } + ], + "matrix": [ + { + "compiler": "5.9.3", + "mode": "node10-commonjs", + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "message": "Type 'void' is not assignable to type 'Promise'." + }, + { + "code": 2554, + "message": "Expected 0 arguments, but got 1." + }, + { + "code": 2322, + "message": "Type '\"dash\"' is not assignable to type '\"artplayerPluginDashControl\"'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option'." + }, + { + "code": 2339, + "message": "Property 'unknownProperty' does not exist on type 'QualityLevel'." + } + ] + }, + { + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "message": "Type 'void' is not assignable to type 'Promise'." + }, + { + "code": 2554, + "message": "Expected 0 arguments, but got 1." + }, + { + "code": 2322, + "message": "Type '\"dash\"' is not assignable to type '\"artplayerPluginDashControl\"'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option'." + }, + { + "code": 2339, + "message": "Property 'unknownProperty' does not exist on type 'QualityLevel'." + } + ] + }, + { + "compiler": "5.9.3", + "mode": "nodenext-esm", + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "message": "Type 'void' is not assignable to type 'Promise'." + }, + { + "code": 2554, + "message": "Expected 0 arguments, but got 1." + }, + { + "code": 2322, + "message": "Type '\"dash\"' is not assignable to type '\"artplayerPluginDashControl\"'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option'." + }, + { + "code": 2339, + "message": "Property 'unknownProperty' does not exist on type 'QualityLevel'." + } + ] + }, + { + "compiler": "5.9.3", + "mode": "bundler-esm", + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "message": "Type 'void' is not assignable to type 'Promise'." + }, + { + "code": 2554, + "message": "Expected 0 arguments, but got 1." + }, + { + "code": 2322, + "message": "Type '\"dash\"' is not assignable to type '\"artplayerPluginDashControl\"'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Target signature provides too few arguments. Expected 2 or more, but got 1." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option'." + }, + { + "code": 2339, + "message": "Property 'unknownProperty' does not exist on type 'QualityLevel'." + } + ] + }, + { + "compiler": "4.3.5", + "mode": "node10-commonjs", + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "message": "Type 'void' is not assignable to type 'Promise'." + }, + { + "code": 2554, + "message": "Expected 0 arguments, but got 1." + }, + { + "code": 2322, + "message": "Type '\"dash\"' is not assignable to type '\"artplayerPluginDashControl\"'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '(value: QualityLevel, index: number) => string' is not assignable to type '(item: QualityLevel) => string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'.\n Type 'number' is not assignable to type 'string'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type '() => number' is not assignable to type '(item: AudioTrack) => string'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Type 'string' is not assignable to type 'boolean | undefined'." + }, + { + "code": 2769, + "message": "No overload matches this call.\n Overload 1 of 2, '(option?: Option | undefined): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option | undefined'.\n Overload 2 of 2, '(option: Option): (art: Artplayer) => Result', gave the following error.\n Argument of type 'null' is not assignable to parameter of type 'Option'." + }, + { + "code": 2339, + "message": "Property 'unknownProperty' does not exist on type 'QualityLevel'." + } + ] + } + ] + } + }, + "logs": [ + { + "file": "refactor/.cache/dash04-types-first.log", + "sha256": "1722b7de63332fbb27d1b16ff53259f2d7711b81481b613b672cebc4c0d73b3a", + "bytes": 453 + }, + { + "file": "refactor/.cache/dash04-node-first.log", + "sha256": "eb8756edd8bab415e399089a728aedf920a5b4c93ad7225ecc0ea33dc4fe6f05", + "bytes": 11456 + }, + { + "file": "refactor/.cache/dash04-artifacts.log", + "sha256": "c5630239788a664ca4b336ba5881b62d9018f1e3a3856e095c6bdcaae8ba1a23", + "bytes": 31670 + }, + { + "file": "refactor/.cache/dash04-build.log", + "sha256": "284b484e71d65b4c0bd3f37f676274d00f0f1fbc7168c960ea8553bd0b19b17a", + "bytes": 1472 + }, + { + "file": "refactor/.cache/dash04-editor-build.log", + "sha256": "3ae75d177979679613b527b8b00828ffd44e08a41f9b210c2e52423fbba9a095", + "bytes": 1559 + }, + { + "file": "refactor/.cache/dash04-main.log", + "sha256": "3cc2751324c08133182cb29bce8f9801a78886f9ef30fb54c5b9d2b5fab9f0d3", + "bytes": 17074 + }, + { + "file": "refactor/.cache/dash04-legacy.log", + "sha256": "539e115240a76727589ab12ef57a2d1b9837dd94cf49c0edf452eb01f23318fb", + "bytes": 16163 + }, + { + "file": "refactor/.cache/dash04-ci.log", + "sha256": "afcd8c48a28809ce1a4471305e691d2801e17d1f575b64b51750873dee7b164d", + "bytes": 74919 + }, + { + "file": "refactor/.cache/dash04-package-before.log", + "sha256": "6a5f7f4ef15aa013a938bdb236b2d166a5274908ad249de95d2eaa8b1f1842be", + "bytes": 1139 + }, + { + "file": "refactor/.cache/dash04-package-first-pass.log", + "sha256": "deef62610167968fd27f58c5376ef7a05bc8671d4ba5cd40f680ab41fd73bb7f", + "bytes": 630 + }, + { + "file": "refactor/.cache/dash04-package-final.log", + "sha256": "6e92f2a15ee50ea3d592de52c4691b1a4639004939a949261247d65997fa5c0d", + "bytes": 630 + } + ], + "counts": { + "ownedTS": 5, + "productionTS": 261, + "sourceNode": 116, + "nodeWithArtifacts": 266, + "baselineTypes": 3, + "publicTypeModes": 5, + "isolatedTypeModes": 5, + "invalidUsesPerMode": 8, + "mainBrowser": 90, + "legacyBrowser": 84, + "monacoIncludedInMain": 6, + "ci": { + "unit": 661, + "engineering": 14, + "baseline": 40 + } + }, + "failure": "Initial package check rejected leaked tsconfig.json; .npmignore excludes it in final tarballs. No browser retries/skips. Raw reports, traces, logs and tarballs remain local cache, not uploaded evidence." +} diff --git a/refactor/changes/2026-09-12-PKG-DASH-04-types.md b/refactor/changes/2026-09-12-PKG-DASH-04-types.md new file mode 100644 index 000000000..29a5bcc47 --- /dev/null +++ b/refactor/changes/2026-09-12-PKG-DASH-04-types.md @@ -0,0 +1,49 @@ +# PKG-DASH-04:DASH 严格 TS 与兼容公开类型 + +起点 `7de04573`。四个已拆分 JS 模块迁移为 TS,新增 `src/types.ts` 描述窄宿主、两代 SDK +方法、selector/menu、事件和资源类型。纳入 strict/noUncheckedIndexedAccess,未加入 any、 +通配模块声明或 dash.js 依赖。源码工厂直接使用公开 Option/Result,并有双向可赋值夹具。 + +## 类型兼容 + +保留原 .d.ts 入口、工厂与字面量插件名、同步 update、单参数无接收者的 getName。 +新增 QualityLevel/AudioTrack/Config/Option/Result 命名类型及自定义 SDK 泛型;旧 object +回调依然可用。可选重载允许运行时原本支持的省略/undefined,最后保留必填重载以保护 +`Parameters[0]['quality']`。不要求消费者改变现有调用。 + +增加 ESM/CommonJS 类型桥和 legacy 的旧解析路径,所有 JS 分发文件路径不变。 +原始 npm 声明的 legacy 消费夹具在 TS 4.3.5/5.9.3 通过,候选运行同样夹具也通过。 +五种 TS/模块模式的新消费者零诊断,移除 expect-error 后八条非法调用全部被拒绝。 + +有限断言位于外部 art.dash 宿主、选定 SDK 方法族、非空轨道回退、Auto 与数字键边界。 +内部类型保留 SDK 字段缺失可能;断言不引入运行时修正,不把不完整 SDK 静默变成成功。 +源码与前一步运行代码相比只有局部别名和类型擦除,已知行为修正仍由 03 的契约测试保护。 +详情见 [包内架构](../../packages/artplayer-plugin-dash-control/ARCHITECTURE.md)。 + +## 编辑器与验证 + +build:ts 将 DASH 加入已存在的 AST 生成器,生成可独立编译的 callable/global 类型桥, +避免旧 default export 与 export= 混用。实际生成文件有可重复生成、TS 4.3/5.9 正反例验证。 +新增 Monaco worker 用例,加载真实声明、检查回调/旧类型提取、拒绝错误返回并执行编译输出。 +没有手改 docs/assets/ts,产物由标准脚本生成。实际 SDK 的类型/媒体组合仍由 05 核验。 + +新增 `yarn test:dash-types-package`,使用现有打包检查和隔离目录工具,在工作区外离线安装 +核心与 DASH tarball,冻结锁重装,逐文件核对实际安装字节,编译时禁止回工作区解析类型。 +首次打包发现新增 tsconfig.json 泄露,先保留失败再补 .npmignore;最终五种模式及每组八条 +非法调用通过。此脚本只验收隔离类型,不替代 06 的完整运行时分发验收。 + +最终证据见 [冻结记录](../baselines/dash-types-validation.json): + +| 验证 | 结果 | +| --- | --- | +| 严格源码 | DASH 5 个自有 TS 模块,全仓 261 个生产 TS 文件通过 | +| 声明与隔离类型 | 新增 3 项类型基线通过;工作区与隔离 tarball 各 5 种模式,8 条非法用法均拒绝;旧实际 npm 声明对照通过 | +| 运行时 | 源码 116 项、带三格式的 Node 266 项通过 | +| 浏览器 | main 90 项(含核心/DASH Monaco 6 项)、legacy 84 项通过,无重试/跳过 | +| 完整 CI | 715 项:661 单元、14 工程、40 基线;隔离打包脚本随后独立执行并通过 | +| 构建 | 标准 build / build:ts 通过,三份 dist 与 docs/compiled 字节一致 | + +DASH-TYPE-01 关闭;DASH-SDK-01/DASH-STATE-01、实际 SDK 类型/媒体及设备仍由 05 接续。 +当前 217 项:74 done、3 doing、140 todo。没有增加依赖、改变版本或发布。 +本地独立提交,回退同时恢复前一步 JS、声明和生成方式;保留 01~03 的基线与修复。 +下一步 05 完成真实 dash.js 4.5.2/5.2.1 媒体、组合和示例验证,06 接续隔离安装/分发。 diff --git a/refactor/dash-validation.md b/refactor/dash-validation.md index 0f34d4b34..db04b490f 100644 --- a/refactor/dash-validation.md +++ b/refactor/dash-validation.md @@ -1,7 +1,8 @@ # DASH Control 验证与维护入口 测试保护真实 npm 1.1.0 的既有调用契约,以及重构前工作区的 SDK 5.x 稳定 ID 修正。 -版本来源见 [发布契约](baselines/dash-control-contract.md)。02 建立基线,03 已改造生产实现; +版本来源见 [发布契约](baselines/dash-control-contract.md)。02 建立基线,03 改造运行行为, +04 已把五个自有模块迁移为严格 TS 并补齐公开类型与编辑器生成; 实际模块地图与生命周期规则见 [包内架构](../packages/artplayer-plugin-dash-control/ARCHITECTURE.md)。 ## 可重跑的测试 @@ -12,6 +13,12 @@ 候选插件 SDK 4/5、两类菜单、两种选择及候选清理、Chromium/Firefox/WebKit。 浏览器套件串行运行,避免共享端口和报告覆盖。 - `yarn ci:check`:工具链、计划、lint、声明漂移、严格类型和 Node/基线测试。 +- `yarn test:browser test/browser/dash-editor-types.spec.js --trace on`:三引擎 Monaco 实际 + worker 编译全局声明、验证回调/旧类型提取及错误诊断,并执行发出的 JS 工厂。 +- `yarn build:ts`:重新生成编辑器声明;`refactor/scripts/dash-types.test.mjs` 校验可复现、 + TS 4.3.5/5.9.3、五种模块模式、八条非法用法及实际 npm 旧声明的消费者对照。 +- `yarn test:dash-types-package`:Yarn 打包核心/DASH、工作区外离线安装与冻结锁重装, + 检查五种模式、CommonJS callable/命名空间、每模式八条非法调用,并拒绝解析回工作区。 Node helper 分别加载当前源码、固定 Git 提交的旧工作区 UMD、实际 npm main/legacy/module。 Git 内容以 LF 指纹核对,发布归档与成员以原始字节指纹核对。`ARTPLAYER_TEST_DASH` 可指定 @@ -45,5 +52,5 @@ Git 内容以 LF 指纹核对,发布归档与成员以原始字节指纹核对 - DASH-LIFE-01:空列表/禁用后 UI、旧回调、销毁后 update/订阅已修正,补充重入和错误回归。 - DASH-STATE-01:唯一克隆当前轨道、重复标签、数字零 ID 已修正;实际 SDK 轨道身份仍由 05 核验。 -状态以 risks.json 为准;受控修复不替代真实 SDK 验收。04 完成严格 TS/公开类型兼容,05 完成 +状态以 risks.json 为准;受控修复不替代真实 SDK 验收。严格 TS/公开类型已有独立验证,05 完成 真实 SDK/媒体/组合验证,06 完成包分发验收。物理设备和 npm 发布准入仍待完成。 diff --git a/refactor/fixtures/consumers/dash-control-legacy.ts b/refactor/fixtures/consumers/dash-control-legacy.ts new file mode 100644 index 000000000..fb2a320d3 --- /dev/null +++ b/refactor/fixtures/consumers/dash-control-legacy.ts @@ -0,0 +1,12 @@ +import Artplayer from 'artplayer' +import dashControl from 'artplayer-plugin-dash-control' + +type OldOption = Parameters[0] +type OldQuality = NonNullable +const options: OldOption = { quality: { control: true, getName: (level: object) => String(level) }, audio: { setting: true } } +const quality: OldQuality = { title: 'Quality', auto: 'Auto' } +const formatter: NonNullable = value => String(value) +const art = new Artplayer({ container: '#player', url: 'video.mpd', plugins: [dashControl(options)] }) +const plugin: { name: 'artplayerPluginDashControl', update: () => void } = dashControl({ quality })(art) +const done: void = plugin.update() +void [formatter, done] diff --git a/refactor/plan.md b/refactor/plan.md index 871963c9d..b7820431d 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。 -状态:todo 141 / doing 3 / blocked 0 / done 73 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 140 / doing 3 / blocked 0 / done 74 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -204,7 +204,7 @@ | PKG-DASH-01 | artplayer-plugin-dash-control
核对包契约与历史用法 | BASE-05 | quality/audio、representation ID/Auto、getName 和 update | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done | | PKG-DASH-02 | artplayer-plugin-dash-control
建立特有行为与错误测试 | PKG-DASH-01, ENG-03, ENG-05 | 保留已有 5 项稳定 ID 回归,补发布版 SDK 4.x/当前 5.x、音轨/空列表/过滤/换源与关闭引用 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-DASH-03 | artplayer-plugin-dash-control
整理内部职责与资源 | PKG-DASH-02, CORE-11, CORE-14 | 能力适配保留 SDK 4.x 与 5.x,稳定 ID 映射、ABR 状态、UI 清理和生命周期职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | -| PKG-DASH-04 | artplayer-plugin-dash-control
迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | +| PKG-DASH-04 | artplayer-plugin-dash-control
迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done | | PKG-DASH-05 | artplayer-plugin-dash-control
验证新旧核心和组合 | PKG-DASH-04, CORE-22 | 固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-DASH-06 | artplayer-plugin-dash-control
验证分发并同步文档 | PKG-DASH-05, ENG-07 | dash.control.js、支持的 dash.js 版本与产物 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | @@ -482,3 +482,4 @@ - PKG-DASH-01: [记录](changes/2026-09-12-PKG-DASH-01-contract.md) [记录](baselines/dash-control-contract.md) [记录](baselines/dash-control-release.json) - PKG-DASH-02: [记录](changes/2026-09-12-PKG-DASH-02-tests.md) [记录](dash-validation.md) [记录](baselines/dash-validation.json) - PKG-DASH-03: [记录](changes/2026-09-12-PKG-DASH-03-runtime.md) [记录](baselines/dash-runtime.json) [记录](dash-validation.md) +- PKG-DASH-04: [记录](changes/2026-09-12-PKG-DASH-04-types.md) [记录](baselines/dash-types-validation.json) [记录](dash-validation.md) diff --git a/refactor/progress.md b/refactor/progress.md index 1a83c02f1..9c9a14479 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,21 @@ # 进度与证据 +## 最新完成:DASH-04 严格 TS、公开类型和编辑器迁移 + +DASH 五个自有模块全部 TS 化,源码/公开工厂可互相赋值;默认字段和自定义 SDK 泛型准确, +旧 object 回调、Parameters 配置提取、同步 update 与所有 JS 分发路径保持。 +补齐 CJS/ESM/legacy 类型桥和 Monaco 生成声明;实际 npm 旧声明与候选消费者对照通过。 + +全仓 261 个生产 TS 严格检查,完整 CI 715 项通过;源码 Node 116、三格式 Node 266 项通过。 +正式 main 三引擎 90 项(含核心/DASH Monaco 6 项)、legacy 84 项通过,无重试/跳过。 +工作区外 Yarn tarball 离线安装/冻结重装后五种类型模式通过,每模式拒绝八条非法用法。 +首次发现 tsconfig.json 泄露到包,已保留失败并修正 .npmignore,未手改生成产物。 + +DASH-TYPE-01 关闭;真实 dash.js/MPD/ABR、SDK 类型与设备/完整分发仍待验证。 +当前 217 项:74 done、3 doing、140 todo。下一步 DASH-05 进入真实 SDK 与媒体组合。 +详见 [本次变更](changes/2026-09-12-PKG-DASH-04-types.md) 和 +[执行证据](baselines/dash-types-validation.json)。独立本地提交,不推送/发布,全项目目标未完成。 + ## 最新完成:DASH-03 SDK 适配与生命周期源码改造 DASH 源码已拆为入口生命周期、SDK 适配、selector 模型、菜单所有权四个 JS 模块。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 0df46db58..b90cd192c 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -138,5 +138,5 @@ | DASH-SDK-01 | open / 已复现 | npm 发布版 4.x 与工作区仅 5.x 的 SDK 接口不兼容 | PKG-DASH-02, PKG-DASH-03, PKG-DASH-05 | | DASH-LIFE-01 | resolved / 已复现 | DASH 空拓扑残留 UI,监听和旧选择回调缺乏关闭守卫 | PKG-DASH-02, PKG-DASH-03 | | DASH-STATE-01 | open / 已复现 | DASH 当前项高亮使用真值 ID、音轨身份和标签去重,边界待核实 | PKG-DASH-02, PKG-DASH-03, PKG-DASH-05 | -| DASH-TYPE-01 | open / 源码/产物事实 | DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口 | PKG-DASH-04 | +| DASH-TYPE-01 | resolved / 源码/产物事实 | DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口 | PKG-DASH-04 | | DASH-DEMO-01 | open / 源码/产物事实 | DASH 示例仅新 SDK 且重复安装会累积销毁回调 | PKG-DASH-06 | diff --git a/refactor/risks.json b/refactor/risks.json index 5cd84de15..c18399a63 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -628,7 +628,7 @@ "PKG-DASH-05" ], "evidence": [ - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "docs/assets/example/dash.control.js", "refactor/baselines/dash-control-contract.md" ], @@ -2844,7 +2844,7 @@ "evidence": [ "refactor/baselines/dash-control-contract.md", "refactor/baselines/dash-control-release.json", - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", "docs/assets/example/dash.control.js", "test/dash-contract.test.js", @@ -2867,7 +2867,7 @@ "evidence": [ "refactor/baselines/dash-control-contract.md", "refactor/baselines/dash-control-release.json", - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", "docs/assets/example/dash.control.js", "test/dash-contract.test.js", @@ -2897,7 +2897,7 @@ "evidence": [ "refactor/baselines/dash-control-contract.md", "refactor/baselines/dash-control-release.json", - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", "docs/assets/example/dash.control.js", "test/dash-contract.test.js", @@ -2912,19 +2912,26 @@ "id": "DASH-TYPE-01", "title": "DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口", "confirmation": "source-observed", - "status": "open", + "status": "resolved", "owners": [ "PKG-DASH-04" ], "evidence": [ "refactor/baselines/dash-control-contract.md", "refactor/baselines/dash-control-release.json", - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", "docs/assets/example/dash.control.js" ], "compatibleResolution": "保留旧类型推断,提供准确的 SDK 适配/回调契约和兼容格式入口,不以 any 掩盖源码。", - "closureCriteria": "严格源码、旧/新编译器消费者与隔离产物类型验证通过。" + "closureCriteria": "严格源码、旧/新编译器消费者与隔离产物类型验证通过。", + "resolutionEvidence": [ + "refactor/baselines/dash-types-validation.json", + "refactor/changes/2026-09-12-PKG-DASH-04-types.md", + "test/types/dash-control.ts", + "refactor/scripts/dash-package-types.mjs" + ], + "resolutionRationale": "五个自有 TS 模块严格检查通过,真实旧 npm 声明/候选消费者在 TS 4.3/5.9 兼容,五种模块模式及非法用法验证、真实 Monaco、工作区外 Yarn tarball 安装与类型检查通过。只关闭插件自身声明和格式解析问题,实际 SDK 类型/媒体矩阵仍由 05 接续。" }, { "id": "DASH-DEMO-01", @@ -2937,7 +2944,7 @@ "evidence": [ "refactor/baselines/dash-control-contract.md", "refactor/baselines/dash-control-release.json", - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", "docs/assets/example/dash.control.js" ], diff --git a/refactor/scripts/dash-package-types.mjs b/refactor/scripts/dash-package-types.mjs new file mode 100644 index 000000000..61a5dd5c1 --- /dev/null +++ b/refactor/scripts/dash-package-types.mjs @@ -0,0 +1,85 @@ +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:dash-types-package') + const yarn = process.env.npm_execpath + assert(yarn && fs.existsSync(yarn)) + const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/dash-package-types-')) + console.log(`DASH isolated package type evidence: ${output}`) + const consumer = consumerDirectory() + try { + const packages = [] + const published = JSON.parse(fs.readFileSync(path.join(workspace, 'refactor/baselines/dash-control-release.json'))).release + for (const name of ['artplayer', 'artplayer-plugin-dash-control']) { + 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: 'dash-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 = [] + 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/dash-control.ts'), 'utf8') + const filename = path.join(consumer, `current.${extension}`) + const legacy = path.join(consumer, `legacy.${extension}`) + fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/dash-control-legacy.ts'), legacy) + const files = [filename, legacy] + if (mode === 'nodenext-cjs') { + const commonjs = path.join(consumer, 'commonjs.cts') + fs.writeFileSync(commonjs, `import dash = require('artplayer-plugin-dash-control'); import legacy = require('artplayer-plugin-dash-control/legacy'); +const option: dash.Option = { quality: { getName: level => String(level.height) } }; dash(option); legacy();`) + 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, 8, 'Installed package must reject all invalid uses') + matrix.push({ compiler: compiler.version, mode, diagnostics, invalid }) + } + const report = { task: 'PKG-DASH-04', scope: 'Packed current core and DASH, offline Yarn install and frozen reinstall outside workspace; isolated public type acceptance only, not complete package runtime/SDK/media release validation.', packages, matrix } + writeJson(path.join(output, 'report.json'), report) + console.log(`DASH installed type matrix passed: ${matrix.length} modes, eight invalid uses rejected in each; ${output}`) + } + finally { + removeConsumer(consumer) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/refactor/scripts/dash-types.test.mjs b/refactor/scripts/dash-types.test.mjs new file mode 100644 index 000000000..744bc0494 --- /dev/null +++ b/refactor/scripts/dash-types.test.mjs @@ -0,0 +1,78 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +// eslint-disable-next-line test/no-import-node-test -- Run exact compiler and editor compatibility contracts. +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' + +test('DASH public factories preserve root/legacy imports, inferred SDK fields and historical extraction', () => { + const source = fs.readFileSync('test/types/dash-control.ts', 'utf8') + for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]) { + assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`) + const invalid = source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '') + assert.equal(checkConsumer(compiler, mode, invalid).length, 8, 'Every invalid use must remain invalid') + } + const commonjs = `import dash = require('artplayer-plugin-dash-control'); import legacy = require('artplayer-plugin-dash-control/legacy'); +const option: dash.Option = { quality: { getName: level => String(level.height) } }; dash(option); legacy();` + assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', commonjs), []) +}) + +test('DASH legacy consumer compiles against the actual published declaration and candidate', async () => { + const { release } = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/dash-control-release.json'))) + const member = 'package/types/artplayer-plugin-dash-control.d.ts' + const bytes = readMember(await ensureArchive(release), member) + assert.equal(hash(bytes), release.files[member]) + const source = fs.readFileSync(path.join(refactorDir, 'fixtures/consumers/dash-control-legacy.ts'), 'utf8') + const directory = fs.mkdtempSync(path.join(refactorDir, '.cache/dash-legacy-types-')) + try { + const filename = path.join(directory, 'consumer.ts') + fs.writeFileSync(path.join(directory, 'legacy.d.ts'), bytes) + fs.writeFileSync(filename, source.replace('from \'artplayer-plugin-dash-control\'', 'from \'./legacy\'')) + for (const compiler of [ts, compat]) { + 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'] }) + const diagnostics = compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') })) + assert.deepEqual(diagnostics, [], `Published / TS ${compiler.version}`) + assert(program.getSourceFiles().some(file => path.resolve(file.fileName) === path.join(directory, 'legacy.d.ts'))) + assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', source), [], `Candidate / TS ${compiler.version}`) + } + } + finally { + const resolved = fs.realpathSync(directory) + assert.equal(path.dirname(resolved), fs.realpathSync(path.join(refactorDir, '.cache'))) + assert(path.basename(resolved).startsWith('dash-legacy-types-')) + fs.rmSync(resolved, { recursive: true, force: true }) + } +}) + +test('DASH editor declarations are reproducible, standalone, precise and compatible with old TS', async () => { + const source = fs.readFileSync('packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts', 'utf8') + const generated = generatePluginEditorDeclaration(source, 'artplayerPluginDashControl') + const file = 'docs/assets/ts/artplayer-plugin-dash-control.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, 'Run yarn build:ts') + const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8') + const consumer = ` +const option: artplayerPluginDashControl.Option = { quality: { getName: level => String(level.height) }, audio: { getName: track => track.lang || String(track.id) } } +const oldQuality: Parameters[0]['quality'] = { getName: (level: object) => String(level) } +const factory: (art: Artplayer) => artplayerPluginDashControl.Result = artplayerPluginDashControl(option) +artplayerPluginDashControl() +artplayerPluginDashControl(undefined) +// @ts-expect-error A formatter must return a string. +artplayerPluginDashControl({ quality: { getName: level => level.height } }) +// @ts-expect-error SDK fields cannot become any. +artplayerPluginDashControl({ audio: { getName: track => track.unknownProperty } }) +// @ts-expect-error Private definition namespace must not leak globally. +const hidden: artplayerPluginDashControlDefinitions.Option = {} +void [oldQuality, factory, hidden] +` + for (const compiler of [ts, compat]) { + assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [], compiler.version) + assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 3) + } +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index b896ab4e8..91ac5fe1b 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2037,11 +2037,15 @@ "ENG-06", "CORE-07" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "dash.js adapter、selector 和回调类型", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", - "evidence": [] + "evidence": [ + "changes/2026-09-12-PKG-DASH-04-types.md", + "baselines/dash-types-validation.json", + "dash-validation.md" + ] }, { "id": "PKG-DASH-05", diff --git a/refactor/third-party.json b/refactor/third-party.json index 423489cef..ca241b457 100644 --- a/refactor/third-party.json +++ b/refactor/third-party.json @@ -1042,13 +1042,14 @@ "riskId": "SDK-02", "owners": [ "PKG-DASH-01", + "PKG-DASH-05", "EX-03" ], "sourceFiles": [ - "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/src/index.ts", "docs/assets/example/dash.control.js" ], - "contract": "Caller owns art.dash. Published plugin 1.1.0 uses dash.js 4.x (demo 4.5.2); workspace uses 5.x (demo 5.2.1). Preserve both through capability adapters and v5 stable representation IDs; actual SDK playback remains PKG-DASH-05.", + "contract": "Caller owns art.dash. Candidate adapts the released 4.x quality method surface and workspace 5.x stable representation ID surface. Controlled contracts and DOM validation pass; actual pinned 4.5.2/5.2.1 SDK media remains PKG-DASH-05.", "validationStatus": "Source integration observed; complete real SDK/service/device matrix not yet verified.", "demoPathsSource": "refactor/baselines/demo-inventory.json" }, diff --git a/scripts/build-ts.js b/scripts/build-ts.js index 8d6ad6a32..05ddb17dc 100644 --- a/scripts/build-ts.js +++ b/scripts/build-ts.js @@ -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'].includes(name) + const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl'].includes(name) const code = semanticPlugin ? generatePluginEditorDeclaration(source, name) : `${source.replace(reg, '')}\nexport = ${name};\nexport as namespace ${name};\n` diff --git a/scripts/typecheck.mjs b/scripts/typecheck.mjs index f873f533e..82611376d 100644 --- a/scripts/typecheck.mjs +++ b/scripts/typecheck.mjs @@ -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/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', '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') diff --git a/test/browser/dash-editor-types.spec.js b/test/browser/dash-editor-types.spec.js new file mode 100644 index 000000000..13f44fdc7 --- /dev/null +++ b/test/browser/dash-editor-types.spec.js @@ -0,0 +1,67 @@ +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 consumes DASH global types and executes the emitted plugin factory', async ({ page }, testInfo) => { + const code = process.env.ARTPLAYER_DASH_ARTIFACT + ? fs.readFileSync(process.env.ARTPLAYER_DASH_ARTIFACT, 'utf8') + : await compilePackage('artplayer-plugin-dash-control', 'umd') + const declarations = Object.fromEntries(['artplayer', 'artplayer-plugin-dash-control'].map(name => [name, hash(fs.readFileSync(new URL(`../../docs/assets/ts/${name}.d.ts`, import.meta.url)))])) + const inputs = { candidate: process.env.ARTPLAYER_DASH_ARTIFACT || 'workspace source build', sha256: hash(code), declarations } + await testInfo.attach('dash-editor-inputs', { contentType: 'application/json', body: JSON.stringify(inputs) }) + 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 names = ['artplayer', 'artplayer-plugin-dash-control'] + const libraries = await Promise.all(names.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 dashOption: artplayerPluginDashControl.Option = { + quality: { getName: level => String(level.height) }, + audio: { getName: track => track.lang || String(track.id) }, +} +const oldQuality: NonNullable[0]['quality']> = { getName: (value: object) => String(value) } +const typedFactory: (art: Artplayer) => artplayerPluginDashControl.Result = artplayerPluginDashControl(dashOption) +const defaultFactory = artplayerPluginDashControl() +`, 'typescript', api.Uri.parse('file:///dash-consumer.ts')) + const invalid = api.editor.createModel('artplayerPluginDashControl({ quality: { getName: level => level.height } })', 'typescript', api.Uri.parse('file:///dash-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 DASH 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 typeof typedFactory === 'function' && typeof defaultFactory === 'function'`)() + 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) +}) diff --git a/test/types/dash-control.ts b/test/types/dash-control.ts new file mode 100644 index 000000000..6855895e4 --- /dev/null +++ b/test/types/dash-control.ts @@ -0,0 +1,43 @@ +import type { AudioTrack, Config, Option, QualityLevel, Result } from 'artplayer-plugin-dash-control' +import Artplayer from 'artplayer' +import dashControl from 'artplayer-plugin-dash-control' +import legacy from 'artplayer-plugin-dash-control/legacy' + +const option: Option = { + quality: { control: true, getName: level => `${level.height}p` }, + audio: { setting: true, getName: track => track.lang || String(track.id) }, +} +const art = new Artplayer({ container: '#player', url: 'video.mpd', plugins: [dashControl(), dashControl(option), legacy()] }) +const result: Result = dashControl(option)(art) +type HistoricalOption = Parameters[0] +const oldQuality: HistoricalOption['quality'] = { getName: (item: object) => String(item) } +dashControl({ quality: oldQuality }) +dashControl(undefined) +const done: void = result.update() +const objectConfig: Config = { getName: (item: object) => String(item) } +dashControl({ quality: objectConfig, audio: objectConfig }) +const level: QualityLevel = { height: 720, id: 0 } +const track: AudioTrack = { id: 'en', lang: 'en' } + +interface CustomLevel { height: number, bandwidth: number } +interface CustomTrack { id: number, lang: string, roles: string[] } +dashControl({ quality: { getName: (value: CustomLevel) => String(value.bandwidth) }, audio: { getName: (value: CustomTrack) => value.roles.join(',') } }) +dashControl({ audio: { getName: track => track.lang } }) + +// @ts-expect-error Update remains synchronous. +const promise: Promise = result.update() +// @ts-expect-error Update has no options parameter. +result.update({}) +// @ts-expect-error Plugin name is the original literal. +const invalidName: Result['name'] = 'dash' +// @ts-expect-error DASH formatters receive only one argument. +dashControl({ quality: { getName: (value: QualityLevel, index: number) => String(index) } }) +// @ts-expect-error Custom formatters must return a string. +dashControl({ audio: { getName: () => 123 } }) +// @ts-expect-error Visibility options stay boolean. +legacy({ quality: { control: 'yes' } }) +// @ts-expect-error Null was never a valid options object. +dashControl(null) +// @ts-expect-error Default SDK fields cannot silently become any. +dashControl({ quality: { getName: level => level.unknownProperty } }) +void [done, level, track, promise, invalidName] diff --git a/test/types/dash-source.ts b/test/types/dash-source.ts new file mode 100644 index 000000000..0aa2d6618 --- /dev/null +++ b/test/types/dash-source.ts @@ -0,0 +1,9 @@ +import type PublicFactory from 'artplayer-plugin-dash-control' +import factory from '../../packages/artplayer-plugin-dash-control/src/index' + +const compatible: typeof PublicFactory = factory +const implementation: typeof factory = compatible +implementation({ quality: { getName: level => `${level.height}p` }, audio: { getName: track => track.lang || String(track.id) } }) +compatible() +// @ts-expect-error Source and public declaration reject invalid formatter results. +factory({ quality: { getName: () => 1 } })