refactor(dash): [PKG-DASH-04] migrate strict TypeScript and compatible declarations

This commit is contained in:
Harvey Zhao committed 2026-09-12 05:18:47 +08:00
1 parent 7de04573b6
commit 6840544e0e
38 files changed
+1744 -102

No files matched your search

@@ -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
@@ -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);
@@ -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"
]
}
}
}
@@ -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)
@@ -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())
@@ -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)
@@ -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'
@@ -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