diff --git a/docs/compiled/artplayer-plugin-dash-control.js b/docs/compiled/artplayer-plugin-dash-control.js
index 3925d4075..215fc831f 100644
--- a/docs/compiled/artplayer-plugin-dash-control.js
+++ b/docs/compiled/artplayer-plugin-dash-control.js
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
-!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let 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}}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,{$video:c}=i.template,{errorHandle:s}=i.constructor.utils;let a=!1,l=0;const u=e(i,"dash-quality",''),f=e(i,"dash-audio",''),d=[],h=n({active:t=>!a&&!i.isDestroy&&i.dash===t,refresh:p,reset(){const t=++l;g(()=>t===l)}});function g(t=()=>!0){let e;for(const r of[u.clear,f.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(a||i.isDestroy)return;const e=++l,n=i.dash,o=()=>!a&&!i.isDestroy&&e===l&&i.dash===n,d=()=>o()&&n.getVideoElement()===c&&o();try{if(s(n.getVideoElement()===c,'Cannot find instance of DASH from "art.dash"'),!o())return;if(h.bind(n),!d())return;const e=r.quality||{},i=function(e,n,r){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),i=o.levels();if(!r()||!i?.length)return null;const c=n.auto||"Auto",s=n.getName||(t=>`${t.height}p`),a=o.current(i);if(!r())return null;const l=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!r())return null;const u=!l&&a?s(a):c;if(!r())return null;const f=[];for(const[t,h]of i.entries()){const e=s(h);if(!r())return null;f.push({html:e,...o.item(h,t,a,l)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:c,value:"auto",default:l}),{html:u,title:n.title||"Quality",selector:d,select:o.select}}(n,e,d);if(!d())return;if(u.update(e,i,d),!d())return;const a=r.audio||{},l=function(e,n,r){const o=e.getTracksFor("audio");if(!r()||!o?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>t.lang||t.id),s=e.getCurrentTrackFor("audio")||o[0];if(!r())return null;const a=s?c(s):i;if(!r())return null;const l=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(o,s),u=[];for(const t of o){const e=c(t);if(!r())return null;u.push({html:e,value:t,default:t===l})}const f=t(u);return{html:a,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(n,a,d);d()&&f.update(a,l,d)}catch(p){if(o()){const t=++l;for(const e of[h.release,()=>g(()=>l===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw p}}function y(){if(a)return;let t;a=!0,l++;const e=[h.release,g,...d.splice(0).map(([t,e])=>()=>i.off(t,e))];for(const r of e)try{r()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",y]];for(const e of t){if(a)break;d.push(e),i.on(...e)}}catch(m){try{y()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw m}return{name:"artplayerPluginDashControl",update:p}}}});
+!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,{$video:c}=i.template,{errorHandle:s}=i.constructor.utils;let a=!1,l=0;const u=e(i,"dash-quality",''),f=e(i,"dash-audio",''),d=[],h=n({active:t=>!a&&!i.isDestroy&&i.dash===t,refresh(){const t=i.dash;let e=l;const n=()=>!a&&!i.isDestroy&&i.dash===t&&l===e,r=u.captureNavigation(n)||f.captureNavigation(n);n()&&(e++,p(),n()&&r?.())},reset(){const t=++l;g(()=>t===l)}});function g(t=()=>!0){let e;for(const r of[u.clear,f.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(a||i.isDestroy)return;const e=++l,n=i.dash,o=()=>!a&&!i.isDestroy&&e===l&&i.dash===n,d=()=>o()&&n.getVideoElement()===c&&o();try{if(s(n.getVideoElement()===c,'Cannot find instance of DASH from "art.dash"'),!o())return;if(h.bind(n),!d())return;const e=r.quality||{},i=function(e,n,r){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),i=o.levels();if(!r()||!i?.length)return null;const c=n.auto||"Auto",s=n.getName||(t=>`${t.height}p`),a=o.current(i);if(!r())return null;const l=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!r())return null;const u=!l&&a?s(a):c;if(!r())return null;const f=[];for(const[t,h]of i.entries()){const e=s(h);if(!r())return null;f.push({html:e,...o.item(h,t,a,l)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:c,value:"auto",default:l}),{html:u,title:n.title||"Quality",selector:d,select:o.select}}(n,e,d);if(!d())return;if(u.update(e,i,d),!d())return;const a=r.audio||{},l=function(e,n,r){const o=e.getTracksFor("audio");if(!r()||!o?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>t.lang||t.id),s=e.getCurrentTrackFor("audio")||o[0];if(!r())return null;const a=s?c(s):i;if(!r())return null;const l=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(o,s),u=[];for(const t of o){const e=c(t);if(!r())return null;u.push({html:e,value:t,default:t===l})}const f=t(u);return{html:a,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(n,a,d);d()&&f.update(a,l,d)}catch(p){if(o()){const t=++l;for(const e of[h.release,()=>g(()=>l===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw p}}function y(){if(a)return;let t;a=!0,l++;const e=[h.release,g,...d.splice(0).map(([t,e])=>()=>i.off(t,e))];for(const r of e)try{r()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",y]];for(const e of t){if(a)break;d.push(e),i.on(...e)}}catch(m){try{y()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw m}return{name:"artplayerPluginDashControl",update:p}}}});
diff --git a/docs/compiled/artplayer-plugin-dash-control.legacy.js b/docs/compiled/artplayer-plugin-dash-control.legacy.js
index e259b2e74..097a689bf 100644
--- a/docs/compiled/artplayer-plugin-dash-control.legacy.js
+++ b/docs/compiled/artplayer-plugin-dash-control.legacy.js
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
-!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,o=(e,n,r)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[n]=r,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&o(t,l,i[l]);if(e)for(var l of e(i))r.call(i,l)&&o(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 r;const o=new Map;function i(n,r=o.get(n)){var i,l,c,a;if(!r||o.get(n)!==r)return;o.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)!==r||"setting"===n&&s.find&&(null==(a=s.find(e))?void 0:a.onSelect)!==r||s.remove(e)}function l(){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(c,a,s){if(!a)return void l();const u={};r=u;const f=()=>u===r&&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?(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()&&(c.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:l}}function a(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 l(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(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=r(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(o(),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=r(s);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(s.suspended=!1),l(s)}]),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(o){return}let r=!1;return()=>{var t,o,i,l,c,a,s,u,f;if(!r&&e()){r=!0;try{const r=null==(t=n.getVideoElement)?void 0:t.call(n);if(!(null==r?void 0:r.seeking)||!Number.isFinite(r.currentTime))return;const d=r.currentTime,h=null==(o=n.getActiveStream)?void 0:o.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 o=null==(c=t.getType)?void 0:c.call(t);if("audio"!==o&&"video"!==o)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(o),p=i.getRangeAt(d),y=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!y||!1!==i.getIsPruningInProgress())continue;const v=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(v)&&0===v.length){if(!e()||(null==(s=n.getActiveStream)?void 0:s.call(n))!==h||(null==(u=n.getDashMetrics)?void 0:u.call(n))!==g||(null==(f=n.getVideoElement)?void 0:f.call(n))!==r||r.currentTime!==d||!r.seeking||!e())return;g.addBufferLevel(o,new Date,0)}}}catch(d){console.warn("ArtPlayer DASH seek buffer refresh failed:",d)}finally{r=!1}}}}(c,()=>n(s)&&!s.suspended);o&&e.push(["playbackSeeking",o]),e.push(["playbackTimeUpdated",()=>{if(n(s)&&!s.suspended)try{const t=r(s);n(s)&&t!==s.automatic&&(s.automatic=t,l(s))}catch(t){i(s,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(s)){s.epoch++,s.queued=!1,s.suspended=!0;try{t.reset()}catch(e){i(s,e)}}}]);for(const t of e){if(!n(s))break;s.callbacks.push(t),u.call(c,...t)}}catch(f){if(e===s)try{o()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:o}}return function(t={}){return e=>{const n=e,{$video:r}=n.template,{errorHandle:o}=n.constructor.utils;let s=!1,u=0;const f=c(n,"dash-quality",''),d=c(n,"dash-audio",''),h=[],g=a({active:t=>!s&&!n.isDestroy&&n.dash===t,refresh:y,reset(){const t=++u;p(()=>t===u)}});function p(t=()=>!0){let e;for(const r of[f.clear,d.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function y(){if(s||n.isDestroy)return;const e=++u,c=n.dash,a=()=>!s&&!n.isDestroy&&e===u&&n.dash===c,h=()=>a()&&c.getVideoElement()===r&&a();try{if(o(c.getVideoElement()===r,'Cannot find instance of DASH from "art.dash"'),!a())return;if(g.bind(c),!h())return;const e=t.quality||{},n=function(t,e,n){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),o=r.levels();if(!n()||!(null==o?void 0:o.length))return null;const c=e.auto||"Auto",a=e.getName||(t=>`${t.height}p`),s=r.current(o);if(!n())return null;const u=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!u&&s?a(s):c;if(!n())return null;const d=[];for(const[l,g]of o.entries()){const t=a(g);if(!n())return null;d.push(i({html:t},r.item(g,l,s,u)))}const h=l(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:u}),{html:f,title:e.title||"Quality",selector:h,select:r.select}}(c,e,h);if(!h())return;if(f.update(e,n,h),!h())return;const s=t.audio||{},u=function(t,e,n){const r=t.getTracksFor("audio");if(!n()||!(null==r?void 0:r.length))return null;const o=e.auto||"Auto",i=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||r[0];if(!n())return null;const a=c?i(c):o;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"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(r,c),u=[];for(const l of r){const t=i(l);if(!n())return null;u.push({html:t,value:l,default:l===s})}const f=l(u);return{html:a,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,s,h);h()&&d.update(s,u,h)}catch(y){if(a()){const t=++u;for(const e of[g.release,()=>p(()=>u===t)])try{e()}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw y}}function v(){if(s)return;let t;s=!0,u++;const e=[g.release,p,...h.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(r){t||(t=r)}if(t)throw t}try{const t=[["ready",y],["restart",y],["destroy",v]];for(const e of t){if(s)break;h.push(e),n.on(...e)}}catch(m){try{v()}catch(w){console.warn("ArtPlayer DASH subscription cleanup failed:",w)}throw m}return{name:"artplayerPluginDashControl",update:y}}}});
+!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.getActiveStream)?void 0:s.call(n))!==h||(null==(u=n.getDashMetrics)?void 0:u.call(n))!==g||(null==(f=n.getVideoElement)?void 0:f.call(n))!==o||o.currentTime!==d||!o.seeking||!e())return;g.addBufferLevel(r,new Date,0)}}}catch(d){console.warn("ArtPlayer DASH seek buffer refresh failed:",d)}finally{o=!1}}}}(c,()=>n(s)&&!s.suspended);r&&e.push(["playbackSeeking",r]),e.push(["playbackTimeUpdated",()=>{if(n(s)&&!s.suspended)try{const t=o(s);n(s)&&t!==s.automatic&&(s.automatic=t,l(s))}catch(t){i(s,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(s)){s.epoch++,s.queued=!1,s.suspended=!0;try{t.reset()}catch(e){i(s,e)}}}]);for(const t of e){if(!n(s))break;s.callbacks.push(t),u.call(c,...t)}}catch(f){if(e===s)try{r()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:r}}return function(t={}){return e=>{const n=e,{$video:o}=n.template,{errorHandle:r}=n.constructor.utils;let s=!1,u=0;const f=c(n,"dash-quality",''),d=c(n,"dash-audio",''),h=[],g=a({active:t=>!s&&!n.isDestroy&&n.dash===t,refresh(){const t=n.dash;let e=u;const o=()=>!s&&!n.isDestroy&&n.dash===t&&u===e,r=f.captureNavigation(o)||d.captureNavigation(o);o()&&(e++,v(),o()&&(null==r||r()))},reset(){const t=++u;p(()=>t===u)}});function p(t=()=>!0){let e;for(const o of[f.clear,d.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function v(){if(s||n.isDestroy)return;const e=++u,c=n.dash,a=()=>!s&&!n.isDestroy&&e===u&&n.dash===c,h=()=>a()&&c.getVideoElement()===o&&a();try{if(r(c.getVideoElement()===o,'Cannot find instance of DASH from "art.dash"'),!a())return;if(g.bind(c),!h())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",a=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const u=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!u&&s?a(s):c;if(!n())return null;const d=[];for(const[l,g]of r.entries()){const t=a(g);if(!n())return null;d.push(i({html:t},o.item(g,l,s,u)))}const h=l(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:u}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(c,e,h);if(!h())return;if(f.update(e,n,h),!h())return;const s=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",i=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const a=c?i(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(o,c),u=[];for(const l of o){const t=i(l);if(!n())return null;u.push({html:t,value:l,default:l===s})}const f=l(u);return{html:a,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,s,h);h()&&d.update(s,u,h)}catch(v){if(a()){const t=++u;for(const e of[g.release,()=>p(()=>u===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw v}}function y(){if(s)return;let t;s=!0,u++;const e=[g.release,p,...h.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(o){t||(t=o)}if(t)throw t}try{const t=[["ready",v],["restart",v],["destroy",y]];for(const e of t){if(s)break;h.push(e),n.on(...e)}}catch(m){try{y()}catch(w){console.warn("ArtPlayer DASH subscription cleanup failed:",w)}throw m}return{name:"artplayerPluginDashControl",update:v}}}});
diff --git a/docs/compiled/artplayer-plugin-dash-control.mjs b/docs/compiled/artplayer-plugin-dash-control.mjs
index 90b9a2295..f7049a29d 100644
--- a/docs/compiled/artplayer-plugin-dash-control.mjs
+++ b/docs/compiled/artplayer-plugin-dash-control.mjs
@@ -167,7 +167,22 @@ function createMenu(art, name, icon) {
remove("setting");
}
}
- return { update, clear };
+ function captureNavigation(active) {
+ const setting = art.setting;
+ const item = setting.find?.(name);
+ const callback = owned.get("setting");
+ if (!active() || !callback || setting.show !== true || item?.onSelect !== callback || !item.selector || setting.active !== item.selector || typeof setting.render !== "function")
+ return;
+ return () => {
+ if (!active() || art.setting !== setting || setting.show !== true || !setting.option || setting.active !== setting.option)
+ return;
+ const replacement = setting.find?.(name);
+ const callback2 = owned.get("setting");
+ if (active() && callback2 && replacement?.onSelect === callback2 && replacement.selector)
+ setting.render?.(replacement.selector);
+ };
+ }
+ return { update, clear, captureNavigation };
}
const $quality = '';
function createSeekRecovery(value, active) {
@@ -365,7 +380,18 @@ function artplayerPluginDashControl(option = {}) {
const subscriptions = [];
const observer = observeSDK({
active: (dash) => !closed && !art.isDestroy && art.dash === dash,
- refresh: update,
+ refresh() {
+ const dash = art.dash;
+ let version = revision;
+ const current = () => !closed && !art.isDestroy && art.dash === dash && revision === version;
+ const restore = quality.captureNavigation(current) || audio.captureNavigation(current);
+ if (!current())
+ return;
+ version++;
+ update();
+ if (current())
+ restore?.();
+ },
reset() {
const version = ++revision;
clear(() => version === revision);
diff --git a/packages/artplayer-plugin-dash-control/ARCHITECTURE.md b/packages/artplayer-plugin-dash-control/ARCHITECTURE.md
index 30911cfab..b7ff7764d 100644
--- a/packages/artplayer-plugin-dash-control/ARCHITECTURE.md
+++ b/packages/artplayer-plugin-dash-control/ARCHITECTURE.md
@@ -8,6 +8,19 @@ destroys it, changes its source, or adds a runtime dependency on dash.js.
## Module map
+Automatic SDK refresh captures an open, plugin-owned quality or audio settings
+panel before updating both menus. The menu module restores its new selector only
+if settings remain visible, navigation was reset to the root, and the replacement
+still belongs to this plugin. Restoration runs after both updates because updating
+the sibling menu also resets the core settings view. It uses existing optional
+`find`, `active`, `option`, `show` and `render` capabilities; no core API change or
+new event subscription is required. The entry checks SDK identity, destruction
+and the exact update revision before restoration and during its final lookup.
+Nested explicit updates invalidate the earlier restore. Closed, foreign, removed
+or unsupported panels are not reopened. Explicit public `update()` retains the
+historical navigation behavior. Browser tests drive real SDK selection changes
+while each submenu is open and then select another option through normal clicks.
+
| File | Responsibility |
| ------------------------------------------ | --------------------------------------------------------------------------------------- |
| `src/index.ts` | Deferred installation, media identity, update revisions, event subscription and cleanup |
@@ -16,7 +29,7 @@ destroys it, changes its source, or adds a runtime dependency on dash.js.
| `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 |
-| `src/menu.ts` | Existing control/setting registries, menu ownership and guarded selection callbacks |
+| `src/menu.ts` | Control/setting ownership, selection callbacks and owned settings navigation |
| `src/audio.svg`, `src/quality.svg` | Existing setting icons |
| `types/artplayer-plugin-dash-control.d.ts` | Public options, generic SDK inputs and callable result |
diff --git a/packages/artplayer-plugin-dash-control/README.md b/packages/artplayer-plugin-dash-control/README.md
index a55858402..d3368c183 100644
--- a/packages/artplayer-plugin-dash-control/README.md
+++ b/packages/artplayer-plugin-dash-control/README.md
@@ -49,5 +49,8 @@ MIT © Harvey Zhao
## Maintenance
+Automatic SDK refresh keeps an open quality or audio settings menu usable while
+updating its selected item. Explicit `update()` keeps its existing behavior.
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for module ownership, compatibility rules,
test commands and the remaining real SDK playback validation scope.
diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js
index 3925d4075..215fc831f 100644
--- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js
+++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
-!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let 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}}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,{$video:c}=i.template,{errorHandle:s}=i.constructor.utils;let a=!1,l=0;const u=e(i,"dash-quality",''),f=e(i,"dash-audio",''),d=[],h=n({active:t=>!a&&!i.isDestroy&&i.dash===t,refresh:p,reset(){const t=++l;g(()=>t===l)}});function g(t=()=>!0){let e;for(const r of[u.clear,f.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(a||i.isDestroy)return;const e=++l,n=i.dash,o=()=>!a&&!i.isDestroy&&e===l&&i.dash===n,d=()=>o()&&n.getVideoElement()===c&&o();try{if(s(n.getVideoElement()===c,'Cannot find instance of DASH from "art.dash"'),!o())return;if(h.bind(n),!d())return;const e=r.quality||{},i=function(e,n,r){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),i=o.levels();if(!r()||!i?.length)return null;const c=n.auto||"Auto",s=n.getName||(t=>`${t.height}p`),a=o.current(i);if(!r())return null;const l=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!r())return null;const u=!l&&a?s(a):c;if(!r())return null;const f=[];for(const[t,h]of i.entries()){const e=s(h);if(!r())return null;f.push({html:e,...o.item(h,t,a,l)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:c,value:"auto",default:l}),{html:u,title:n.title||"Quality",selector:d,select:o.select}}(n,e,d);if(!d())return;if(u.update(e,i,d),!d())return;const a=r.audio||{},l=function(e,n,r){const o=e.getTracksFor("audio");if(!r()||!o?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>t.lang||t.id),s=e.getCurrentTrackFor("audio")||o[0];if(!r())return null;const a=s?c(s):i;if(!r())return null;const l=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(o,s),u=[];for(const t of o){const e=c(t);if(!r())return null;u.push({html:e,value:t,default:t===l})}const f=t(u);return{html:a,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(n,a,d);d()&&f.update(a,l,d)}catch(p){if(o()){const t=++l;for(const e of[h.release,()=>g(()=>l===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw p}}function y(){if(a)return;let t;a=!0,l++;const e=[h.release,g,...d.splice(0).map(([t,e])=>()=>i.off(t,e))];for(const r of e)try{r()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",y]];for(const e of t){if(a)break;d.push(e),i.on(...e)}}catch(m){try{y()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw m}return{name:"artplayerPluginDashControl",update:p}}}});
+!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,{$video:c}=i.template,{errorHandle:s}=i.constructor.utils;let a=!1,l=0;const u=e(i,"dash-quality",''),f=e(i,"dash-audio",''),d=[],h=n({active:t=>!a&&!i.isDestroy&&i.dash===t,refresh(){const t=i.dash;let e=l;const n=()=>!a&&!i.isDestroy&&i.dash===t&&l===e,r=u.captureNavigation(n)||f.captureNavigation(n);n()&&(e++,p(),n()&&r?.())},reset(){const t=++l;g(()=>t===l)}});function g(t=()=>!0){let e;for(const r of[u.clear,f.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function p(){if(a||i.isDestroy)return;const e=++l,n=i.dash,o=()=>!a&&!i.isDestroy&&e===l&&i.dash===n,d=()=>o()&&n.getVideoElement()===c&&o();try{if(s(n.getVideoElement()===c,'Cannot find instance of DASH from "art.dash"'),!o())return;if(h.bind(n),!d())return;const e=r.quality||{},i=function(e,n,r){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(e),i=o.levels();if(!r()||!i?.length)return null;const c=n.auto||"Auto",s=n.getName||(t=>`${t.height}p`),a=o.current(i);if(!r())return null;const l=e.getSettings().streaming.abr.autoSwitchBitrate.video;if(!r())return null;const u=!l&&a?s(a):c;if(!r())return null;const f=[];for(const[t,h]of i.entries()){const e=s(h);if(!r())return null;f.push({html:e,...o.item(h,t,a,l)})}const d=t(f).sort((t,e)=>e.value-t.value);return d.push({html:c,value:"auto",default:l}),{html:u,title:n.title||"Quality",selector:d,select:o.select}}(n,e,d);if(!d())return;if(u.update(e,i,d),!d())return;const a=r.audio||{},l=function(e,n,r){const o=e.getTracksFor("audio");if(!r()||!o?.length)return null;const i=n.auto||"Auto",c=n.getName||(t=>t.lang||t.id),s=e.getCurrentTrackFor("audio")||o[0];if(!r())return null;const a=s?c(s):i;if(!r())return null;const l=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(o,s),u=[];for(const t of o){const e=c(t);if(!r())return null;u.push({html:e,value:t,default:t===l})}const f=t(u);return{html:a,title:n.title||"Audio",selector:f,select:t=>e.setCurrentTrack(t.value)}}(n,a,d);d()&&f.update(a,l,d)}catch(p){if(o()){const t=++l;for(const e of[h.release,()=>g(()=>l===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw p}}function y(){if(a)return;let t;a=!0,l++;const e=[h.release,g,...d.splice(0).map(([t,e])=>()=>i.off(t,e))];for(const r of e)try{r()}catch(n){t||(t=n)}if(t)throw t}try{const t=[["ready",p],["restart",p],["destroy",y]];for(const e of t){if(a)break;d.push(e),i.on(...e)}}catch(m){try{y()}catch(v){console.warn("ArtPlayer DASH subscription cleanup failed:",v)}throw m}return{name:"artplayerPluginDashControl",update:p}}}});
diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js
index e259b2e74..097a689bf 100644
--- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js
+++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
-!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,o=(e,n,r)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[n]=r,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&o(t,l,i[l]);if(e)for(var l of e(i))r.call(i,l)&&o(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 r;const o=new Map;function i(n,r=o.get(n)){var i,l,c,a;if(!r||o.get(n)!==r)return;o.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)!==r||"setting"===n&&s.find&&(null==(a=s.find(e))?void 0:a.onSelect)!==r||s.remove(e)}function l(){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(c,a,s){if(!a)return void l();const u={};r=u;const f=()=>u===r&&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?(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()&&(c.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:l}}function a(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 l(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(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=r(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(o(),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=r(s);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(s.suspended=!1),l(s)}]),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(o){return}let r=!1;return()=>{var t,o,i,l,c,a,s,u,f;if(!r&&e()){r=!0;try{const r=null==(t=n.getVideoElement)?void 0:t.call(n);if(!(null==r?void 0:r.seeking)||!Number.isFinite(r.currentTime))return;const d=r.currentTime,h=null==(o=n.getActiveStream)?void 0:o.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 o=null==(c=t.getType)?void 0:c.call(t);if("audio"!==o&&"video"!==o)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(o),p=i.getRangeAt(d),y=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!y||!1!==i.getIsPruningInProgress())continue;const v=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(v)&&0===v.length){if(!e()||(null==(s=n.getActiveStream)?void 0:s.call(n))!==h||(null==(u=n.getDashMetrics)?void 0:u.call(n))!==g||(null==(f=n.getVideoElement)?void 0:f.call(n))!==r||r.currentTime!==d||!r.seeking||!e())return;g.addBufferLevel(o,new Date,0)}}}catch(d){console.warn("ArtPlayer DASH seek buffer refresh failed:",d)}finally{r=!1}}}}(c,()=>n(s)&&!s.suspended);o&&e.push(["playbackSeeking",o]),e.push(["playbackTimeUpdated",()=>{if(n(s)&&!s.suspended)try{const t=r(s);n(s)&&t!==s.automatic&&(s.automatic=t,l(s))}catch(t){i(s,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(s)){s.epoch++,s.queued=!1,s.suspended=!0;try{t.reset()}catch(e){i(s,e)}}}]);for(const t of e){if(!n(s))break;s.callbacks.push(t),u.call(c,...t)}}catch(f){if(e===s)try{o()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:o}}return function(t={}){return e=>{const n=e,{$video:r}=n.template,{errorHandle:o}=n.constructor.utils;let s=!1,u=0;const f=c(n,"dash-quality",''),d=c(n,"dash-audio",''),h=[],g=a({active:t=>!s&&!n.isDestroy&&n.dash===t,refresh:y,reset(){const t=++u;p(()=>t===u)}});function p(t=()=>!0){let e;for(const r of[f.clear,d.clear]){if(!t())break;try{r()}catch(n){e||(e=n)}}if(e)throw e}function y(){if(s||n.isDestroy)return;const e=++u,c=n.dash,a=()=>!s&&!n.isDestroy&&e===u&&n.dash===c,h=()=>a()&&c.getVideoElement()===r&&a();try{if(o(c.getVideoElement()===r,'Cannot find instance of DASH from "art.dash"'),!a())return;if(g.bind(c),!h())return;const e=t.quality||{},n=function(t,e,n){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 r=t.getQualityFor("video");return n.find(t=>t.qualityIndex===r)},item:(t,n,r,o)=>e?{value:n,id:t.id,default:!o&&null!=r&&t.id===r.id}:{value:t.qualityIndex,default:!o&&null!=r&&t.qualityIndex===r.qualityIndex},select(n,r){const o="auto"===n.value;t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:o}}}}),!o&&r()&&(e?t.setRepresentationForTypeById("video",n.id):t.setQualityFor("video",n.value))}}}(t),o=r.levels();if(!n()||!(null==o?void 0:o.length))return null;const c=e.auto||"Auto",a=e.getName||(t=>`${t.height}p`),s=r.current(o);if(!n())return null;const u=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!u&&s?a(s):c;if(!n())return null;const d=[];for(const[l,g]of o.entries()){const t=a(g);if(!n())return null;d.push(i({html:t},r.item(g,l,s,u)))}const h=l(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:u}),{html:f,title:e.title||"Quality",selector:h,select:r.select}}(c,e,h);if(!h())return;if(f.update(e,n,h),!h())return;const s=t.audio||{},u=function(t,e,n){const r=t.getTracksFor("audio");if(!n()||!(null==r?void 0:r.length))return null;const o=e.auto||"Auto",i=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||r[0];if(!n())return null;const a=c?i(c):o;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"],r=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===r.length?r[0]:void 0}(r,c),u=[];for(const l of r){const t=i(l);if(!n())return null;u.push({html:t,value:l,default:l===s})}const f=l(u);return{html:a,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,s,h);h()&&d.update(s,u,h)}catch(y){if(a()){const t=++u;for(const e of[g.release,()=>p(()=>u===t)])try{e()}catch(v){console.warn("ArtPlayer DASH cleanup failed:",v)}}throw y}}function v(){if(s)return;let t;s=!0,u++;const e=[g.release,p,...h.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(r){t||(t=r)}if(t)throw t}try{const t=[["ready",y],["restart",y],["destroy",v]];for(const e of t){if(s)break;h.push(e),n.on(...e)}}catch(m){try{v()}catch(w){console.warn("ArtPlayer DASH subscription cleanup failed:",w)}throw m}return{name:"artplayerPluginDashControl",update:y}}}});
+!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.getActiveStream)?void 0:s.call(n))!==h||(null==(u=n.getDashMetrics)?void 0:u.call(n))!==g||(null==(f=n.getVideoElement)?void 0:f.call(n))!==o||o.currentTime!==d||!o.seeking||!e())return;g.addBufferLevel(r,new Date,0)}}}catch(d){console.warn("ArtPlayer DASH seek buffer refresh failed:",d)}finally{o=!1}}}}(c,()=>n(s)&&!s.suspended);r&&e.push(["playbackSeeking",r]),e.push(["playbackTimeUpdated",()=>{if(n(s)&&!s.suspended)try{const t=o(s);n(s)&&t!==s.automatic&&(s.automatic=t,l(s))}catch(t){i(s,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(s)){s.epoch++,s.queued=!1,s.suspended=!0;try{t.reset()}catch(e){i(s,e)}}}]);for(const t of e){if(!n(s))break;s.callbacks.push(t),u.call(c,...t)}}catch(f){if(e===s)try{r()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:r}}return function(t={}){return e=>{const n=e,{$video:o}=n.template,{errorHandle:r}=n.constructor.utils;let s=!1,u=0;const f=c(n,"dash-quality",''),d=c(n,"dash-audio",''),h=[],g=a({active:t=>!s&&!n.isDestroy&&n.dash===t,refresh(){const t=n.dash;let e=u;const o=()=>!s&&!n.isDestroy&&n.dash===t&&u===e,r=f.captureNavigation(o)||d.captureNavigation(o);o()&&(e++,v(),o()&&(null==r||r()))},reset(){const t=++u;p(()=>t===u)}});function p(t=()=>!0){let e;for(const o of[f.clear,d.clear]){if(!t())break;try{o()}catch(n){e||(e=n)}}if(e)throw e}function v(){if(s||n.isDestroy)return;const e=++u,c=n.dash,a=()=>!s&&!n.isDestroy&&e===u&&n.dash===c,h=()=>a()&&c.getVideoElement()===o&&a();try{if(r(c.getVideoElement()===o,'Cannot find instance of DASH from "art.dash"'),!a())return;if(g.bind(c),!h())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",a=e.getName||(t=>`${t.height}p`),s=o.current(r);if(!n())return null;const u=t.getSettings().streaming.abr.autoSwitchBitrate.video;if(!n())return null;const f=!u&&s?a(s):c;if(!n())return null;const d=[];for(const[l,g]of r.entries()){const t=a(g);if(!n())return null;d.push(i({html:t},o.item(g,l,s,u)))}const h=l(d).sort((t,e)=>e.value-t.value);return h.push({html:c,value:"auto",default:u}),{html:f,title:e.title||"Quality",selector:h,select:o.select}}(c,e,h);if(!h())return;if(f.update(e,n,h),!h())return;const s=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",i=e.getName||(t=>t.lang||t.id),c=t.getCurrentTrackFor("audio")||o[0];if(!n())return null;const a=c?i(c):r;if(!n())return null;const s=function(t,e){if(t.includes(e))return e;if(null==e.id&&null==e.index)return;const n=["id","index","lang"],o=t.filter(t=>n.every(n=>null==e[n]||t[n]===e[n]));return 1===o.length?o[0]:void 0}(o,c),u=[];for(const l of o){const t=i(l);if(!n())return null;u.push({html:t,value:l,default:l===s})}const f=l(u);return{html:a,title:e.title||"Audio",selector:f,select:e=>t.setCurrentTrack(e.value)}}(c,s,h);h()&&d.update(s,u,h)}catch(v){if(a()){const t=++u;for(const e of[g.release,()=>p(()=>u===t)])try{e()}catch(y){console.warn("ArtPlayer DASH cleanup failed:",y)}}throw v}}function y(){if(s)return;let t;s=!0,u++;const e=[g.release,p,...h.splice(0).map(([t,e])=>()=>n.off(t,e))];for(const n of e)try{n()}catch(o){t||(t=o)}if(t)throw t}try{const t=[["ready",v],["restart",v],["destroy",y]];for(const e of t){if(s)break;h.push(e),n.on(...e)}}catch(m){try{y()}catch(w){console.warn("ArtPlayer DASH subscription cleanup failed:",w)}throw m}return{name:"artplayerPluginDashControl",update:v}}}});
diff --git a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs
index 90b9a2295..f7049a29d 100644
--- a/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs
+++ b/packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs
@@ -167,7 +167,22 @@ function createMenu(art, name, icon) {
remove("setting");
}
}
- return { update, clear };
+ function captureNavigation(active) {
+ const setting = art.setting;
+ const item = setting.find?.(name);
+ const callback = owned.get("setting");
+ if (!active() || !callback || setting.show !== true || item?.onSelect !== callback || !item.selector || setting.active !== item.selector || typeof setting.render !== "function")
+ return;
+ return () => {
+ if (!active() || art.setting !== setting || setting.show !== true || !setting.option || setting.active !== setting.option)
+ return;
+ const replacement = setting.find?.(name);
+ const callback2 = owned.get("setting");
+ if (active() && callback2 && replacement?.onSelect === callback2 && replacement.selector)
+ setting.render?.(replacement.selector);
+ };
+ }
+ return { update, clear, captureNavigation };
}
const $quality = '';
function createSeekRecovery(value, active) {
@@ -365,7 +380,18 @@ function artplayerPluginDashControl(option = {}) {
const subscriptions = [];
const observer = observeSDK({
active: (dash) => !closed && !art.isDestroy && art.dash === dash,
- refresh: update,
+ refresh() {
+ const dash = art.dash;
+ let version = revision;
+ const current = () => !closed && !art.isDestroy && art.dash === dash && revision === version;
+ const restore = quality.captureNavigation(current) || audio.captureNavigation(current);
+ if (!current())
+ return;
+ version++;
+ update();
+ if (current())
+ restore?.();
+ },
reset() {
const version = ++revision;
clear(() => version === revision);
diff --git a/packages/artplayer-plugin-dash-control/src/index.ts b/packages/artplayer-plugin-dash-control/src/index.ts
index 72370c6e7..2a1ab7c0e 100644
--- a/packages/artplayer-plugin-dash-control/src/index.ts
+++ b/packages/artplayer-plugin-dash-control/src/index.ts
@@ -20,7 +20,18 @@ export default function artplayerPluginDashControl({
active: dash => !closed && !art.isDestroy && art.dash === dash,
- refresh: update,
+ refresh() {
+ const dash = art.dash
+ let version = revision
+ const current = () => !closed && !art.isDestroy && art.dash === dash && revision === version
+ const restore = quality.captureNavigation(current) || audio.captureNavigation(current)
+ if (!current())
+ return
+ version++
+ update()
+ if (current())
+ restore?.()
+ },
reset() {
const version = ++revision
clear(() => version === revision)
diff --git a/packages/artplayer-plugin-dash-control/src/menu.ts b/packages/artplayer-plugin-dash-control/src/menu.ts
index 64e2b3db8..787a17f0c 100644
--- a/packages/artplayer-plugin-dash-control/src/menu.ts
+++ b/packages/artplayer-plugin-dash-control/src/menu.ts
@@ -1,4 +1,4 @@
-import type { DisplayConfig, Label, MenuHost, MenuModel, SelectorItem, Valid } from './types'
+import type { Cleanup, DisplayConfig, Label, MenuHost, MenuModel, SelectorItem, Valid } from './types'
export function createMenu- (art: MenuHost, name: string, icon: string) {
let current: object | undefined
@@ -70,5 +70,21 @@ export function createMenu
- (art: MenuHost, name: strin
}
}
- return { update, clear }
+ function captureNavigation(active: Valid): Cleanup | undefined {
+ const setting = art.setting
+ const item = setting.find?.(name)
+ const callback = owned.get('setting')
+ if (!active() || !callback || setting.show !== true || item?.onSelect !== callback || !item.selector || setting.active !== item.selector || typeof setting.render !== 'function')
+ return
+ return () => {
+ if (!active() || art.setting !== setting || setting.show !== true || !setting.option || setting.active !== setting.option)
+ return
+ const replacement = setting.find?.(name)
+ const callback = owned.get('setting')
+ if (active() && callback && replacement?.onSelect === callback && replacement.selector)
+ setting.render?.(replacement.selector)
+ }
+ }
+
+ return { update, clear, captureNavigation }
}
diff --git a/packages/artplayer-plugin-dash-control/src/types.ts b/packages/artplayer-plugin-dash-control/src/types.ts
index af69ef94a..be90f3879 100644
--- a/packages/artplayer-plugin-dash-control/src/types.ts
+++ b/packages/artplayer-plugin-dash-control/src/types.ts
@@ -77,7 +77,11 @@ export interface Registry {
remove: (name: string) => unknown
check: (item: SelectorItem) => unknown
cache?: { get?: (name: string) => { option?: { onSelect?: unknown } } | undefined }
- find?: (name: string) => { onSelect?: unknown } | undefined
+ find?: (name: string) => { onSelect?: unknown, selector?: SelectorItem[] } | null | undefined
+ active?: SelectorItem[] | null
+ option?: SelectorItem[]
+ show?: boolean
+ render?: (option: SelectorItem[]) => unknown
}
export interface MenuHost {
diff --git a/refactor/baselines/dash-menu-validation.json b/refactor/baselines/dash-menu-validation.json
new file mode 100644
index 000000000..09b1d4c08
--- /dev/null
+++ b/refactor/baselines/dash-menu-validation.json
@@ -0,0 +1,415 @@
+{
+ "schemaVersion": 1,
+ "task": "PKG-DASH-MENU-01",
+ "baselineCommit": "30f646be97aaa929265392c661fd6cddad2037f9",
+ "rootCause": "SDK automatic refresh calls setting.update for both menus. Core update renders the root panel; the open quality/audio panel loses art-current before the click. Restore the owned panel only after both automatic updates, guarded by SDK identity, update revision and ownership. Explicit update is unchanged.",
+ "originalFailure": {
+ "file": "refactor/.cache/dash-seek-legacy-final-sdk-report/report.json",
+ "sha256": "74a3924972eeb7f3b22a3595bb1d5450088bba025cc83e022066fd3820989d8a",
+ "title": "published core / DASH 5.2.1: actual settings quality selection survives SDK refresh",
+ "project": "chromium",
+ "trace": {
+ "file": "refactor/.cache/dash-seek-legacy-final-sdk-report/results/dash-sdk-published-core-DA-81045-ection-survives-SDK-refresh-chromium/trace.zip",
+ "sha256": "3b3a12dd98abdedb95a4aa0ab74cdb72f51ed132e0df504bd2872d2cd3c6dc4f"
+ },
+ "panelBeforeClickAt": 63311.912,
+ "panelAfterTimeoutAt": 82551.665,
+ "observation": "Settings remain shown; active quality selector is replaced by root settings panel. Controlled real-SDK refresh reproduces the same navigation reset on all four tested core/SDK pairs."
+ },
+ "environment": {
+ "node": "24.21.0",
+ "yarn": "1.22.22",
+ "platform": "win32",
+ "remoteCI": false,
+ "physicalDevices": false
+ },
+ "source": [
+ {
+ "file": "packages/artplayer-plugin-dash-control/src/index.ts",
+ "sha256LF": "e22cfd0a3a2129913278cb2fbb50e2a7b2333cc9bd53647ec764d3f8f4f30ccc"
+ },
+ {
+ "file": "packages/artplayer-plugin-dash-control/src/menu.ts",
+ "sha256LF": "f5049045ead1d47c6275b0d13fc862b53a90e2accfe99935f641d07757e06428"
+ },
+ {
+ "file": "packages/artplayer-plugin-dash-control/src/types.ts",
+ "sha256LF": "d812577c7bd877f6998b55747916117da1f400067977292907e3a910e04c239c"
+ },
+ {
+ "file": "test/dash-events.test.js",
+ "sha256LF": "b753e21e068b02fc8176bac1cc804dc3b7daaa59f152cdc1f14ca5b910c8a39a"
+ },
+ {
+ "file": "test/browser/dash-sdk.spec.js",
+ "sha256LF": "e1b2980cfbdd948180308321705e39ca9b8df06d4bde33e421f66abbc5ce2065"
+ }
+ ],
+ "artifacts": [
+ {
+ "file": "refactor/.cache/dash-package-types-QIKdVC/runtime/artplayer-plugin-dash-control.js",
+ "member": "package/dist/artplayer-plugin-dash-control.js",
+ "sha256": "2d639a5c6a7044ada2b1d74e5b039d4cc787996b997c5a186a67724848f36c2a",
+ "archiveSha256": "83cd29340bbc6788f9b069d18909fd0344aeb2fa8e12515f92796cfd7bd2fdab"
+ },
+ {
+ "file": "refactor/.cache/dash-package-types-QIKdVC/runtime/artplayer-plugin-dash-control.legacy.js",
+ "member": "package/dist/artplayer-plugin-dash-control.legacy.js",
+ "sha256": "eef089514bf7304c7910e4f093c7c350495458284c6f55871358ae085f504ddc",
+ "archiveSha256": "83cd29340bbc6788f9b069d18909fd0344aeb2fa8e12515f92796cfd7bd2fdab"
+ },
+ {
+ "file": "refactor/.cache/dash-package-types-QIKdVC/runtime/artplayer-plugin-dash-control.mjs",
+ "member": "package/dist/artplayer-plugin-dash-control.mjs",
+ "sha256": "bec1c9fa15fa6b80841517b9afe29da8c63a4d28fc57a6218c869b0c0e407703",
+ "archiveSha256": "83cd29340bbc6788f9b069d18909fd0344aeb2fa8e12515f92796cfd7bd2fdab"
+ }
+ ],
+ "reports": [
+ {
+ "directory": "refactor/.cache/dash-menu-red-report",
+ "reportSha256": "09c08de619c8b1c67c4abbcd36827ed36a94c8424b3cea69422690d9b9fb974f",
+ "result": {
+ "scope": "source",
+ "exitCode": 1,
+ "signal": null,
+ "args": [
+ "test",
+ "--config=playwright.source.config.js",
+ "test/browser/dash-sdk.spec.js",
+ "--project=chromium",
+ "--grep",
+ "SDK refresh preserves the open quality",
+ "--workers=2"
+ ]
+ },
+ "stats": {
+ "startTime": "2026-09-14T12:21:57.499Z",
+ "duration": 23198.907,
+ "expected": 0,
+ "skipped": 0,
+ "unexpected": 4,
+ "flaky": 0
+ },
+ "browsers": [
+ "153.0.8010.12"
+ ],
+ "inputs": [
+ {
+ "sdk": "4.5.2",
+ "sdkArchiveSha256": "9a0944c48deb0fd8f92b0eea436c5b0d0aea9237c7da0078c0310f1430d0a31f",
+ "sdkCodeMember": "package/dist/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "b9c81fe7223b79775b5c2d3b9dbc79477b7e993201a8412fba81143df39972cf"
+ },
+ {
+ "sdk": "5.2.1",
+ "sdkArchiveSha256": "4eacb0cc3525276666ca3f084611cdabb5a2264d128dfe0750fe106fbddd088f",
+ "sdkCodeMember": "package/dist/modern/umd/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "b9c81fe7223b79775b5c2d3b9dbc79477b7e993201a8412fba81143df39972cf"
+ }
+ ],
+ "failures": [
+ {
+ "title": "published core / DASH 4.5.2: SDK refresh preserves the open quality settings panel",
+ "project": "chromium",
+ "errors": [
+ "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32mtrue\u001b[39m\nReceived: \u001b[31mfalse\u001b[39m\n\nCall Log:\n- Timeout 7000ms exceeded while waiting on the predicate\n\n 353 | const changed = type === 'quality' ? '180p' : 'fr'\n 354 | await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(changed)\n> 355 | await expect.poll(() => page.evaluate(type => window.art.setting.active === window.art.setting.find(`dash-${type}`).selector, type)).toBe(true)\n | ^\n 356 | const label = type === 'quality' ? '90p' : 'en'\n 357 | await page.locator('.art-setting-panel.art-current .art-setting-item-left-text').filter({ hasText: new RegExp(`^${label}$`) }).click()\n 358 | if (type === 'quality')\n at D:\\github\\ArtPlayer\\test\\browser\\dash-sdk.spec.js:355:142"
+ ]
+ },
+ {
+ "title": "published core / DASH 5.2.1: SDK refresh preserves the open quality settings panel",
+ "project": "chromium",
+ "errors": [
+ "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32mtrue\u001b[39m\nReceived: \u001b[31mfalse\u001b[39m\n\nCall Log:\n- Timeout 7000ms exceeded while waiting on the predicate\n\n 353 | const changed = type === 'quality' ? '180p' : 'fr'\n 354 | await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(changed)\n> 355 | await expect.poll(() => page.evaluate(type => window.art.setting.active === window.art.setting.find(`dash-${type}`).selector, type)).toBe(true)\n | ^\n 356 | const label = type === 'quality' ? '90p' : 'en'\n 357 | await page.locator('.art-setting-panel.art-current .art-setting-item-left-text').filter({ hasText: new RegExp(`^${label}$`) }).click()\n 358 | if (type === 'quality')\n at D:\\github\\ArtPlayer\\test\\browser\\dash-sdk.spec.js:355:142"
+ ]
+ },
+ {
+ "title": "candidate core / DASH 4.5.2: SDK refresh preserves the open quality settings panel",
+ "project": "chromium",
+ "errors": [
+ "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32mtrue\u001b[39m\nReceived: \u001b[31mfalse\u001b[39m\n\nCall Log:\n- Timeout 7000ms exceeded while waiting on the predicate\n\n 353 | const changed = type === 'quality' ? '180p' : 'fr'\n 354 | await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(changed)\n> 355 | await expect.poll(() => page.evaluate(type => window.art.setting.active === window.art.setting.find(`dash-${type}`).selector, type)).toBe(true)\n | ^\n 356 | const label = type === 'quality' ? '90p' : 'en'\n 357 | await page.locator('.art-setting-panel.art-current .art-setting-item-left-text').filter({ hasText: new RegExp(`^${label}$`) }).click()\n 358 | if (type === 'quality')\n at D:\\github\\ArtPlayer\\test\\browser\\dash-sdk.spec.js:355:142"
+ ]
+ },
+ {
+ "title": "candidate core / DASH 5.2.1: SDK refresh preserves the open quality settings panel",
+ "project": "chromium",
+ "errors": [
+ "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32mtrue\u001b[39m\nReceived: \u001b[31mfalse\u001b[39m\n\nCall Log:\n- Timeout 7000ms exceeded while waiting on the predicate\n\n 353 | const changed = type === 'quality' ? '180p' : 'fr'\n 354 | await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(changed)\n> 355 | await expect.poll(() => page.evaluate(type => window.art.setting.active === window.art.setting.find(`dash-${type}`).selector, type)).toBe(true)\n | ^\n 356 | const label = type === 'quality' ? '90p' : 'en'\n 357 | await page.locator('.art-setting-panel.art-current .art-setting-item-left-text').filter({ hasText: new RegExp(`^${label}$`) }).click()\n 358 | if (type === 'quality')\n at D:\\github\\ArtPlayer\\test\\browser\\dash-sdk.spec.js:355:142"
+ ]
+ }
+ ],
+ "menuCases": 4
+ },
+ {
+ "directory": "refactor/.cache/dash-menu-source-green-report",
+ "reportSha256": "f793008f42ccca55b431bba347aca88d79602b17a58eb05384b276881079f63e",
+ "result": {
+ "scope": "source",
+ "exitCode": 0,
+ "signal": null,
+ "args": [
+ "test",
+ "--config=playwright.source.config.js",
+ "test/browser/dash-sdk.spec.js",
+ "--project=chromium",
+ "--project=firefox",
+ "--grep",
+ "SDK refresh preserves the open|actual settings",
+ "--workers=2"
+ ]
+ },
+ "stats": {
+ "startTime": "2026-09-14T12:23:06.427Z",
+ "duration": 41683.534,
+ "expected": 32,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "browsers": [
+ "153.0.8010.12",
+ "155.0"
+ ],
+ "inputs": [
+ {
+ "sdk": "4.5.2",
+ "sdkArchiveSha256": "9a0944c48deb0fd8f92b0eea436c5b0d0aea9237c7da0078c0310f1430d0a31f",
+ "sdkCodeMember": "package/dist/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "d5fce5ae917ad8fa3e666aceed9a23f626bfbdd89fd73379db56c85e145ae02d"
+ },
+ {
+ "sdk": "5.2.1",
+ "sdkArchiveSha256": "4eacb0cc3525276666ca3f084611cdabb5a2264d128dfe0750fe106fbddd088f",
+ "sdkCodeMember": "package/dist/modern/umd/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "d5fce5ae917ad8fa3e666aceed9a23f626bfbdd89fd73379db56c85e145ae02d"
+ }
+ ],
+ "failures": [],
+ "menuCases": 32
+ },
+ {
+ "directory": "refactor/.cache/dash-menu-source-final-report",
+ "reportSha256": "7385e41e449f203b7c177355d0a16bafc26e798c7c8104b31fad7d47fa689a73",
+ "result": {
+ "scope": "source",
+ "exitCode": 0,
+ "signal": null,
+ "args": [
+ "test",
+ "--config=playwright.source.config.js",
+ "test/browser/dash-sdk.spec.js",
+ "--project=chromium",
+ "--project=firefox",
+ "--grep-invert",
+ "native SDK track switch|published DASH / 4.5.2",
+ "--workers=2"
+ ]
+ },
+ "stats": {
+ "startTime": "2026-09-14T12:26:48.315Z",
+ "duration": 137303.375,
+ "expected": 92,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "browsers": [
+ "153.0.8010.12",
+ "155.0"
+ ],
+ "inputs": [
+ {
+ "sdk": "4.5.2",
+ "sdkArchiveSha256": "9a0944c48deb0fd8f92b0eea436c5b0d0aea9237c7da0078c0310f1430d0a31f",
+ "sdkCodeMember": "package/dist/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "5bd2e6b031996c2b42970a790769f6e91752cef76697522d0e57c6d8909fb568"
+ },
+ {
+ "sdk": "5.2.1",
+ "sdkArchiveSha256": "4eacb0cc3525276666ca3f084611cdabb5a2264d128dfe0750fe106fbddd088f",
+ "sdkCodeMember": "package/dist/modern/umd/dash.all.min.js",
+ "candidate": "workspace source build",
+ "candidateSha256": "5bd2e6b031996c2b42970a790769f6e91752cef76697522d0e57c6d8909fb568"
+ }
+ ],
+ "failures": [],
+ "menuCases": 32
+ },
+ {
+ "directory": "refactor/.cache/dash-menu-main-report",
+ "reportSha256": "dd4d0ae710851220e7edc007b752d7678c404eb8c7cec4484f925bf81bc74858",
+ "result": {
+ "scope": "source",
+ "exitCode": 0,
+ "signal": null,
+ "args": [
+ "test",
+ "--config=playwright.source.config.js",
+ "test/browser/dash-sdk.spec.js",
+ "--project=chromium",
+ "--project=firefox",
+ "--grep",
+ "SDK refresh preserves the open|actual settings",
+ "--workers=2"
+ ]
+ },
+ "stats": {
+ "startTime": "2026-09-14T12:30:20.600Z",
+ "duration": 41334.104,
+ "expected": 32,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "browsers": [
+ "153.0.8010.12",
+ "155.0"
+ ],
+ "inputs": [
+ {
+ "sdk": "4.5.2",
+ "sdkArchiveSha256": "9a0944c48deb0fd8f92b0eea436c5b0d0aea9237c7da0078c0310f1430d0a31f",
+ "sdkCodeMember": "package/dist/dash.all.min.js",
+ "candidate": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-QIKdVC\\runtime\\artplayer-plugin-dash-control.js",
+ "candidateSha256": "2d639a5c6a7044ada2b1d74e5b039d4cc787996b997c5a186a67724848f36c2a"
+ },
+ {
+ "sdk": "5.2.1",
+ "sdkArchiveSha256": "4eacb0cc3525276666ca3f084611cdabb5a2264d128dfe0750fe106fbddd088f",
+ "sdkCodeMember": "package/dist/modern/umd/dash.all.min.js",
+ "candidate": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-QIKdVC\\runtime\\artplayer-plugin-dash-control.js",
+ "candidateSha256": "2d639a5c6a7044ada2b1d74e5b039d4cc787996b997c5a186a67724848f36c2a"
+ }
+ ],
+ "failures": [],
+ "menuCases": 32
+ },
+ {
+ "directory": "refactor/.cache/dash-menu-legacy-report",
+ "reportSha256": "3f1db2574bbfd759a6382b09b55842444df6db3005902460acf3a6839304840a",
+ "result": {
+ "scope": "source",
+ "exitCode": 0,
+ "signal": null,
+ "args": [
+ "test",
+ "--config=playwright.source.config.js",
+ "test/browser/dash-sdk.spec.js",
+ "--project=chromium",
+ "--project=firefox",
+ "--grep-invert",
+ "native SDK track switch|published DASH / 4.5.2",
+ "--workers=2"
+ ]
+ },
+ "stats": {
+ "startTime": "2026-09-14T12:31:23.841Z",
+ "duration": 136092.201,
+ "expected": 92,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "browsers": [
+ "153.0.8010.12",
+ "155.0"
+ ],
+ "inputs": [
+ {
+ "sdk": "4.5.2",
+ "sdkArchiveSha256": "9a0944c48deb0fd8f92b0eea436c5b0d0aea9237c7da0078c0310f1430d0a31f",
+ "sdkCodeMember": "package/dist/dash.all.min.js",
+ "candidate": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-QIKdVC\\runtime\\artplayer-plugin-dash-control.legacy.js",
+ "candidateSha256": "eef089514bf7304c7910e4f093c7c350495458284c6f55871358ae085f504ddc"
+ },
+ {
+ "sdk": "5.2.1",
+ "sdkArchiveSha256": "4eacb0cc3525276666ca3f084611cdabb5a2264d128dfe0750fe106fbddd088f",
+ "sdkCodeMember": "package/dist/modern/umd/dash.all.min.js",
+ "candidate": "D:\\github\\ArtPlayer\\refactor\\.cache\\dash-package-types-QIKdVC\\runtime\\artplayer-plugin-dash-control.legacy.js",
+ "candidateSha256": "eef089514bf7304c7910e4f093c7c350495458284c6f55871358ae085f504ddc"
+ }
+ ],
+ "failures": [],
+ "menuCases": 32
+ }
+ ],
+ "node": {
+ "file": "refactor/.cache/dash-menu-node-artifacts.log",
+ "sha256": "9abf0919778aa5daac80e6951b5f6bc32238a7972389a08bdbdafcf2d065c8e9",
+ "passed": 446,
+ "failed": 0,
+ "skipped": 0,
+ "initialReentrancyFailures": {
+ "file": "refactor/.cache/dash-menu-node-initial.log",
+ "sha256": "ca9a1394e5ab7f64e1e225b9e25eda8e87a9d99472e1b35d26d8d9ef24f2b0b0"
+ }
+ },
+ "types": {
+ "file": "refactor/.cache/dash-package-types-QIKdVC/report.json",
+ "sha256": "5f3fb18d59cb60fcba08e6f6aa43b947f97a0c283d832c1391f66c581ae87b7f",
+ "archives": [
+ {
+ "name": "artplayer",
+ "sha256": "d4a0cc5dcbe015e254b3bce64d505ee4e010792ab70414bcf8d0139d7c413b75"
+ },
+ {
+ "name": "artplayer-plugin-dash-control",
+ "sha256": "83cd29340bbc6788f9b069d18909fd0344aeb2fa8e12515f92796cfd7bd2fdab"
+ }
+ ],
+ "matrix": [
+ {
+ "compiler": "5.9.3",
+ "mode": "node10-commonjs",
+ "diagnostics": 0,
+ "invalidRejected": 8
+ },
+ {
+ "compiler": "5.9.3",
+ "mode": "nodenext-cjs",
+ "diagnostics": 0,
+ "invalidRejected": 8
+ },
+ {
+ "compiler": "5.9.3",
+ "mode": "nodenext-esm",
+ "diagnostics": 0,
+ "invalidRejected": 8
+ },
+ {
+ "compiler": "5.9.3",
+ "mode": "bundler-esm",
+ "diagnostics": 0,
+ "invalidRejected": 8
+ },
+ {
+ "compiler": "4.3.5",
+ "mode": "node10-commonjs",
+ "diagnostics": 0,
+ "invalidRejected": 8
+ }
+ ],
+ "method": "Offline Yarn install and frozen reinstall outside workspace; all installed files match tarball. Browser uses extracted tarball runtime, not a complete installed browser suite."
+ },
+ "checks": {
+ "strictTypeScriptExitCode": 0,
+ "targetedLintExitCode": 0,
+ "rootLint": {
+ "file": "refactor/.cache/dash-menu-lint.log",
+ "sha256": "8f03fe722a69933197e1985dcd5bffe6110fa8c03be9fa9d92313447762ca4bf",
+ "errors": 0,
+ "existingWarnings": 1
+ }
+ },
+ "limitations": [
+ "Full DASH combination and distribution acceptance remains open",
+ "Standalone SDK 4.5.2 and frozen published-plugin failure controls remain unchanged; only these invocations exclude them from the broad candidate matrix",
+ "WebKit MSE capability, physical devices, remote CI and global release reviews are not completed here"
+ ]
+}
diff --git a/refactor/changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md b/refactor/changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md
new file mode 100644
index 000000000..1132d2e72
--- /dev/null
+++ b/refactor/changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md
@@ -0,0 +1,63 @@
+# PKG-DASH-MENU-01:SDK 自动刷新保留设置子菜单
+
+## 问题和根因
+
+此前最终 legacy SDK 矩阵中,Chromium、旧核心和 dash.js 5.2.1 的 180p 设置点击
+超时。trace 在点击前显示质量子菜单拥有 `art-current`;失败时设置仍然显示,
+但根菜单重新成为当前面板。选项仍存在于隐藏子菜单,所以 locator 已解析但不可见。
+原报告保留于 `refactor/.cache/dash-seek-legacy-final-sdk-report`,不覆盖该失败。
+
+SDK 自动事件刷新通过 `update()` 更新质量、音轨两个菜单。核心 `setting.update()`
+会回到根面板;两次更新都会重置导航。新增真实 SDK 选择变化的回归用例,在保持
+菜单打开时触发实际质量/音轨选择,检查刷新后的当前面板,并用普通鼠标点击选择
+另一项。修复前质量用例在新旧核心、两代 SDK 的四个 Chromium 组合全部失败。
+这将 trace 中的导航复位现象与可控的真实 SDK 刷新联系起来,没有伪造 SDK 事件。
+
+## 实现、兼容和类型
+
+`src/menu.ts` 在自动刷新前捕获本插件拥有的打开子菜单,两项更新完成后才恢复
+新的 selector。恢复要求设置仍显示、当前面板确实回到根菜单、目标仍由插件拥有。
+关闭、移除、其他调用方接管及缺少可选导航能力时不恢复,不强制打开设置。
+该职责与菜单所有权放在同一模块,不新增只用于转发的抽象或异步资源。
+
+`src/index.ts` 将此逻辑限定在 SDK observer 的自动 refresh 中;公开同步 `update()`
+继续保留原有导航行为。恢复与原 SDK 身份、销毁状态、确切 update revision 绑定。
+初版的嵌套显式 update 用例有两项失败,补入 revision 守卫后通过;销毁中的最后
+lookup 同样不能重新打开菜单。`src/types.ts` 只添加可选内部导航能力的窄类型。
+
+工厂/option/update 签名、SDK 选择顺序、公开声明、DOM/CSS hooks、入口不变。
+没有修改核心 setting.update 的语义,没有强制点击或增加等待超时,没有新增依赖。
+包内 README 与 ARCHITECTURE 已解释自动和显式刷新、导航及资源所有权。
+
+## 验证
+
+固定 Node24.21.0/Yarn1.22.22,Windows 的 Chromium153.0.8010.12、Firefox155.0。
+源码初轮设置矩阵32项通过;增加重入守卫后,完整候选DASH定向矩阵92项通过。
+源码及main/legacy/ESM三格式Node446项通过,包内严格TS与专项lint通过;根lint
+0错误/1个既有warning。最终含文档tarball在仓库外离线安装并冻结重装,全部
+文件指纹核对,五组类型模式通过且各拒绝八项非法调用。
+
+打包产物的main设置矩阵32项、legacy完整定向矩阵92项均通过,0失败/跳过/重试。
+任务完成,DASH-MENU-01按根因和旧红新绿证据关闭;完整组合及发布任务仍开放。
+浏览器使用核验tarball解出的运行时,类型检查使用实际安装包;不是完整installed
+浏览器验收。[最终报告](../baselines/dash-menu-validation.json)记录每个浏览器的
+输入SHA、退出码及失败,不以套件名称替代输入来源证明。
+
+一次tarball提取辅助脚本最初把字节传给需要路径的函数,提取失败;随后运行未能
+设置产物路径而实际验证了源码。已按附件输入明确归档为源码92项,修正提取后
+main/legacy另行运行并核对候选SHA,未把该源码结果计作打包产物验证。
+
+定向候选矩阵仅在命令行排除已冻结的裸SDK与旧发布插件对照;默认完整文件与CI
+仍保留它们的原断言。原DASH-SEEK-01的上游缺陷、PKG-DASH-05/06、WebKit能力、
+设备、远端CI及三轮发布复盘不因本次菜单修复而完成。
+
+## 重跑与回退
+
+`yarn test:dash-control`、`yarn test:dash-types-package`;设置专项通过
+`yarn test:browser:source test/browser/dash-sdk.spec.js --project=chromium
+--project=firefox --grep 'SDK refresh preserves the open|actual settings' --workers=2`。
+产物验证必须显式设置 `ARTPLAYER_DASH_ARTIFACT` 为已核验的main/legacy文件。
+完整候选范围使用 `--grep-invert 'native SDK track switch|published DASH / 4.5.2'`;
+去掉过滤将包含保留的历史SDK/旧插件失败。每次运行前先归档已有报告。
+
+回退本任务独立提交并重建DASH三格式即可,不单独修改生成文件。没有push、部署或发布。
diff --git a/refactor/dash-validation.md b/refactor/dash-validation.md
index b7522f608..16eb66f72 100644
--- a/refactor/dash-validation.md
+++ b/refactor/dash-validation.md
@@ -99,3 +99,13 @@ UMD 测试 VM 显式注入宿主 console,以检查原始错误身份;没有
跳转4失败,新源码8通过,最终main/legacy各8个严格跳转通过。main完整定向76通过;
legacy同范围75通过、1个SDK5.2.1设置点击不可见失败,归PKG-DASH-MENU-01继续调查。
不将局部跳转修复等同于DASH组合/分发验收全部完成。
+
+## 自动刷新设置导航修复
+
+PKG-DASH-MENU-01已确认SDK自动刷新调用setting.update重置根面板,导致打开的
+子菜单失去art-current;在真实SDK四组组合复现。插件现在只为自动刷新捕获并
+恢复仍由自己拥有、仍打开的子菜单,且在质量和音轨两项更新后恢复。显式update
+保持原行为;revision/SDK/销毁/菜单所有权防止陈旧恢复。源码92、main设置32、
+legacy候选92均通过;Node446和五组安装类型通过,原始75/1报告保留。
+见[实施记录](changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md)及
+[证据](baselines/dash-menu-validation.json)。DASH-MENU-01关闭,其他SDK/设备/分发门槛不变。
diff --git a/refactor/plan.md b/refactor/plan.md
index 0b8194a18..20aab76f6 100644
--- a/refactor/plan.md
+++ b/refactor/plan.md
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 261 项,范围 22 个包及工作区/示例。
-状态:todo 50 / doing 20 / blocked 0 / done 191 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
+状态:todo 49 / doing 20 / blocked 0 / done 192 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -219,7 +219,7 @@
| PKG-DASH-03 | artplayer-plugin-dash-control
整理内部职责与资源 | PKG-DASH-02, CORE-11, CORE-14 | 能力适配保留 SDK 4.x 与 5.x,稳定 ID 映射、ABR 状态、UI 清理和生命周期职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-DASH-04 | artplayer-plugin-dash-control
迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-DASH-SEEK-01 | artplayer-plugin-dash-control
修复 SDK 4.5.2 暂停跳转后的缓冲指标停滞 | PKG-DASH-04 | 按版本和实测空缓冲限定的指标同步、生命周期守卫及真实 SDK 旧红新绿 | 新插件在新旧核心及 Chromium/Firefox 原位置回归通过;不修改 SDK 方法/配置、媒体时间或删除缓冲;旧 SDK 原生缺陷与旧插件限制独立记录,不宣称第三方本体已修复 | H | done |
-| PKG-DASH-MENU-01 | artplayer-plugin-dash-control
定位并修复 SDK 刷新期间设置选项不可见 | PKG-DASH-SEEK-01 | 真实 SDK 菜单点击失败根因、对应兼容修复及新旧核心回归 | 保留 legacy/旧核心/SDK5.2.1 的原失败;确定菜单在点击过程中不可见的原因;不靠 force 点击、扩大超时或重复运行覆盖问题;main/legacy 质量与音轨设置及 SDK 刷新组合通过 | H | todo |
+| PKG-DASH-MENU-01 | artplayer-plugin-dash-control
定位并修复 SDK 刷新期间设置选项不可见 | PKG-DASH-SEEK-01 | 真实 SDK 菜单点击失败根因、对应兼容修复及新旧核心回归 | 保留 legacy/旧核心/SDK5.2.1 的原失败;确定菜单在点击过程中不可见的原因;不靠 force 点击、扩大超时或重复运行覆盖问题;main/legacy 质量与音轨设置及 SDK 刷新组合通过 | H | done |
| PKG-DASH-05 | artplayer-plugin-dash-control
验证新旧核心和组合 | PKG-DASH-04, CORE-22, PKG-DASH-SEEK-01 | 固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
| PKG-DASH-06 | artplayer-plugin-dash-control
验证分发并同步文档 | PKG-DASH-05, ENG-07, PKG-DASH-MENU-01 | dash.control.js、支持的 dash.js 版本与产物 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
@@ -563,7 +563,7 @@
- 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)
- PKG-DASH-SEEK-01: [记录](changes/2026-09-14-PKG-DASH-SEEK-01-buffer-metrics.md) [记录](baselines/dash-seek-recovery-validation.json)
-- PKG-DASH-MENU-01: [记录](changes/2026-09-14-PKG-DASH-SEEK-01-buffer-metrics.md) [记录](baselines/dash-seek-recovery-validation.json)
+- PKG-DASH-MENU-01: [记录](changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md) [记录](baselines/dash-menu-validation.json) [记录](dash-validation.md)
- PKG-DASH-05: [记录](changes/2026-09-12-PKG-DASH-05-sdk-checkpoint.md) [记录](baselines/dash-sdk.json) [记录](baselines/dash-sdk-checkpoint.json) [记录](dash-validation.md) [记录](changes/2026-09-12-PKG-DASH-05-sdk-types.md) [记录](baselines/dash-sdk-types-validation.json) [记录](changes/2026-09-12-PKG-DASH-05-sdk-events.md) [记录](baselines/dash-sdk-events-validation.json) [记录](changes/2026-09-12-PKG-DASH-05-seek-diagnosis.md) [记录](baselines/dash-seek-diagnosis.json)
- PKG-MULTI-SUB-01: [记录](baselines/multiple-subtitles-release.json) [记录](baselines/multiple-subtitles-contract.md) [记录](baselines/multiple-subtitles-contract-validation.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-01-contract.md)
- PKG-MULTI-SUB-02: [记录](baselines/multiple-subtitles-failures.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-02-failures.md)
diff --git a/refactor/progress.md b/refactor/progress.md
index 35d2deaac..f9f09015b 100644
--- a/refactor/progress.md
+++ b/refactor/progress.md
@@ -1,5 +1,21 @@
# 进度与证据
+## PKG-DASH-MENU-01 SDK 刷新设置子菜单修复完成
+
+原legacy设置点击失败的trace显示根面板取代质量子菜单,setting仍显示。
+真实SDK刷新四组旧红用例全部复现;在插件自动refresh中捕获并在两项菜单更新后
+恢复拥有的打开selector,显式update维持旧行为。新旧核心/两SDK/两引擎设置32项
+通过,最终源码完整候选92通过;核验tarball的main设置32、legacy完整候选92通过。
+三格式及源码Node446、仓库外安装/冻结重装与五组类型通过,严格TS/lint通过,
+根lint仍1个既有warning。嵌套update旧版本守卫两项失败补修后通过;销毁和接管不复活。
+
+关闭DASH-MENU-01,原legacy75/1报告与四个新复现均保留,没有force点击、扩大超时
+或覆盖旧失败。辅助提取最初失败后的运行按真实输入计作源码;产物修正后独立验证。
+见[记录](changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md)和
+[证据](baselines/dash-menu-validation.json)。261项:192 done、20 doing、49 todo。
+DASH-SEEK-01的裸SDK/旧插件限制、DASH05/06、设备/远端CI及全局发布仍开放。
+本任务独立本地提交并完成提交审计后继续其他插件与组合验收;没有push或发布。
+
## PKG-DASH-SEEK-01 暂停跳转缓冲指标修复完成
仅在dash.js 4.5.2真实空缓冲且缓存指标陈旧时同步零值,恢复SDK自身下载调度。
diff --git a/refactor/risk-table.md b/refactor/risk-table.md
index 0767afef7..98ed875e1 100644
--- a/refactor/risk-table.md
+++ b/refactor/risk-table.md
@@ -274,4 +274,4 @@
| CHAPTER-FULLSCREEN-GEOMETRY-01 | resolved / 已复现 | Chromium 原生全屏后章节 hover 标题曾出现后又隐藏 | PKG-CHAPTER-05, PKG-CHAPTER-HOVER-01 |
| CANVAS-SUBTITLE-01 | resolved / 已复现 | Canvas底层video缺初始字幕轨道且普通Canvas挂载不能加载原生字幕 | PKG-CANVAS-SUBTITLE-01, PKG-CANVAS-05 |
| DPIP-INSTALLED-PROVENANCE-01 | resolved / 已复现 | Document PiP测试加载器忽略显式安装map并回退到源码 | PKG-CANVAS-05, CI-01 |
-| DASH-MENU-01 | open / 已复现 | SDK 5.2.1 播放中设置质量选项在点击时变为不可见 | PKG-DASH-MENU-01, PKG-DASH-05, REVIEW-02 |
+| DASH-MENU-01 | resolved / 已复现 | SDK 5.2.1 播放中设置质量选项在点击时变为不可见 | PKG-DASH-MENU-01, PKG-DASH-05, REVIEW-02 |
diff --git a/refactor/risks.json b/refactor/risks.json
index 3f8bfa7d4..4a4f95dfd 100644
--- a/refactor/risks.json
+++ b/refactor/risks.json
@@ -6275,7 +6275,7 @@
"id": "DASH-MENU-01",
"title": "SDK 5.2.1 播放中设置质量选项在点击时变为不可见",
"confirmation": "reproduced",
- "status": "open",
+ "status": "resolved",
"owners": [
"PKG-DASH-MENU-01",
"PKG-DASH-05",
@@ -6285,8 +6285,15 @@
"refactor/changes/2026-09-14-PKG-DASH-SEEK-01-buffer-metrics.md",
"refactor/baselines/dash-seek-recovery-validation.json"
],
- "compatibleResolution": "最终 legacy 定向矩阵复现:旧核心、新插件、Chromium 的180p选项先不稳定后不可见;保留点击失败与trace。SDK5.2.1不进入4.5.2指标修复分支,但菜单根因及历史来源尚未确定。",
- "closureCriteria": "定位实际隐藏/刷新时序并提供可复现旧红新绿;新旧核心、main/legacy、质量/音轨、SDK刷新与销毁组合验证,不靠force点击或重跑覆盖失败。"
+ "compatibleResolution": "SDK自动刷新前捕获本插件拥有的打开子菜单,在两项设置更新后按新selector恢复;显式update、关闭/移除/接管以及嵌套更新保持原边界。守卫SDK身份、revision、销毁与菜单所有权。",
+ "closureCriteria": "定位实际隐藏/刷新时序并提供可复现旧红新绿;新旧核心、main/legacy、质量/音轨、SDK刷新与销毁组合验证,不靠force点击或重跑覆盖失败。",
+ "resolutionEvidence": [
+ "refactor/changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md",
+ "refactor/baselines/dash-menu-validation.json",
+ "test/browser/dash-sdk.spec.js",
+ "test/dash-events.test.js"
+ ],
+ "resolutionRationale": "原trace显示设置仍打开但导航退回根面板;真实SDK刷新在四组核心/SDK组合复现相同失败。修复后源码92、main设置32、legacy完整候选92通过,新旧核心/两SDK/两引擎覆盖质量及音轨,原失败与未加force/延长超时的回归均保留。Node446验证三格式及重入/销毁边界;DASH其他SDK/设备/发布风险仍独立开放。"
}
]
}
diff --git a/refactor/tasks.json b/refactor/tasks.json
index 5d23dfa51..527b1bfee 100644
--- a/refactor/tasks.json
+++ b/refactor/tasks.json
@@ -2268,13 +2268,14 @@
"dependsOn": [
"PKG-DASH-SEEK-01"
],
- "status": "todo",
+ "status": "done",
"risk": "H",
"deliverable": "真实 SDK 菜单点击失败根因、对应兼容修复及新旧核心回归",
"acceptance": "保留 legacy/旧核心/SDK5.2.1 的原失败;确定菜单在点击过程中不可见的原因;不靠 force 点击、扩大超时或重复运行覆盖问题;main/legacy 质量与音轨设置及 SDK 刷新组合通过",
"evidence": [
- "changes/2026-09-14-PKG-DASH-SEEK-01-buffer-metrics.md",
- "baselines/dash-seek-recovery-validation.json"
+ "changes/2026-09-14-PKG-DASH-MENU-01-settings-navigation.md",
+ "baselines/dash-menu-validation.json",
+ "dash-validation.md"
]
},
{
diff --git a/test/browser/dash-sdk.spec.js b/test/browser/dash-sdk.spec.js
index b1229479e..54f696ac3 100644
--- a/test/browser/dash-sdk.spec.js
+++ b/test/browser/dash-sdk.spec.js
@@ -330,6 +330,39 @@ for (const version of ['4.5.2', '5.2.1']) {
for (const core of ['published', 'candidate']) {
for (const version of ['4.5.2', '5.2.1']) {
for (const type of ['quality', 'audio']) {
+ test(`${core} core / DASH ${version}: SDK refresh preserves the open ${type} settings panel`, async ({ page }, testInfo) => {
+ await openDash(page, version, core, 'candidate', testInfo)
+ await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
+ await page.locator('#play').click()
+ await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
+ await page.locator('.art-control-setting').click()
+ await page.locator(`[data-name="dash-${type}"]`).click()
+ await page.evaluate((type) => {
+ const { dash } = window.art
+ if (type === 'quality') {
+ dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: false } } } })
+ if (dash.setQualityFor)
+ dash.setQualityFor('video', 1)
+ else
+ dash.setRepresentationForTypeById('video', '1')
+ }
+ else {
+ dash.setCurrentTrack(dash.getTracksFor('audio').find(track => track.lang === 'fr'))
+ }
+ }, type)
+ const changed = type === 'quality' ? '180p' : 'fr'
+ await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(changed)
+ await expect.poll(() => page.evaluate(type => window.art.setting.active === window.art.setting.find(`dash-${type}`).selector, type)).toBe(true)
+ const label = type === 'quality' ? '90p' : 'en'
+ await page.locator('.art-setting-panel.art-current .art-setting-item-left-text').filter({ hasText: new RegExp(`^${label}$`) }).click()
+ if (type === 'quality')
+ await expect.poll(() => page.evaluate(() => window.art.video.videoHeight)).toBe(90)
+ else
+ await expect.poll(() => page.evaluate(() => window.art.dash.getCurrentTrackFor('audio').lang)).toBe('en')
+ await expect(page.locator(`.art-control-dash-${type} .art-selector-value`)).toHaveText(label)
+ expect(await page.evaluate(() => window.sdkErrors)).toEqual([])
+ })
+
test(`${core} core / DASH ${version}: actual settings ${type} selection survives SDK refresh`, async ({ page }, testInfo) => {
await openDash(page, version, core, 'candidate', testInfo)
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
diff --git a/test/dash-events.test.js b/test/dash-events.test.js
index d432d94b8..4e533300c 100644
--- a/test/dash-events.test.js
+++ b/test/dash-events.test.js
@@ -50,6 +50,108 @@ function seekHost() {
return { ...host, samples, levels, buffers, metrics, seekState: state }
}
+function navigationHost(version) {
+ const host = eventHost(version)
+ const setting = host.art.setting
+ const update = setting.update
+ const renders = []
+ Object.assign(setting, {
+ option: [],
+ active: null,
+ show: true,
+ find: name => host.settings.get(name),
+ render(option) {
+ setting.active = option
+ renders.push(option)
+ },
+ update(option) {
+ update(option)
+ setting.active = setting.option
+ },
+ })
+ return { ...host, renders, open(name) {
+ setting.active = host.settings.get(name).selector
+ } }
+}
+
+for (const { name, sdk, factory } of candidates) {
+ test(`DASH ${sdk} (${name}): automatic refresh retains the owned quality or audio panel after both menu updates`, async () => {
+ for (const name of ['dash-quality', 'dash-audio']) {
+ const host = navigationHost(sdk)
+ factory(bothMenus())(host.art).update()
+ host.open(name)
+ const previous = host.art.setting.active
+ host.fire('qualityChangeRendered')
+ await flush()
+ assert.equal(host.renders.length, 1)
+ assert.equal(host.art.setting.active, host.settings.get(name).selector)
+ assert.notEqual(host.art.setting.active, previous)
+ }
+ })
+
+ test(`DASH ${sdk} (${name}): explicit update and closed or foreign panels retain existing navigation behavior`, async () => {
+ for (const mode of ['explicit', 'closed', 'foreign', 'unavailable', 'removed']) {
+ const host = navigationHost(sdk)
+ const option = bothMenus()
+ const plugin = factory(option)(host.art)
+ plugin.update()
+ host.open('dash-quality')
+ if (mode === 'explicit') {
+ plugin.update()
+ }
+ else {
+ if (mode === 'closed')
+ host.art.setting.show = false
+ if (mode === 'foreign')
+ host.settings.get('dash-quality').onSelect = () => {}
+ if (mode === 'unavailable')
+ delete host.art.setting.render
+ if (mode === 'removed')
+ option.quality.setting = false
+ host.fire('qualityChangeRendered')
+ await flush()
+ }
+ assert.equal(host.renders.length, 0)
+ assert.equal(host.art.setting.active, host.art.setting.option)
+ }
+ })
+
+ test(`DASH ${sdk} (${name}): navigation lookup cannot restore an obsolete refresh after a nested explicit update`, async () => {
+ const host = navigationHost(sdk)
+ const plugin = factory(bothMenus())(host.art)
+ plugin.update()
+ host.open('dash-quality')
+ const find = host.art.setting.find
+ let reads = 0
+ host.art.setting.find = (name) => {
+ if (++reads === 2)
+ plugin.update()
+ return find(name)
+ }
+ host.fire('qualityChangeRendered')
+ await flush()
+ assert.equal(host.renders.length, 0)
+ assert.equal(host.art.setting.active, host.art.setting.option)
+ })
+
+ test(`DASH ${sdk} (${name}): destruction from navigation lookup cannot reopen settings`, async () => {
+ const host = navigationHost(sdk)
+ factory(bothMenus())(host.art).update()
+ host.open('dash-quality')
+ const find = host.art.setting.find
+ let reads = 0
+ host.art.setting.find = (name) => {
+ const result = find(name)
+ if (++reads === 2)
+ host.art.destroy()
+ return result
+ }
+ host.fire('qualityChangeRendered')
+ await flush()
+ assert.equal(host.renders.length, 0)
+ })
+}
+
for (const { name, factory } of candidates.filter(item => item.sdk === 4)) {
test(`DASH (${name}): seeking records only measured empty stale metrics without changing SDK methods or media`, () => {
const host = seekHost()