mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
refactor(dash): [PKG-DASH-04] migrate strict TypeScript and compatible declarations
This commit is contained in:
1 parent
7de04573b6
commit
6840544e0e
38 files changed
+1744
-102
No files matched your search
@@ -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:
|
||||
|
||||
|
||||
+42
-13
@@ -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<Item extends object = object> {
|
||||
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<Level extends object = QualityLevel, Track extends object = AudioTrack> {
|
||||
quality?: Config<Level>
|
||||
audio?: Config<Track>
|
||||
}
|
||||
export interface Result {
|
||||
name: 'artplayerPluginDashControl'
|
||||
update: () => void
|
||||
}
|
||||
export function artplayerPluginDashControl<Level extends object = QualityLevel, Track extends object = AudioTrack>(option?: Option<Level, Track>): (art: Artplayer) => Result
|
||||
// Preserve the required last signature for historical Parameters<typeof factory>[0] consumers.
|
||||
export function artplayerPluginDashControl<Level extends object = QualityLevel, Track extends object = AudioTrack>(option: Option<Level, Track>): (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<Item extends object = object> = artplayerPluginDashControlDefinitions.Config<Item>
|
||||
export type Option<Level extends object = QualityLevel, Track extends object = AudioTrack> = artplayerPluginDashControlDefinitions.Option<Level, Track>
|
||||
export type Result = artplayerPluginDashControlDefinitions.Result
|
||||
}
|
||||
|
||||
export default artplayerPluginDashControl
|
||||
|
||||
export = artplayerPluginDashControl
|
||||
export as namespace artplayerPluginDashControl;
|
||||
@@ -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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),u=e(o,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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))Line truncated
|
||||
!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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),a=e(r,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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.Line truncated
|
||||
@@ -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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),a=c(e,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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.lLine truncated
|
||||
!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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),f=c(n,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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]));returLine truncated
|
||||
@@ -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 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>';
|
||||
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);
|
||||
|
||||
+2
-1
@@ -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": {
|
||||
|
||||
@@ -2,4 +2,5 @@ node_modules
|
||||
.DS_Store
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
src
|
||||
src
|
||||
tsconfig.json
|
||||
@@ -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<typeof factory>[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
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),u=e(o,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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))Line truncated
|
||||
!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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),a=e(r,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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.Line truncated
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),a=c(e,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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.lLine truncated
|
||||
!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",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>'),f=c(n,"dash-audio",'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>'),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]));returLine truncated
|
||||
+6
-3
@@ -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 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>';
|
||||
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);
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-12
@@ -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<Level extends object = QualityLevel, Track extends object = AudioTrack>(option: Option<Level, Track> = {}) {
|
||||
return (player: Artplayer): Result => {
|
||||
// art.dash is caller-owned; update keeps the historical SDK/media validation boundary.
|
||||
const art = player as unknown as Host<Level, Track>
|
||||
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<QualityItem>(art, 'dash-quality', $quality)
|
||||
const audio = createMenu<AudioItem<Track>>(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)
|
||||
+17
-12
@@ -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<Item extends SelectorItem>(items: Item[]): Item[] {
|
||||
const seen = new Map<Label, Item>()
|
||||
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<Level extends object, Track extends object>(dash: Dash<Level, Track>, config: Config<Level>, active: Valid): MenuModel<QualityItem> | 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<Track extends AudioFields>(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<Level extends object, Track extends object>(dash: Dash<Level, Track>, config: Config<Track>, active: Valid): MenuModel<AudioItem<Track>> | 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<Track>[] = []
|
||||
for (const track of tracks) {
|
||||
const label = getName(track)
|
||||
if (!active())
|
||||
+10
-8
@@ -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<Item extends SelectorItem>(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<Item> | 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)
|
||||
+9
-6
@@ -1,11 +1,14 @@
|
||||
export function qualityAdapter(dash) {
|
||||
import type { Dash, QualityAdapter } from './types'
|
||||
|
||||
export function qualityAdapter<Level extends object, Track extends object>(dash: Dash<Level, Track>): QualityAdapter<Level> {
|
||||
// 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)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -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<Level extends object = object, Track extends object = object> {
|
||||
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<Track extends object> extends SelectorItem {
|
||||
value: Track & AudioFields
|
||||
}
|
||||
|
||||
export interface QualityAdapter<Level extends object> {
|
||||
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<QualityItem, 'html'>
|
||||
select: (item: QualityItem, valid: Valid) => void
|
||||
}
|
||||
|
||||
export interface MenuModel<Item extends SelectorItem> {
|
||||
html: Label
|
||||
title: string
|
||||
selector: Item[]
|
||||
select: (item: Item, valid: Valid) => unknown
|
||||
}
|
||||
|
||||
export interface MenuOption<Item extends SelectorItem> {
|
||||
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: <Item extends SelectorItem>(option: MenuOption<Item>) => 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<Level extends object, Track extends object> extends MenuHost {
|
||||
template: { $video: object }
|
||||
constructor: { utils: { errorHandle: (condition: unknown, message: string) => void } }
|
||||
isDestroy?: boolean
|
||||
dash?: Dash<Level, Track>
|
||||
on: (name: EventName, callback: Cleanup) => unknown
|
||||
off: (name: EventName, callback: Cleanup) => unknown
|
||||
}
|
||||
|
||||
export type DisplayConfig = Pick<Config, 'control' | 'setting'>
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -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<Item extends object = object> = Definition.Config<Item>
|
||||
type Option<Level extends object = QualityLevel, Track extends object = AudioTrack> = Definition.Option<Level, Track>
|
||||
type Result = Definition.Result
|
||||
}
|
||||
|
||||
export = artplayerPluginDashControl
|
||||
@@ -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'
|
||||
+28
-3
@@ -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<Item extends object = object> {
|
||||
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<Level extends object = QualityLevel, Track extends object = AudioTrack> {
|
||||
quality?: Config<Level>
|
||||
audio?: Config<Track>
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
name: 'artplayerPluginDashControl'
|
||||
update: () => void
|
||||
}
|
||||
|
||||
declare function artplayerPluginDashControl<Level extends object = QualityLevel, Track extends object = AudioTrack>(option?: Option<Level, Track>): (art: Artplayer) => Result
|
||||
// Preserve the required last signature for historical Parameters<typeof factory>[0] consumers.
|
||||
declare function artplayerPluginDashControl<Level extends object = QualityLevel, Track extends object = AudioTrack>(option: Option<Level, Track>): (art: Artplayer) => Result
|
||||
|
||||
export default artplayerPluginDashControl
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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<typeof factory>[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 接续隔离安装/分发。
|
||||
@@ -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 发布准入仍待完成。
|
||||
@@ -0,0 +1,12 @@
|
||||
import Artplayer from 'artplayer'
|
||||
import dashControl from 'artplayer-plugin-dash-control'
|
||||
|
||||
type OldOption = Parameters<typeof dashControl>[0]
|
||||
type OldQuality = NonNullable<OldOption['quality']>
|
||||
const options: OldOption = { quality: { control: true, getName: (level: object) => String(level) }, audio: { setting: true } }
|
||||
const quality: OldQuality = { title: 'Quality', auto: 'Auto' }
|
||||
const formatter: NonNullable<OldQuality['getName']> = 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]
|
||||
+3
-2
@@ -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<br>核对包契约与历史用法 | BASE-05 | quality/audio、representation ID/Auto、getName 和 update | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
|
||||
| PKG-DASH-02 | artplayer-plugin-dash-control<br>建立特有行为与错误测试 | PKG-DASH-01, ENG-03, ENG-05 | 保留已有 5 项稳定 ID 回归,补发布版 SDK 4.x/当前 5.x、音轨/空列表/过滤/换源与关闭引用 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
|
||||
| PKG-DASH-03 | artplayer-plugin-dash-control<br>整理内部职责与资源 | 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<br>迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
|
||||
| PKG-DASH-04 | artplayer-plugin-dash-control<br>迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
|
||||
| PKG-DASH-05 | artplayer-plugin-dash-control<br>验证新旧核心和组合 | 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<br>验证分发并同步文档 | 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)
|
||||
@@ -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 模块。
|
||||
|
||||
@@ -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 |
|
||||
+15
-8
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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
|
||||
})
|
||||
@@ -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<typeof artplayerPluginDashControl>[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)
|
||||
}
|
||||
})
|
||||
+6
-2
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ console.log(`✨ Built ${artplayerTSoutput}`);
|
||||
const type = pluginsTS[index]
|
||||
const { name, file } = parsePluginInfo(type)
|
||||
const source = String(fs.readFileSync(type))
|
||||
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack'].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`
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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<Parameters<typeof artplayerPluginDashControl>[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)
|
||||
})
|
||||
@@ -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<typeof dashControl>[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<CustomLevel, CustomTrack>({ audio: { getName: track => track.lang } })
|
||||
|
||||
// @ts-expect-error Update remains synchronous.
|
||||
const promise: Promise<void> = 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]
|
||||
@@ -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 } })
|
||||
Reference in new issue
Block a user