fix(dash): [PKG-DASH-CLEANUP-01] preserve first cleanup exception

This commit is contained in:
Harvey Zhao committed 2026-09-15 15:57:04 +08:00
1 parent 1c522f181b
commit 25faf88e80
23 files changed
+49546 -147

No files matched your search

@@ -26,6 +26,7 @@ while each submenu is open and then select another option through normal clicks.
| `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/sdk-events.ts` | Owned SDK subscriptions, coalesced refresh, teardown and asynchronous errors |
| `src/cleanup.ts` | Ordered cleanup, revision guards and first thrown value preservation |
| `src/seek-buffer.ts` | Exact SDK 4.5.2 empty-seek metric recovery and stale/reentrant measurement guards |
| `src/types.ts` | Narrow internal SDK, host, model and cleanup types |
| `src/mapping.ts` | Names, current item matching, duplicate labels and selector ordering |
@@ -36,7 +37,7 @@ while each submenu is open and then select another option through normal clicks.
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. The event observer receives active/refresh/reset
callbacks and owns no DOM or ArtPlayer object. All seven owned modules are checked with strict
callbacks and owns no DOM or ArtPlayer object. All eight owned modules are checked with strict
TypeScript, noUncheckedIndexedAccess, and skipLibCheck=false; there are no remaining
owned JavaScript modules in this package.
@@ -98,6 +99,22 @@ or destroy an SDK to handle a plugin rendering failure.
## Types and compatibility
`cleanup.ts` is shared by menu removal, SDK unsubscription and entry teardown.
It records whether an action threw separately from the thrown value, so `undefined`,
`null`, `false`, zero (including negative zero), empty strings and `NaN` propagate
unchanged. Remaining owned cleanup actions still run; a newer update revision stops
stale menu cleanup before the next action. Later failures cannot replace the first.
This does not change the observer's asynchronous warning policy or make the caller's
SDK plugin-owned. A caller's throwing `beforeUnmount` can prevent that particular
core control from being removed; the plugin releases its sibling surfaces and
propagates the original error rather than bypassing the core callback contract.
`test/dash-cleanup-errors.test.js` covers both SDK method generations, all seven
values, secondary errors, complete unsubscription and repeated destruction. The
browser regression uses public control updates with fresh selector items: core
selector items acquire non-configurable DOM references and must not be reused as
new input objects. It exercises actual old/new core `beforeUnmount` callbacks.
`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
@@ -155,8 +172,11 @@ 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.
implementation files/config do not leak into the archive. This is type acceptance.
Verified installed files are retained under the report's `installed-artifacts/`
directory for subsequent runtime/browser checks; every retained file is checked
against its tarball hash before the temporary outside-workspace consumer is removed.
The full installed runtime and SDK release matrix still belongs to PKG-DASH-06/05.
`dash-control.spec.js` 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 r;const o=new Map;function i(n,r=o.get(n)){if(!r||o.get(n)!==r)return;o.delete(n);const i=t[n];"controls"===n&&i.cache?.get&&i.cache.get(e)?.option?.onSelect!==r||"setting"===n&&i.find&&i.find(e)?.onSelect!==r||i.remove(e)}function c(){let t;r=void 0;for(const[n,r]of[...o])try{i(n,r)}catch(e){t||(t=e)}if(t)throw t}return{update:function(s,a,l){if(!a)return void c();const u={};r=u;const f=()=>u===r&&l(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.title}: ${e.html}`,f()&&s.control&&t.controls.check(e),f()&&s.setting&&t.setting.check(e),e.html):e.html):e.html;s.control?(o.set("controls",d),t.controls.update({name:e,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(s.setting?(o.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){const r=t.setting,i=r.find?.(e),c=o.get("setting");if(n()&&c&&!0===r.show&&i?.onSelect===c&&i.selector&&r.active===i.selector&&"function"===typeof r.render)return()=>{if(!n()||t.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const i=r.find?.(e),c=o.get("setting");n()&&c&&i?.onSelect===c&&i.selector&&r.render?.(i.selector)}}}}function n(t){let e;const n=n=>e===n&&t.active(n.dash),r=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function o(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,o]of t.callbacks.splice(0))try{t.off.call(t.dash,e,o)}catch(r){n||(n=r)}if(n)throw n}function i(n,r){if(e===n){try{o()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const o=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===o&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=r(e),n(e)&&t.refresh()}catch(c){i(e,c)}finally{e.running=!1}}})}return{bind:function(s){const a=e;if(a?.dash===s){const t=r(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(o(),e||!t.active(s)||"function"!==typeof s.on||"function"!==typeof s.off)return;const l={dash:s,off:s.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=l;const u=s.on;try{l.automatic=r(l);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),o=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),c=o?.getProcessors?.();if(!Array.isArray(c)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const s of c){if(!e())return;const c=s.getType?.();if("audio"!==c&&"video"!==c)continue;const a=s.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(c),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(c,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(s,()=>n(l)&&!l.suspended);o&&e.push(["playbackSeeking",o]),e.push(["playbackTimeUpdated",()=>{if(n(l)&&!l.suspended)try{const t=r(l);n(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){i(l,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{t.reset()}catch(e){i(l,e)}}}]);for(const t of e){if(!n(l))break;l.callbacks.push(t),u.call(s,...t)}}catch(f){if(e===l)try{o()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:o}}return function(r={}){return o=>{const i=o,{$viLine 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,e=()=>!0){let n,r=!1;for(const i of t){if(!e())break;try{i()}catch(o){r||(r=!0,n=o)}}if(r)throw n}function e(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 n(e,n,r){let o;const i=new Map;function s(t,r=i.get(t)){if(!r||i.get(t)!==r)return;i.delete(t);const o=e[t];"controls"===t&&o.cache?.get&&o.cache.get(n)?.option?.onSelect!==r||"setting"===t&&o.find&&o.find(n)?.onSelect!==r||o.remove(n)}function c(){o=void 0,t([...i].map(([t,e])=>()=>s(t,e)))}return{update:function(t,a,l){if(!a)return void c();const u={};o=u;const f=()=>u===o&&l(),d=n=>f()?(a.select(n,f),f()?(e.notice.show=`${a.title}: ${n.html}`,f()&&t.control&&e.controls.check(n),f()&&t.setting&&e.setting.check(n),n.html):n.html):n.html;t.control?(i.set("controls",d),e.controls.update({name:n,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):s("controls"),f()&&(t.setting?(i.set("setting",d),e.setting.update({name:n,tooltip:a.html,html:a.title,icon:r,width:200,selector:a.selector,onSelect:d})):s("setting"))},clear:c,captureNavigation:function(t){const r=e.setting,o=r.find?.(n),s=i.get("setting");if(t()&&s&&!0===r.show&&o?.onSelect===s&&o.selector&&r.active===o.selector&&"function"===typeof r.render)return()=>{if(!t()||e.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const o=r.find?.(n),s=i.get("setting");t()&&s&&o?.onSelect===s&&o.selector&&r.render?.(o.selector)}}}}function r(e){let n;const r=t=>n===t&&e.active(t.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function i(){const e=n;e&&(n=void 0,e.epoch++,t(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function s(t,r){if(n===t){try{i()}catch(o){console.warn("ArtPlayer DASH subscription cleanup failed:",o)}if(!n&&e.active(t.dash))try{e.reset()}catch(o){console.warn("ArtPlayer DASH cleanup failed:",o)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(t){if(!r(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const n=t.epoch;Promise.resolve().then(()=>{if(r(t)&&t.epoch===n&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),r(t)&&e.refresh()}catch(i){s(t,i)}finally{t.running=!1}}})}return{bind:function(t){const a=n;if(a?.dash===t){const t=o(a);return void(r(a)&&(a.automatic=t,a.suspended=!1))}if(i(),n||!e.active(t)||"function"!==typeof t.on||"function"!==typeof t.off)return;const l={dash:t,off:t.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};n=l;const u=t.on;try{l.automatic=o(l);const n=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),i=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),s=o?.getProcessors?.();if(!Array.isArray(s)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const c of s){if(!e())return;const s=c.getType?.();if("audio"!==s&&"video"!==s)continue;const a=c.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(s),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(s,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(t,()=>r(l)&&!l.suspended);i&&n.push(["playbackSeeking",i]),n.push(["playbackTimeUpdated",()=>{if(r(l)&&!l.suspended)try{const t=o(l);r(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){s(l,t)}}]),n.push(["streamTeardownComplete",()=>{if(r(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{e.reset()}catch(t){s(l,t)}}}]);for(const e of n){if(!r(l))break;l.callbacks.push(e),u.call(t,...e)}}catch(f){if(n===l)try{i()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:i}}return function(oLine 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,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t};function l(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 i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(e))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(e))?void 0:a.onSelect)!==o||s.remove(e)}function l(){let t;o=void 0;for(const[n,o]of[...r])try{i(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,a,s){if(!a)return void l();const u={};o=u;const f=()=>u===o&&s(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.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:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:l,captureNavigation:function(n){var o;const i=t.setting,l=null==(o=i.find)?void 0:o.call(i,e),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||t.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,e),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function a(t){let e;const n=n=>e===n&&t.active(n.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,r]of t.callbacks.splice(0))try{t.off.call(t.dash,e,r)}catch(o){n||(n=o)}if(n)throw n}function i(n,o){if(e===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function l(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const r=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===r&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=o(e),n(e)&&t.refresh()}catch(l){i(e,l)}finally{e.running=!1}}})}return{bind:function(c){const a=e;if((null==a?void 0:a.dash)===c){const t=o(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(r(),e||!t.active(c)||"function"!==typeof c.on||"function"!==typeof c.off)return;const s={dash:c,off:c.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=s;const u=c.on;try{s.automatic=o(s);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(s.suspended=!1),l(s)}]),r=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var t,r,i,l,c,a,s,u,f;if(!o&&e()){o=!0;try{const o=null==(t=n.getVideoElement)?void 0:t.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,h=null==(r=n.getActiveStream)?void 0:r.call(n),g=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==h?void 0:h.getProcessors)?void 0:l.call(h);if(!Array.isArray(p)||"function"!==typeof(null==g?void 0:g.getCurrentBufferLevel)||"function"!==typeof g.addBufferLevel)return;for(const t of p){if(!e())return;const r=null==(c=t.getType)?void 0:c.call(t);if("audio"!==r&&"video"!==r)continue;const i=null==(a=t.getBufferController)?void 0:a.call(t);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=g.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.length){if(!e()||(null==(s=n.getAcLine truncated
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginDashControl=t(),define(function(){return e.artplayerPluginDashControl})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginDashControl=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(t,n,o)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[n]=o,i=(e,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(e,l,i[l]);if(t)for(var l of t(i))o.call(i,l)&&r(e,l,i[l]);return e};function l(e,t=()=>!0){let n,o=!1;for(const i of e){if(!t())break;try{i()}catch(r){o||(o=!0,n=r)}}if(o)throw n}function c(e){const t=new Map;return e.filter(e=>{if(void 0===e.html)return!0;const n=t.get(e.html);return n?(e.default&&Object.assign(n,e),!1):(t.set(e.html,e),!0)})}function a(e,t,n){let o;const r=new Map;function i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=e[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(t))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(t))?void 0:a.onSelect)!==o||s.remove(t)}function c(){o=void 0,l([...r].map(([e,t])=>()=>i(e,t)))}return{update:function(l,a,s){if(!a)return void c();const u={};o=u;const f=()=>u===o&&s(),d=t=>f()?(a.select(t,f),f()?(e.notice.show=`${a.title}: ${t.html}`,f()&&l.control&&e.controls.check(t),f()&&l.setting&&e.setting.check(t),t.html):t.html):t.html;l.control?(r.set("controls",d),e.controls.update({name:t,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(l.setting?(r.set("setting",d),e.setting.update({name:t,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){var o;const i=e.setting,l=null==(o=i.find)?void 0:o.call(i,t),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||e.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,t),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function s(e){let t;const n=n=>t===n&&e.active(n.dash),o=e=>e.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const e=t;e&&(t=void 0,e.epoch++,l(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function i(n,o){if(t===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!t&&e.active(n.dash))try{e.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function c(t){if(!n(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const r=t.epoch;Promise.resolve().then(()=>{if(n(t)&&t.epoch===r&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),n(t)&&e.refresh()}catch(l){i(t,l)}finally{t.running=!1}}})}return{bind:function(l){const a=t;if((null==a?void 0:a.dash)===l){const e=o(a);return void(n(a)&&(a.automatic=e,a.suspended=!1))}if(r(),t||!e.active(l)||"function"!==typeof l.on||"function"!==typeof l.off)return;const s={dash:l,off:l.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};t=s;const u=l.on;try{s.automatic=o(s);const t=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(e=>[e,()=>{"streamUpdated"!==e&&"streamInitialized"!==e||(s.suspended=!1),c(s)}]),r=function(e,t){const n=e;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var e,r,i,l,c,a,s,u,f;if(!o&&t()){o=!0;try{const o=null==(e=n.getVideoElement)?void 0:e.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,g=null==(r=n.getActiveStream)?void 0:r.call(n),h=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==g?void 0:g.getProcessors)?void 0:l.call(g);if(!Array.isArray(p)||"function"!==typeof(null==h?void 0:h.getCurrentBufferLevel)||"function"!==typeof h.addBufferLevel)return;for(const e of p){if(!t())return;const r=null==(c=e.getType)?void 0:c.call(e);if("audio"!==r&&"video"!==r)continue;const i=null==(a=e.getBufferController)?void 0:a.call(e);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=h.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.lengLine truncated
@@ -5,6 +5,24 @@
* Released under the MIT License.
*/
const $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>';
function runCleanups(actions, current = () => true) {
let failed = false;
let failure;
for (const action of actions) {
if (!current())
break;
try {
action();
} catch (error) {
if (!failed) {
failed = true;
failure = error;
}
}
}
if (failed)
throw failure;
}
function qualityAdapter(dash) {
const modern = typeof dash.getRepresentationsByType === "function";
return {
@@ -120,16 +138,7 @@ function createMenu(art, name, icon) {
}
function clear() {
current = void 0;
let failure;
for (const [surface, callback] of [...owned]) {
try {
remove(surface, callback);
} catch (error) {
failure || (failure = error);
}
}
if (failure)
throw failure;
runCleanups([...owned].map(([surface, callback]) => () => remove(surface, callback)));
}
function update(config, model, active) {
if (!model) {
@@ -248,16 +257,7 @@ function observeSDK(options) {
return;
binding = void 0;
record.epoch++;
let failure;
for (const [name, callback] of record.callbacks.splice(0)) {
try {
record.off.call(record.dash, name, callback);
} catch (error) {
failure || (failure = error);
}
}
if (failure)
throw failure;
runCleanups(record.callbacks.splice(0).map(([name, callback]) => () => record.off.call(record.dash, name, callback)));
}
function fail(record, error) {
if (binding === record) {
@@ -398,18 +398,7 @@ function artplayerPluginDashControl(option = {}) {
}
});
function clear(current = () => true) {
let failure;
for (const cleanup of [quality.clear, audio.clear]) {
if (!current())
break;
try {
cleanup();
} catch (error) {
failure || (failure = error);
}
}
if (failure)
throw failure;
runCleanups([quality.clear, audio.clear], current);
}
function update() {
if (closed || art.isDestroy)
@@ -455,17 +444,8 @@ function artplayerPluginDashControl(option = {}) {
return;
closed = true;
revision++;
let failure;
const actions = [observer.release, clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))];
for (const action of actions) {
try {
action();
} catch (error) {
failure || (failure = error);
}
}
if (failure)
throw failure;
runCleanups(actions);
}
try {
const entries = [["ready", update], ["restart", update], ["destroy", destroy]];
@@ -0,0 +1,21 @@
import type { Cleanup, Valid } from './types'
export function runCleanups(actions: Cleanup[], current: Valid = () => true): void {
let failed = false
let failure: unknown
for (const action of actions) {
if (!current())
break
try {
action()
}
catch (error) {
if (!failed) {
failed = true
failure = error
}
}
}
if (failed)
throw failure
}
@@ -2,6 +2,7 @@ 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 { runCleanups } from './cleanup'
import { audioModel, qualityModel } from './mapping'
import { createMenu } from './menu'
import $quality from './quality.svg?raw'
@@ -39,19 +40,7 @@ export default function artplayerPluginDashControl<Level extends object = Qualit
})
function clear(current: Valid = () => true): void {
let failure: unknown
for (const cleanup of [quality.clear, audio.clear]) {
if (!current())
break
try {
cleanup()
}
catch (error) {
failure ||= error
}
}
if (failure)
throw failure
runCleanups([quality.clear, audio.clear], current)
}
function update(): void {
@@ -101,18 +90,8 @@ export default function artplayerPluginDashControl<Level extends object = Qualit
return
closed = true
revision++
let failure: unknown
const actions = [observer.release, clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))]
for (const action of actions) {
try {
action()
}
catch (error) {
failure ||= error
}
}
if (failure)
throw failure
runCleanups(actions)
}
try {
@@ -1,4 +1,5 @@
import type { Cleanup, DisplayConfig, Label, MenuHost, MenuModel, SelectorItem, Valid } from './types'
import { runCleanups } from './cleanup'
export function createMenu<Item extends SelectorItem>(art: MenuHost, name: string, icon: string) {
let current: object | undefined
@@ -18,17 +19,7 @@ export function createMenu<Item extends SelectorItem>(art: MenuHost, name: strin
function clear(): void {
current = undefined
let failure: unknown
for (const [surface, callback] of [...owned]) {
try {
remove(surface, callback)
}
catch (error) {
failure ||= error
}
}
if (failure)
throw failure
runCleanups([...owned].map(([surface, callback]) => () => remove(surface, callback)))
}
function update(config: DisplayConfig, model: MenuModel<Item> | null, active: Valid): void {
@@ -1,4 +1,5 @@
import type { Cleanup, Dash, SDKEventName } from './types'
import { runCleanups } from './cleanup'
import { createSeekRecovery } from './seek-buffer'
interface Binding<Level extends object, Track extends object> {
@@ -27,17 +28,7 @@ export function observeSDK<Level extends object, Track extends object>(options:
return
binding = undefined
record.epoch++
let failure: unknown
for (const [name, callback] of record.callbacks.splice(0)) {
try {
record.off.call(record.dash, name, callback)
}
catch (error) {
failure ||= error
}
}
if (failure)
throw failure
runCleanups(record.callbacks.splice(0).map(([name, callback]) => () => record.off.call(record.dash, name, callback)))
}
function fail(record: Binding<Level, Track>, error: unknown): void {