mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
fix(dash): [PKG-DASH-CLEANUP-01] preserve first cleanup exception
This commit is contained in:
1 parent
1c522f181b
commit
25faf88e80
23 files changed
+49546
-147
No files matched your search
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let r;const o=new Map;function i(n,r=o.get(n)){if(!r||o.get(n)!==r)return;o.delete(n);const i=t[n];"controls"===n&&i.cache?.get&&i.cache.get(e)?.option?.onSelect!==r||"setting"===n&&i.find&&i.find(e)?.onSelect!==r||i.remove(e)}function c(){let t;r=void 0;for(const[n,r]of[...o])try{i(n,r)}catch(e){t||(t=e)}if(t)throw t}return{update:function(s,a,l){if(!a)return void c();const u={};r=u;const f=()=>u===r&&l(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.title}: ${e.html}`,f()&&s.control&&t.controls.check(e),f()&&s.setting&&t.setting.check(e),e.html):e.html):e.html;s.control?(o.set("controls",d),t.controls.update({name:e,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(s.setting?(o.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){const r=t.setting,i=r.find?.(e),c=o.get("setting");if(n()&&c&&!0===r.show&&i?.onSelect===c&&i.selector&&r.active===i.selector&&"function"===typeof r.render)return()=>{if(!n()||t.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const i=r.find?.(e),c=o.get("setting");n()&&c&&i?.onSelect===c&&i.selector&&r.render?.(i.selector)}}}}function n(t){let e;const n=n=>e===n&&t.active(n.dash),r=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function o(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,o]of t.callbacks.splice(0))try{t.off.call(t.dash,e,o)}catch(r){n||(n=r)}if(n)throw n}function i(n,r){if(e===n){try{o()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const o=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===o&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=r(e),n(e)&&t.refresh()}catch(c){i(e,c)}finally{e.running=!1}}})}return{bind:function(s){const a=e;if(a?.dash===s){const t=r(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(o(),e||!t.active(s)||"function"!==typeof s.on||"function"!==typeof s.off)return;const l={dash:s,off:s.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=l;const u=s.on;try{l.automatic=r(l);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),o=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),c=o?.getProcessors?.();if(!Array.isArray(c)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const s of c){if(!e())return;const c=s.getType?.();if("audio"!==c&&"video"!==c)continue;const a=s.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(c),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(c,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(s,()=>n(l)&&!l.suspended);o&&e.push(["playbackSeeking",o]),e.push(["playbackTimeUpdated",()=>{if(n(l)&&!l.suspended)try{const t=r(l);n(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){i(l,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{t.reset()}catch(e){i(l,e)}}}]);for(const t of e){if(!n(l))break;l.callbacks.push(t),u.call(s,...t)}}catch(f){if(e===l)try{o()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:o}}return function(r={}){return o=>{const i=o,{$viLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t,e=()=>!0){let n,r=!1;for(const i of t){if(!e())break;try{i()}catch(o){r||(r=!0,n=o)}}if(r)throw n}function e(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function n(e,n,r){let o;const i=new Map;function s(t,r=i.get(t)){if(!r||i.get(t)!==r)return;i.delete(t);const o=e[t];"controls"===t&&o.cache?.get&&o.cache.get(n)?.option?.onSelect!==r||"setting"===t&&o.find&&o.find(n)?.onSelect!==r||o.remove(n)}function c(){o=void 0,t([...i].map(([t,e])=>()=>s(t,e)))}return{update:function(t,a,l){if(!a)return void c();const u={};o=u;const f=()=>u===o&&l(),d=n=>f()?(a.select(n,f),f()?(e.notice.show=`${a.title}: ${n.html}`,f()&&t.control&&e.controls.check(n),f()&&t.setting&&e.setting.check(n),n.html):n.html):n.html;t.control?(i.set("controls",d),e.controls.update({name:n,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):s("controls"),f()&&(t.setting?(i.set("setting",d),e.setting.update({name:n,tooltip:a.html,html:a.title,icon:r,width:200,selector:a.selector,onSelect:d})):s("setting"))},clear:c,captureNavigation:function(t){const r=e.setting,o=r.find?.(n),s=i.get("setting");if(t()&&s&&!0===r.show&&o?.onSelect===s&&o.selector&&r.active===o.selector&&"function"===typeof r.render)return()=>{if(!t()||e.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const o=r.find?.(n),s=i.get("setting");t()&&s&&o?.onSelect===s&&o.selector&&r.render?.(o.selector)}}}}function r(e){let n;const r=t=>n===t&&e.active(t.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function i(){const e=n;e&&(n=void 0,e.epoch++,t(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function s(t,r){if(n===t){try{i()}catch(o){console.warn("ArtPlayer DASH subscription cleanup failed:",o)}if(!n&&e.active(t.dash))try{e.reset()}catch(o){console.warn("ArtPlayer DASH cleanup failed:",o)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(t){if(!r(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const n=t.epoch;Promise.resolve().then(()=>{if(r(t)&&t.epoch===n&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),r(t)&&e.refresh()}catch(i){s(t,i)}finally{t.running=!1}}})}return{bind:function(t){const a=n;if(a?.dash===t){const t=o(a);return void(r(a)&&(a.automatic=t,a.suspended=!1))}if(i(),n||!e.active(t)||"function"!==typeof t.on||"function"!==typeof t.off)return;const l={dash:t,off:t.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};n=l;const u=t.on;try{l.automatic=o(l);const n=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),i=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),s=o?.getProcessors?.();if(!Array.isArray(s)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const c of s){if(!e())return;const s=c.getType?.();if("audio"!==s&&"video"!==s)continue;const a=c.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(s),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(s,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(t,()=>r(l)&&!l.suspended);i&&n.push(["playbackSeeking",i]),n.push(["playbackTimeUpdated",()=>{if(r(l)&&!l.suspended)try{const t=o(l);r(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){s(l,t)}}]),n.push(["streamTeardownComplete",()=>{if(r(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{e.reset()}catch(t){s(l,t)}}}]);for(const e of n){if(!r(l))break;l.callbacks.push(e),u.call(t,...e)}}catch(f){if(n===l)try{i()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:i}}return function(oLine truncated
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t};function l(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(e))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(e))?void 0:a.onSelect)!==o||s.remove(e)}function l(){let t;o=void 0;for(const[n,o]of[...r])try{i(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,a,s){if(!a)return void l();const u={};o=u;const f=()=>u===o&&s(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:l,captureNavigation:function(n){var o;const i=t.setting,l=null==(o=i.find)?void 0:o.call(i,e),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||t.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,e),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function a(t){let e;const n=n=>e===n&&t.active(n.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,r]of t.callbacks.splice(0))try{t.off.call(t.dash,e,r)}catch(o){n||(n=o)}if(n)throw n}function i(n,o){if(e===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function l(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const r=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===r&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=o(e),n(e)&&t.refresh()}catch(l){i(e,l)}finally{e.running=!1}}})}return{bind:function(c){const a=e;if((null==a?void 0:a.dash)===c){const t=o(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(r(),e||!t.active(c)||"function"!==typeof c.on||"function"!==typeof c.off)return;const s={dash:c,off:c.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=s;const u=c.on;try{s.automatic=o(s);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(s.suspended=!1),l(s)}]),r=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var t,r,i,l,c,a,s,u,f;if(!o&&e()){o=!0;try{const o=null==(t=n.getVideoElement)?void 0:t.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,h=null==(r=n.getActiveStream)?void 0:r.call(n),g=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==h?void 0:h.getProcessors)?void 0:l.call(h);if(!Array.isArray(p)||"function"!==typeof(null==g?void 0:g.getCurrentBufferLevel)||"function"!==typeof g.addBufferLevel)return;for(const t of p){if(!e())return;const r=null==(c=t.getType)?void 0:c.call(t);if("audio"!==r&&"video"!==r)continue;const i=null==(a=t.getBufferController)?void 0:a.call(t);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=g.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.length){if(!e()||(null==(s=n.getAcLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginDashControl=t(),define(function(){return e.artplayerPluginDashControl})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginDashControl=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(t,n,o)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[n]=o,i=(e,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(e,l,i[l]);if(t)for(var l of t(i))o.call(i,l)&&r(e,l,i[l]);return e};function l(e,t=()=>!0){let n,o=!1;for(const i of e){if(!t())break;try{i()}catch(r){o||(o=!0,n=r)}}if(o)throw n}function c(e){const t=new Map;return e.filter(e=>{if(void 0===e.html)return!0;const n=t.get(e.html);return n?(e.default&&Object.assign(n,e),!1):(t.set(e.html,e),!0)})}function a(e,t,n){let o;const r=new Map;function i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=e[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(t))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(t))?void 0:a.onSelect)!==o||s.remove(t)}function c(){o=void 0,l([...r].map(([e,t])=>()=>i(e,t)))}return{update:function(l,a,s){if(!a)return void c();const u={};o=u;const f=()=>u===o&&s(),d=t=>f()?(a.select(t,f),f()?(e.notice.show=`${a.title}: ${t.html}`,f()&&l.control&&e.controls.check(t),f()&&l.setting&&e.setting.check(t),t.html):t.html):t.html;l.control?(r.set("controls",d),e.controls.update({name:t,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(l.setting?(r.set("setting",d),e.setting.update({name:t,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){var o;const i=e.setting,l=null==(o=i.find)?void 0:o.call(i,t),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||e.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,t),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function s(e){let t;const n=n=>t===n&&e.active(n.dash),o=e=>e.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const e=t;e&&(t=void 0,e.epoch++,l(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function i(n,o){if(t===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!t&&e.active(n.dash))try{e.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function c(t){if(!n(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const r=t.epoch;Promise.resolve().then(()=>{if(n(t)&&t.epoch===r&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),n(t)&&e.refresh()}catch(l){i(t,l)}finally{t.running=!1}}})}return{bind:function(l){const a=t;if((null==a?void 0:a.dash)===l){const e=o(a);return void(n(a)&&(a.automatic=e,a.suspended=!1))}if(r(),t||!e.active(l)||"function"!==typeof l.on||"function"!==typeof l.off)return;const s={dash:l,off:l.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};t=s;const u=l.on;try{s.automatic=o(s);const t=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(e=>[e,()=>{"streamUpdated"!==e&&"streamInitialized"!==e||(s.suspended=!1),c(s)}]),r=function(e,t){const n=e;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var e,r,i,l,c,a,s,u,f;if(!o&&t()){o=!0;try{const o=null==(e=n.getVideoElement)?void 0:e.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,g=null==(r=n.getActiveStream)?void 0:r.call(n),h=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==g?void 0:g.getProcessors)?void 0:l.call(g);if(!Array.isArray(p)||"function"!==typeof(null==h?void 0:h.getCurrentBufferLevel)||"function"!==typeof h.addBufferLevel)return;for(const e of p){if(!t())return;const r=null==(c=e.getType)?void 0:c.call(e);if("audio"!==r&&"video"!==r)continue;const i=null==(a=e.getBufferController)?void 0:a.call(e);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=h.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.lengLine truncated
|
||||
@@ -5,6 +5,24 @@
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
const $audio = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>';
|
||||
function runCleanups(actions, current = () => true) {
|
||||
let failed = false;
|
||||
let failure;
|
||||
for (const action of actions) {
|
||||
if (!current())
|
||||
break;
|
||||
try {
|
||||
action();
|
||||
} catch (error) {
|
||||
if (!failed) {
|
||||
failed = true;
|
||||
failure = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
throw failure;
|
||||
}
|
||||
function qualityAdapter(dash) {
|
||||
const modern = typeof dash.getRepresentationsByType === "function";
|
||||
return {
|
||||
@@ -120,16 +138,7 @@ function createMenu(art, name, icon) {
|
||||
}
|
||||
function clear() {
|
||||
current = void 0;
|
||||
let failure;
|
||||
for (const [surface, callback] of [...owned]) {
|
||||
try {
|
||||
remove(surface, callback);
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups([...owned].map(([surface, callback]) => () => remove(surface, callback)));
|
||||
}
|
||||
function update(config, model, active) {
|
||||
if (!model) {
|
||||
@@ -248,16 +257,7 @@ function observeSDK(options) {
|
||||
return;
|
||||
binding = void 0;
|
||||
record.epoch++;
|
||||
let failure;
|
||||
for (const [name, callback] of record.callbacks.splice(0)) {
|
||||
try {
|
||||
record.off.call(record.dash, name, callback);
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups(record.callbacks.splice(0).map(([name, callback]) => () => record.off.call(record.dash, name, callback)));
|
||||
}
|
||||
function fail(record, error) {
|
||||
if (binding === record) {
|
||||
@@ -398,18 +398,7 @@ function artplayerPluginDashControl(option = {}) {
|
||||
}
|
||||
});
|
||||
function clear(current = () => true) {
|
||||
let failure;
|
||||
for (const cleanup of [quality.clear, audio.clear]) {
|
||||
if (!current())
|
||||
break;
|
||||
try {
|
||||
cleanup();
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups([quality.clear, audio.clear], current);
|
||||
}
|
||||
function update() {
|
||||
if (closed || art.isDestroy)
|
||||
@@ -455,17 +444,8 @@ function artplayerPluginDashControl(option = {}) {
|
||||
return;
|
||||
closed = true;
|
||||
revision++;
|
||||
let failure;
|
||||
const actions = [observer.release, clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))];
|
||||
for (const action of actions) {
|
||||
try {
|
||||
action();
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups(actions);
|
||||
}
|
||||
try {
|
||||
const entries = [["ready", update], ["restart", update], ["destroy", destroy]];
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@
|
||||
"test:ads": "node --test test/ads.test.js test/ads-lifecycle.test.js",
|
||||
"trans:docs": "node ./scripts/trans-docs.js",
|
||||
"create:plugin": "node ./scripts/plugin/create.js",
|
||||
"test:dash-control": "node --test test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js",
|
||||
"test:dash-control": "node --test test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/dash-cleanup-errors.test.js",
|
||||
"dev": "npx cross-env NODE_ENV=development node ./scripts/dev.js",
|
||||
"build": "npx cross-env NODE_ENV=production node ./scripts/build.js",
|
||||
"lint": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build,library,pages,site-vendor,browser-validation}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --no-fix",
|
||||
@@ -51,7 +51,7 @@
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js test/asr-distribution.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
"test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/jassub-registration.test.js test/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/vast-compatibility.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js test/asr-media-error.test.js test/thumbnail-cleanup-errors.test.js",
|
||||
"test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/jassub-registration.test.js test/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/vast-compatibility.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js test/asr-media-error.test.js test/thumbnail-cleanup-errors.test.js test/dash-cleanup-errors.test.js",
|
||||
"test:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs",
|
||||
"test": "yarn test:node && yarn test:baseline",
|
||||
"test:browser": "playwright test",
|
||||
|
||||
@@ -26,6 +26,7 @@ while each submenu is open and then select another option through normal clicks.
|
||||
| `src/index.ts` | Deferred installation, media identity, update revisions, event subscription and cleanup |
|
||||
| `src/sdk.ts` | 4.x qualityIndex vs 5.x representation ID access and manual/Auto selection |
|
||||
| `src/sdk-events.ts` | Owned SDK subscriptions, coalesced refresh, teardown and asynchronous errors |
|
||||
| `src/cleanup.ts` | Ordered cleanup, revision guards and first thrown value preservation |
|
||||
| `src/seek-buffer.ts` | Exact SDK 4.5.2 empty-seek metric recovery and stale/reentrant measurement guards |
|
||||
| `src/types.ts` | Narrow internal SDK, host, model and cleanup types |
|
||||
| `src/mapping.ts` | Names, current item matching, duplicate labels and selector ordering |
|
||||
@@ -36,7 +37,7 @@ while each submenu is open and then select another option through normal clicks.
|
||||
Dependencies flow from the entry into mapping/menu, and from mapping into the SDK
|
||||
adapter. Mapping has no DOM dependency. Menu receives a model and validity callback;
|
||||
it does not choose SDK versions. The event observer receives active/refresh/reset
|
||||
callbacks and owns no DOM or ArtPlayer object. All seven owned modules are checked with strict
|
||||
callbacks and owns no DOM or ArtPlayer object. All eight owned modules are checked with strict
|
||||
TypeScript, noUncheckedIndexedAccess, and skipLibCheck=false; there are no remaining
|
||||
owned JavaScript modules in this package.
|
||||
|
||||
@@ -98,6 +99,22 @@ or destroy an SDK to handle a plugin rendering failure.
|
||||
|
||||
## Types and compatibility
|
||||
|
||||
`cleanup.ts` is shared by menu removal, SDK unsubscription and entry teardown.
|
||||
It records whether an action threw separately from the thrown value, so `undefined`,
|
||||
`null`, `false`, zero (including negative zero), empty strings and `NaN` propagate
|
||||
unchanged. Remaining owned cleanup actions still run; a newer update revision stops
|
||||
stale menu cleanup before the next action. Later failures cannot replace the first.
|
||||
This does not change the observer's asynchronous warning policy or make the caller's
|
||||
SDK plugin-owned. A caller's throwing `beforeUnmount` can prevent that particular
|
||||
core control from being removed; the plugin releases its sibling surfaces and
|
||||
propagates the original error rather than bypassing the core callback contract.
|
||||
|
||||
`test/dash-cleanup-errors.test.js` covers both SDK method generations, all seven
|
||||
values, secondary errors, complete unsubscription and repeated destruction. The
|
||||
browser regression uses public control updates with fresh selector items: core
|
||||
selector items acquire non-configurable DOM references and must not be reused as
|
||||
new input objects. It exercises actual old/new core `beforeUnmount` callbacks.
|
||||
|
||||
`src/types.ts` describes the narrow SDK and registry surfaces, menu models, event
|
||||
names, labels, and cleanup callbacks. SDK keys may be absent in malformed external
|
||||
data; the internal types retain that possibility instead of asserting every value
|
||||
@@ -155,8 +172,11 @@ legacy fixture is checked against the actual published declaration and candidate
|
||||
`test:dash-types-package` packs the current core and DASH with Yarn, installs the
|
||||
tarballs offline outside the workspace, repeats a frozen install and checks all
|
||||
public type modes without resolving any workspace source. It also verifies that
|
||||
implementation files/config do not leak into the archive. This is type acceptance;
|
||||
the full installed runtime and SDK release matrix still belongs to PKG-DASH-06/05.
|
||||
implementation files/config do not leak into the archive. This is type acceptance.
|
||||
Verified installed files are retained under the report's `installed-artifacts/`
|
||||
directory for subsequent runtime/browser checks; every retained file is checked
|
||||
against its tarball hash before the temporary outside-workspace consumer is removed.
|
||||
The full installed runtime and SDK release matrix still belongs to PKG-DASH-06/05.
|
||||
`dash-control.spec.js` tests exercise real old/new core DOM, menu clicks, native MP4 and cleanup,
|
||||
with controlled SDK methods. They do not load dash.js or test MPD/ABR decoding.
|
||||
PKG-DASH-05 must verify pinned dash.js 4.5.2 and 5.2.1 with real adaptive media and
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function e(t,e,n){let r;const o=new Map;function i(n,r=o.get(n)){if(!r||o.get(n)!==r)return;o.delete(n);const i=t[n];"controls"===n&&i.cache?.get&&i.cache.get(e)?.option?.onSelect!==r||"setting"===n&&i.find&&i.find(e)?.onSelect!==r||i.remove(e)}function c(){let t;r=void 0;for(const[n,r]of[...o])try{i(n,r)}catch(e){t||(t=e)}if(t)throw t}return{update:function(s,a,l){if(!a)return void c();const u={};r=u;const f=()=>u===r&&l(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.title}: ${e.html}`,f()&&s.control&&t.controls.check(e),f()&&s.setting&&t.setting.check(e),e.html):e.html):e.html;s.control?(o.set("controls",d),t.controls.update({name:e,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(s.setting?(o.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){const r=t.setting,i=r.find?.(e),c=o.get("setting");if(n()&&c&&!0===r.show&&i?.onSelect===c&&i.selector&&r.active===i.selector&&"function"===typeof r.render)return()=>{if(!n()||t.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const i=r.find?.(e),c=o.get("setting");n()&&c&&i?.onSelect===c&&i.selector&&r.render?.(i.selector)}}}}function n(t){let e;const n=n=>e===n&&t.active(n.dash),r=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function o(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,o]of t.callbacks.splice(0))try{t.off.call(t.dash,e,o)}catch(r){n||(n=r)}if(n)throw n}function i(n,r){if(e===n){try{o()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const o=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===o&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=r(e),n(e)&&t.refresh()}catch(c){i(e,c)}finally{e.running=!1}}})}return{bind:function(s){const a=e;if(a?.dash===s){const t=r(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(o(),e||!t.active(s)||"function"!==typeof s.on||"function"!==typeof s.off)return;const l={dash:s,off:s.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=l;const u=s.on;try{l.automatic=r(l);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),o=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),c=o?.getProcessors?.();if(!Array.isArray(c)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const s of c){if(!e())return;const c=s.getType?.();if("audio"!==c&&"video"!==c)continue;const a=s.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(c),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(c,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(s,()=>n(l)&&!l.suspended);o&&e.push(["playbackSeeking",o]),e.push(["playbackTimeUpdated",()=>{if(n(l)&&!l.suspended)try{const t=r(l);n(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){i(l,t)}}]),e.push(["streamTeardownComplete",()=>{if(n(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{t.reset()}catch(e){i(l,e)}}}]);for(const t of e){if(!n(l))break;l.callbacks.push(t),u.call(s,...t)}}catch(f){if(e===l)try{o()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:o}}return function(r={}){return o=>{const i=o,{$viLine truncated
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";function t(t,e=()=>!0){let n,r=!1;for(const i of t){if(!e())break;try{i()}catch(o){r||(r=!0,n=o)}}if(r)throw n}function e(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function n(e,n,r){let o;const i=new Map;function s(t,r=i.get(t)){if(!r||i.get(t)!==r)return;i.delete(t);const o=e[t];"controls"===t&&o.cache?.get&&o.cache.get(n)?.option?.onSelect!==r||"setting"===t&&o.find&&o.find(n)?.onSelect!==r||o.remove(n)}function c(){o=void 0,t([...i].map(([t,e])=>()=>s(t,e)))}return{update:function(t,a,l){if(!a)return void c();const u={};o=u;const f=()=>u===o&&l(),d=n=>f()?(a.select(n,f),f()?(e.notice.show=`${a.title}: ${n.html}`,f()&&t.control&&e.controls.check(n),f()&&t.setting&&e.setting.check(n),n.html):n.html):n.html;t.control?(i.set("controls",d),e.controls.update({name:n,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):s("controls"),f()&&(t.setting?(i.set("setting",d),e.setting.update({name:n,tooltip:a.html,html:a.title,icon:r,width:200,selector:a.selector,onSelect:d})):s("setting"))},clear:c,captureNavigation:function(t){const r=e.setting,o=r.find?.(n),s=i.get("setting");if(t()&&s&&!0===r.show&&o?.onSelect===s&&o.selector&&r.active===o.selector&&"function"===typeof r.render)return()=>{if(!t()||e.setting!==r||!0!==r.show||!r.option||r.active!==r.option)return;const o=r.find?.(n),s=i.get("setting");t()&&s&&o?.onSelect===s&&o.selector&&r.render?.(o.selector)}}}}function r(e){let n;const r=t=>n===t&&e.active(t.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function i(){const e=n;e&&(n=void 0,e.epoch++,t(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function s(t,r){if(n===t){try{i()}catch(o){console.warn("ArtPlayer DASH subscription cleanup failed:",o)}if(!n&&e.active(t.dash))try{e.reset()}catch(o){console.warn("ArtPlayer DASH cleanup failed:",o)}}console.warn("ArtPlayer DASH refresh failed:",r)}function c(t){if(!r(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const n=t.epoch;Promise.resolve().then(()=>{if(r(t)&&t.epoch===n&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),r(t)&&e.refresh()}catch(i){s(t,i)}finally{t.running=!1}}})}return{bind:function(t){const a=n;if(a?.dash===t){const t=o(a);return void(r(a)&&(a.automatic=t,a.suspended=!1))}if(i(),n||!e.active(t)||"function"!==typeof t.on||"function"!==typeof t.off)return;const l={dash:t,off:t.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};n=l;const u=t.on;try{l.automatic=o(l);const n=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(l.suspended=!1),c(l)}]),i=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch{return}let r=!1;return()=>{if(!r&&e()){r=!0;try{const t=n.getVideoElement?.();if(!t?.seeking||!Number.isFinite(t.currentTime))return;const r=t.currentTime,o=n.getActiveStream?.(),i=n.getDashMetrics?.(),s=o?.getProcessors?.();if(!Array.isArray(s)||"function"!==typeof i?.getCurrentBufferLevel||"function"!==typeof i.addBufferLevel)return;for(const c of s){if(!e())return;const s=c.getType?.();if("audio"!==s&&"video"!==s)continue;const a=c.getBufferController?.();if("function"!==typeof a?.getRangeAt||"function"!==typeof a.getIsPruningInProgress||"function"!==typeof a.getAllRangesWithSafetyFactor)continue;const l=i.getCurrentBufferLevel(s),u=a.getRangeAt(r),f=null===u||u&&Number.isFinite(u.end)&&u.end<=r;if(!Number.isFinite(l)||l<=0||!f||!1!==a.getIsPruningInProgress())continue;const d=a.getAllRangesWithSafetyFactor(r);if(Array.isArray(d)&&0===d.length){if(!e()||n.getActiveStream?.()!==o||n.getDashMetrics?.()!==i||n.getVideoElement?.()!==t||t.currentTime!==r||!t.seeking||!e())return;i.addBufferLevel(s,new Date,0)}}}catch(t){console.warn("ArtPlayer DASH seek buffer refresh failed:",t)}finally{r=!1}}}}(t,()=>r(l)&&!l.suspended);i&&n.push(["playbackSeeking",i]),n.push(["playbackTimeUpdated",()=>{if(r(l)&&!l.suspended)try{const t=o(l);r(l)&&t!==l.automatic&&(l.automatic=t,c(l))}catch(t){s(l,t)}}]),n.push(["streamTeardownComplete",()=>{if(r(l)){l.epoch++,l.queued=!1,l.suspended=!0;try{e.reset()}catch(t){s(l,t)}}}]);for(const e of n){if(!r(l))break;l.callbacks.push(e),u.call(t,...e)}}catch(f){if(n===l)try{i()}catch(d){console.warn("ArtPlayer DASH subscription cleanup failed:",d)}throw f}},release:i}}return function(oLine truncated
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDashControl=e(),define(function(){return t.artplayerPluginDashControl})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDashControl=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t};function l(t){const e=new Map;return t.filter(t=>{if(void 0===t.html)return!0;const n=e.get(t.html);return n?(t.default&&Object.assign(n,t),!1):(e.set(t.html,t),!0)})}function c(t,e,n){let o;const r=new Map;function i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=t[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(e))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(e))?void 0:a.onSelect)!==o||s.remove(e)}function l(){let t;o=void 0;for(const[n,o]of[...r])try{i(n,o)}catch(e){t||(t=e)}if(t)throw t}return{update:function(c,a,s){if(!a)return void l();const u={};o=u;const f=()=>u===o&&s(),d=e=>f()?(a.select(e,f),f()?(t.notice.show=`${a.title}: ${e.html}`,f()&&c.control&&t.controls.check(e),f()&&c.setting&&t.setting.check(e),e.html):e.html):e.html;c.control?(r.set("controls",d),t.controls.update({name:e,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(c.setting?(r.set("setting",d),t.setting.update({name:e,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:l,captureNavigation:function(n){var o;const i=t.setting,l=null==(o=i.find)?void 0:o.call(i,e),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||t.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,e),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function a(t){let e;const n=n=>e===n&&t.active(n.dash),o=t=>t.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const t=e;if(!t)return;let n;e=void 0,t.epoch++;for(const[e,r]of t.callbacks.splice(0))try{t.off.call(t.dash,e,r)}catch(o){n||(n=o)}if(n)throw n}function i(n,o){if(e===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!e&&t.active(n.dash))try{t.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function l(e){if(!n(e)||e.suspended||e.queued||e.running)return;e.queued=!0;const r=e.epoch;Promise.resolve().then(()=>{if(n(e)&&e.epoch===r&&!e.suspended){e.queued=!1,e.running=!0;try{e.automatic=o(e),n(e)&&t.refresh()}catch(l){i(e,l)}finally{e.running=!1}}})}return{bind:function(c){const a=e;if((null==a?void 0:a.dash)===c){const t=o(a);return void(n(a)&&(a.automatic=t,a.suspended=!1))}if(r(),e||!t.active(c)||"function"!==typeof c.on||"function"!==typeof c.off)return;const s={dash:c,off:c.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};e=s;const u=c.on;try{s.automatic=o(s);const e=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(t=>[t,()=>{"streamUpdated"!==t&&"streamInitialized"!==t||(s.suspended=!1),l(s)}]),r=function(t,e){const n=t;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var t,r,i,l,c,a,s,u,f;if(!o&&e()){o=!0;try{const o=null==(t=n.getVideoElement)?void 0:t.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,h=null==(r=n.getActiveStream)?void 0:r.call(n),g=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==h?void 0:h.getProcessors)?void 0:l.call(h);if(!Array.isArray(p)||"function"!==typeof(null==g?void 0:g.getCurrentBufferLevel)||"function"!==typeof g.addBufferLevel)return;for(const t of p){if(!e())return;const r=null==(c=t.getType)?void 0:c.call(t);if("audio"!==r&&"video"!==r)continue;const i=null==(a=t.getBufferController)?void 0:a.call(t);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=g.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.length){if(!e()||(null==(s=n.getAcLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerPluginDashControl=t(),define(function(){return e.artplayerPluginDashControl})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginDashControl=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(t,n,o)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[n]=o,i=(e,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(e,l,i[l]);if(t)for(var l of t(i))o.call(i,l)&&r(e,l,i[l]);return e};function l(e,t=()=>!0){let n,o=!1;for(const i of e){if(!t())break;try{i()}catch(r){o||(o=!0,n=r)}}if(o)throw n}function c(e){const t=new Map;return e.filter(e=>{if(void 0===e.html)return!0;const n=t.get(e.html);return n?(e.default&&Object.assign(n,e),!1):(t.set(e.html,e),!0)})}function a(e,t,n){let o;const r=new Map;function i(n,o=r.get(n)){var i,l,c,a;if(!o||r.get(n)!==o)return;r.delete(n);const s=e[n];"controls"===n&&(null==(i=s.cache)?void 0:i.get)&&(null==(c=null==(l=s.cache.get(t))?void 0:l.option)?void 0:c.onSelect)!==o||"setting"===n&&s.find&&(null==(a=s.find(t))?void 0:a.onSelect)!==o||s.remove(t)}function c(){o=void 0,l([...r].map(([e,t])=>()=>i(e,t)))}return{update:function(l,a,s){if(!a)return void c();const u={};o=u;const f=()=>u===o&&s(),d=t=>f()?(a.select(t,f),f()?(e.notice.show=`${a.title}: ${t.html}`,f()&&l.control&&e.controls.check(t),f()&&l.setting&&e.setting.check(t),t.html):t.html):t.html;l.control?(r.set("controls",d),e.controls.update({name:t,position:"right",html:a.html,style:{padding:"0 10px"},selector:a.selector,onSelect:d})):i("controls"),f()&&(l.setting?(r.set("setting",d),e.setting.update({name:t,tooltip:a.html,html:a.title,icon:n,width:200,selector:a.selector,onSelect:d})):i("setting"))},clear:c,captureNavigation:function(n){var o;const i=e.setting,l=null==(o=i.find)?void 0:o.call(i,t),c=r.get("setting");if(n()&&c&&!0===i.show&&(null==l?void 0:l.onSelect)===c&&l.selector&&i.active===l.selector&&"function"===typeof i.render)return()=>{var o,l;if(!n()||e.setting!==i||!0!==i.show||!i.option||i.active!==i.option)return;const c=null==(o=i.find)?void 0:o.call(i,t),a=r.get("setting");n()&&a&&(null==c?void 0:c.onSelect)===a&&c.selector&&(null==(l=i.render)||l.call(i,c.selector))}}}}function s(e){let t;const n=n=>t===n&&e.active(n.dash),o=e=>e.dash.getSettings().streaming.abr.autoSwitchBitrate.video;function r(){const e=t;e&&(t=void 0,e.epoch++,l(e.callbacks.splice(0).map(([t,n])=>()=>e.off.call(e.dash,t,n))))}function i(n,o){if(t===n){try{r()}catch(i){console.warn("ArtPlayer DASH subscription cleanup failed:",i)}if(!t&&e.active(n.dash))try{e.reset()}catch(i){console.warn("ArtPlayer DASH cleanup failed:",i)}}console.warn("ArtPlayer DASH refresh failed:",o)}function c(t){if(!n(t)||t.suspended||t.queued||t.running)return;t.queued=!0;const r=t.epoch;Promise.resolve().then(()=>{if(n(t)&&t.epoch===r&&!t.suspended){t.queued=!1,t.running=!0;try{t.automatic=o(t),n(t)&&e.refresh()}catch(l){i(t,l)}finally{t.running=!1}}})}return{bind:function(l){const a=t;if((null==a?void 0:a.dash)===l){const e=o(a);return void(n(a)&&(a.automatic=e,a.suspended=!1))}if(r(),t||!e.active(l)||"function"!==typeof l.on||"function"!==typeof l.off)return;const s={dash:l,off:l.off,callbacks:[],epoch:0,queued:!1,running:!1,suspended:!1,automatic:!1};t=s;const u=l.on;try{s.automatic=o(s);const t=["qualityChangeRequested","qualityChangeRendered","trackChangeRendered","streamUpdated","streamInitialized"].map(e=>[e,()=>{"streamUpdated"!==e&&"streamInitialized"!==e||(s.suspended=!1),c(s)}]),r=function(e,t){const n=e;try{if("function"!==typeof n.getVersion||"4.5.2"!==n.getVersion())return;if("function"!==typeof n.getVideoElement||"function"!==typeof n.getActiveStream||"function"!==typeof n.getDashMetrics)return}catch(r){return}let o=!1;return()=>{var e,r,i,l,c,a,s,u,f;if(!o&&t()){o=!0;try{const o=null==(e=n.getVideoElement)?void 0:e.call(n);if(!(null==o?void 0:o.seeking)||!Number.isFinite(o.currentTime))return;const d=o.currentTime,g=null==(r=n.getActiveStream)?void 0:r.call(n),h=null==(i=n.getDashMetrics)?void 0:i.call(n),p=null==(l=null==g?void 0:g.getProcessors)?void 0:l.call(g);if(!Array.isArray(p)||"function"!==typeof(null==h?void 0:h.getCurrentBufferLevel)||"function"!==typeof h.addBufferLevel)return;for(const e of p){if(!t())return;const r=null==(c=e.getType)?void 0:c.call(e);if("audio"!==r&&"video"!==r)continue;const i=null==(a=e.getBufferController)?void 0:a.call(e);if("function"!==typeof(null==i?void 0:i.getRangeAt)||"function"!==typeof i.getIsPruningInProgress||"function"!==typeof i.getAllRangesWithSafetyFactor)continue;const l=h.getCurrentBufferLevel(r),p=i.getRangeAt(d),v=null===p||p&&Number.isFinite(p.end)&&p.end<=d;if(!Number.isFinite(l)||l<=0||!v||!1!==i.getIsPruningInProgress())continue;const y=i.getAllRangesWithSafetyFactor(d);if(Array.isArray(y)&&0===y.lengLine truncated
|
||||
+22
-42
@@ -5,6 +5,24 @@
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
const $audio = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>';
|
||||
function runCleanups(actions, current = () => true) {
|
||||
let failed = false;
|
||||
let failure;
|
||||
for (const action of actions) {
|
||||
if (!current())
|
||||
break;
|
||||
try {
|
||||
action();
|
||||
} catch (error) {
|
||||
if (!failed) {
|
||||
failed = true;
|
||||
failure = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
throw failure;
|
||||
}
|
||||
function qualityAdapter(dash) {
|
||||
const modern = typeof dash.getRepresentationsByType === "function";
|
||||
return {
|
||||
@@ -120,16 +138,7 @@ function createMenu(art, name, icon) {
|
||||
}
|
||||
function clear() {
|
||||
current = void 0;
|
||||
let failure;
|
||||
for (const [surface, callback] of [...owned]) {
|
||||
try {
|
||||
remove(surface, callback);
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups([...owned].map(([surface, callback]) => () => remove(surface, callback)));
|
||||
}
|
||||
function update(config, model, active) {
|
||||
if (!model) {
|
||||
@@ -248,16 +257,7 @@ function observeSDK(options) {
|
||||
return;
|
||||
binding = void 0;
|
||||
record.epoch++;
|
||||
let failure;
|
||||
for (const [name, callback] of record.callbacks.splice(0)) {
|
||||
try {
|
||||
record.off.call(record.dash, name, callback);
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups(record.callbacks.splice(0).map(([name, callback]) => () => record.off.call(record.dash, name, callback)));
|
||||
}
|
||||
function fail(record, error) {
|
||||
if (binding === record) {
|
||||
@@ -398,18 +398,7 @@ function artplayerPluginDashControl(option = {}) {
|
||||
}
|
||||
});
|
||||
function clear(current = () => true) {
|
||||
let failure;
|
||||
for (const cleanup of [quality.clear, audio.clear]) {
|
||||
if (!current())
|
||||
break;
|
||||
try {
|
||||
cleanup();
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups([quality.clear, audio.clear], current);
|
||||
}
|
||||
function update() {
|
||||
if (closed || art.isDestroy)
|
||||
@@ -455,17 +444,8 @@ function artplayerPluginDashControl(option = {}) {
|
||||
return;
|
||||
closed = true;
|
||||
revision++;
|
||||
let failure;
|
||||
const actions = [observer.release, clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))];
|
||||
for (const action of actions) {
|
||||
try {
|
||||
action();
|
||||
} catch (error) {
|
||||
failure || (failure = error);
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure;
|
||||
runCleanups(actions);
|
||||
}
|
||||
try {
|
||||
const entries = [["ready", update], ["restart", update], ["destroy", destroy]];
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Cleanup, Valid } from './types'
|
||||
|
||||
export function runCleanups(actions: Cleanup[], current: Valid = () => true): void {
|
||||
let failed = false
|
||||
let failure: unknown
|
||||
for (const action of actions) {
|
||||
if (!current())
|
||||
break
|
||||
try {
|
||||
action()
|
||||
}
|
||||
catch (error) {
|
||||
if (!failed) {
|
||||
failed = true
|
||||
failure = error
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
throw failure
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import type Artplayer from 'artplayer'
|
||||
import type { AudioTrack, Option, QualityLevel, Result } from '../types/artplayer-plugin-dash-control'
|
||||
import type { AudioItem, Cleanup, EventName, Host, QualityItem, Valid } from './types'
|
||||
import $audio from './audio.svg?raw'
|
||||
import { runCleanups } from './cleanup'
|
||||
import { audioModel, qualityModel } from './mapping'
|
||||
import { createMenu } from './menu'
|
||||
import $quality from './quality.svg?raw'
|
||||
@@ -39,19 +40,7 @@ export default function artplayerPluginDashControl<Level extends object = Qualit
|
||||
})
|
||||
|
||||
function clear(current: Valid = () => true): void {
|
||||
let failure: unknown
|
||||
for (const cleanup of [quality.clear, audio.clear]) {
|
||||
if (!current())
|
||||
break
|
||||
try {
|
||||
cleanup()
|
||||
}
|
||||
catch (error) {
|
||||
failure ||= error
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure
|
||||
runCleanups([quality.clear, audio.clear], current)
|
||||
}
|
||||
|
||||
function update(): void {
|
||||
@@ -101,18 +90,8 @@ export default function artplayerPluginDashControl<Level extends object = Qualit
|
||||
return
|
||||
closed = true
|
||||
revision++
|
||||
let failure: unknown
|
||||
const actions = [observer.release, clear, ...subscriptions.splice(0).map(([name, callback]) => () => art.off(name, callback))]
|
||||
for (const action of actions) {
|
||||
try {
|
||||
action()
|
||||
}
|
||||
catch (error) {
|
||||
failure ||= error
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure
|
||||
runCleanups(actions)
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Cleanup, DisplayConfig, Label, MenuHost, MenuModel, SelectorItem, Valid } from './types'
|
||||
import { runCleanups } from './cleanup'
|
||||
|
||||
export function createMenu<Item extends SelectorItem>(art: MenuHost, name: string, icon: string) {
|
||||
let current: object | undefined
|
||||
@@ -18,17 +19,7 @@ export function createMenu<Item extends SelectorItem>(art: MenuHost, name: strin
|
||||
|
||||
function clear(): void {
|
||||
current = undefined
|
||||
let failure: unknown
|
||||
for (const [surface, callback] of [...owned]) {
|
||||
try {
|
||||
remove(surface, callback)
|
||||
}
|
||||
catch (error) {
|
||||
failure ||= error
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure
|
||||
runCleanups([...owned].map(([surface, callback]) => () => remove(surface, callback)))
|
||||
}
|
||||
|
||||
function update(config: DisplayConfig, model: MenuModel<Item> | null, active: Valid): void {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Cleanup, Dash, SDKEventName } from './types'
|
||||
import { runCleanups } from './cleanup'
|
||||
import { createSeekRecovery } from './seek-buffer'
|
||||
|
||||
interface Binding<Level extends object, Track extends object> {
|
||||
@@ -27,17 +28,7 @@ export function observeSDK<Level extends object, Track extends object>(options:
|
||||
return
|
||||
binding = undefined
|
||||
record.epoch++
|
||||
let failure: unknown
|
||||
for (const [name, callback] of record.callbacks.splice(0)) {
|
||||
try {
|
||||
record.off.call(record.dash, name, callback)
|
||||
}
|
||||
catch (error) {
|
||||
failure ||= error
|
||||
}
|
||||
}
|
||||
if (failure)
|
||||
throw failure
|
||||
runCleanups(record.callbacks.splice(0).map(([name, callback]) => () => record.off.call(record.dash, name, callback)))
|
||||
}
|
||||
|
||||
function fail(record: Binding<Level, Track>, error: unknown): void {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,71 @@
|
||||
# PKG-DASH-CLEANUP-01 保留 DASH 清理的首个原始异常
|
||||
|
||||
来源 HEAD:`1c522f181be42f1c3fe5ce309cd42d85e58ac4dc`。
|
||||
对应 API-04 异常传播、API-05 生命周期和 API-07 控件集成。
|
||||
|
||||
## 缺陷、结构与兼容边界
|
||||
|
||||
原来四处清理循环使用 `failure ||= error` 记录失败,并用 `if (failure)` 判断
|
||||
是否抛出。用户通过公开 controls.update 注册的 beforeUnmount 抛出 undefined、
|
||||
null、false、0、-0、空字符串或 NaN 时,这些值被吞掉,或被后续清理的 Error
|
||||
覆盖。SDK off 的自定义包装抛出同类值也会受到影响。
|
||||
|
||||
新增私有 cleanup.ts,把菜单、SDK 订阅、整个插件的清理调度归到一个实现。
|
||||
独立 failed 标记与 unknown 原值分别表示“发生异常”和“异常是什么”;继续
|
||||
执行快照中其余清理,最后原样抛出第一个值。入口的 revision 检查继续在每个
|
||||
菜单清理动作之前执行;SDK 绑定和菜单所有权仍先失效,防止重入及迟到回调。
|
||||
八个模块的责任与维护入口在包内 ARCHITECTURE.md 同步说明。
|
||||
|
||||
不改公开工厂、同步 update、选项、DOM/CSS、类型及分发路径;没有新运行时依赖、
|
||||
版本或锁文件变化。SDK 仍由调用方所有。异步 SDK 刷新失败继续清理并 warn;
|
||||
同步 update/destroy 才沿原调用栈传播错误,未将其改成 Promise。
|
||||
|
||||
核心 beforeUnmount 自身的异常可阻止该控件删除。插件会清理其他菜单并传播
|
||||
原值,不绕过核心回调删除这个控件,也不承诺出错后整个 ArtPlayer.destroy
|
||||
仍然完成。重复插件销毁、失效回调和剩余监听器释放分别由回归覆盖。
|
||||
|
||||
## 复现与验证
|
||||
|
||||
新增六项源码回归在未修复候选上全部失败;修复后每项实际遍历七种值,用
|
||||
独立 threw 标记和 Object.is 检查,区分未抛出/throw undefined、0/-0、NaN。
|
||||
覆盖 update、destroy、SDK off、后续错误覆盖、兄弟资源释放和重复销毁。
|
||||
|
||||
浏览器首轮四项失败来自测试输入复用旧 selector 对象:核心已经在其上定义
|
||||
不可重新定义的 $control_option。修正测试为传入新的 selector 对象后,仍使用
|
||||
公开 controls.update 和 beforeUnmount;这次在未修复产物上四项 Chromium
|
||||
回归均因异常被吞掉而失败。两份失败报告分别保留,首轮不计产品缺陷复现。
|
||||
|
||||
源码专项 167 项通过;加载仓库外实际安装并保留的 main/legacy/module 三种
|
||||
产物后,加上源码及历史对照共 470 项通过。不是新增了 470 个独立回归。
|
||||
严格包类型、定向 lint、三格式正常构建及严格工具链检查通过。
|
||||
|
||||
Yarn Classic 1.22.22 / Node 24.21.0;Yarn pack 后在仓库外离线安装,再冻结锁
|
||||
重装并逐文件核对。5 组 TS 5.9.3/4.3.5 模块配置的正例零诊断,每组拒绝
|
||||
8 项非法调用,共 40 个预期错误。脚本保留核对后的安装副本,支持随后直接
|
||||
验证该份文件,而不重新构建冒充安装产物。
|
||||
|
||||
浏览器采用 Windows 上 Playwright 管理的 Chromium 153.0.8010.12、Firefox
|
||||
155.0 和 WebKit 26.6。结果如下;没有用 Chrome 扩展连接或模拟 SDK 代替实播。
|
||||
|
||||
| 输入与范围 | 通过 | 跳过 |
|
||||
| --- | --- | --- |
|
||||
| 源码,新增真实核心回调回归 | 12 | 0 |
|
||||
| 安装 main,完整受控 SDK/DOM/原生 MP4 套件 | 96 | 0 |
|
||||
| 安装 legacy,同套件的候选插件场景 | 72 | 0 |
|
||||
| 安装 main,真实 SDK 解码/UI、换源清理、异步错误恢复 | 24 | 12 |
|
||||
| 安装 legacy,同组真实 SDK 场景 | 24 | 12 |
|
||||
|
||||
合计 228 项通过、24 项能力跳过,没有失败或重试;main 中额外的发布旧插件
|
||||
对照不在 legacy 轮重复执行。三格式 dist、docs/compiled 与安装副本逐字节一致,
|
||||
安装包 ARCHITECTURE.md 与当前包内文档也一致。根 test:dash-control/test:unit
|
||||
已纳入新增回归。本轮浏览器和分发核对结果见
|
||||
[机器证据](../baselines/dash-cleanup-errors-validation.json)。
|
||||
真实 SDK 播放与受控 SDK 的 DOM 测试分开记录;Windows WebKit 缺失 MSE 的
|
||||
真实 MPD 场景不能算播放通过。物理设备、完整安装/SDK 矩阵和发布验收仍由
|
||||
PKG-DASH-05/06 负责。本轮没有推送、部署或发布。
|
||||
|
||||
## 回退与交付
|
||||
|
||||
本任务单独提交实现、测试、打包产物、文档、风险和任务状态。回退该提交会
|
||||
恢复四个有缺陷的清理循环,并撤销本次回归和安装副本保留能力;不会撤销
|
||||
此前 TS 迁移、SDK 自动刷新、菜单导航或 SDK 4.5.2 seek 恢复。
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
测试保护真实 npm 1.1.0 的既有调用契约,以及重构前工作区的 SDK 5.x 稳定 ID 修正。
|
||||
版本来源见 [发布契约](baselines/dash-control-contract.md)。02 建立基线,03 改造运行行为,
|
||||
04 已把五个自有模块迁移为严格 TS 并补齐公开类型与编辑器生成;05 新增第六个 SDK 事件模块。
|
||||
04 把五个自有模块迁移为严格 TS 并补齐公开类型与编辑器生成;后续增加 SDK 事件、
|
||||
4.5.2 seek 恢复和清理调度,当前八个自有模块均为严格 TS。
|
||||
实际模块地图与生命周期规则见 [包内架构](../packages/artplayer-plugin-dash-control/ARCHITECTURE.md)。
|
||||
|
||||
## 可重跑的测试
|
||||
|
||||
- `yarn test:dash-control`:原有 118 项与两代 SDK 各 13 项事件/清理/重入回归,共 144 项。
|
||||
- `yarn test:dash-control`:当前 167 项,覆盖历史契约、两代 SDK、事件/清理/重入及原始异常。
|
||||
该脚本和 `test:unit` 都保留旧测试,完整 CI 自动执行。
|
||||
- `yarn test:browser test/browser/dash-control.spec.js --trace on`:84 项,两个核心、发布插件 SDK 4/
|
||||
- `yarn test:browser test/browser/dash-control.spec.js --trace on`:96 项,两个核心、发布插件 SDK 4/
|
||||
候选插件 SDK 4/5、两类菜单、两种选择及候选清理、Chromium/Firefox/WebKit。
|
||||
浏览器套件串行运行,避免共享端口和报告覆盖。
|
||||
- `yarn ci:check`:工具链、计划、lint、声明漂移、严格类型和 Node/基线测试。
|
||||
@@ -19,6 +20,8 @@
|
||||
TS 4.3.5/5.9.3、五种模块模式、八条非法用法及实际 npm 旧声明的消费者对照。
|
||||
- `yarn test:dash-types-package`:Yarn 打包核心/DASH、工作区外离线安装与冻结锁重装,
|
||||
检查五种模式、CommonJS callable/命名空间、每模式八条非法调用,并拒绝解析回工作区。
|
||||
报告中的 `installedArtifacts` 保留已逐文件核对 tarball 哈希的安装副本,供后续
|
||||
`ARTPLAYER_TEST_DASH` 和 `ARTPLAYER_DASH_ARTIFACT` 运行时验证。
|
||||
- `yarn test:browser test/browser/dash-sdk.spec.js --trace on`:真实 npm dash.js 4.5.2/5.2.1,
|
||||
本地多画质/多音轨 MPD,新旧核心/插件、外部同步 update、自动事件刷新、同 SDK 换源、
|
||||
formatter 错误恢复、实际设置点击和 SDK 原生对照。目前完整定义 114 项。
|
||||
|
||||
+6
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
> 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 268 项,范围 22 个包及工作区/示例。
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 269 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 43 / doing 21 / blocked 0 / done 204 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 43 / doing 21 / blocked 0 / done 205 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
| artplayer-plugin-chromecast | 1.1.0 | PKG-CAST-01, PKG-CAST-02, PKG-CAST-03, PKG-CAST-04, PKG-CAST-05, PKG-CAST-06 |
|
||||
| artplayer-plugin-danmuku | 5.3.0 | PKG-DANMUKU-01, PKG-DANMUKU-02, PKG-DANMUKU-03, PKG-DANMUKU-04, PKG-DANMUKU-05, PKG-DANMUKU-06, PKG-DANMUKU-07, PKG-DANMUKU-08, PKG-DANMUKU-09, PKG-DANMUKU-10, PKG-DANMUKU-11, PKG-DANMUKU-12, PKG-DANMUKU-MASK-LOAD-01 |
|
||||
| artplayer-plugin-danmuku-mask | 1.1.0 | PKG-MASK-01, PKG-MASK-02, PKG-MASK-03, PKG-MASK-04, PKG-MASK-05, PKG-MASK-06, ENG-LINT-01, PKG-DANMUKU-MASK-LOAD-01 |
|
||||
| artplayer-plugin-dash-control | 1.1.0 | PKG-DASH-01, PKG-DASH-02, PKG-DASH-03, PKG-DASH-04, PKG-DASH-SEEK-01, PKG-DASH-MENU-01, PKG-DASH-05, PKG-DASH-06 |
|
||||
| artplayer-plugin-dash-control | 1.1.0 | PKG-DASH-01, PKG-DASH-02, PKG-DASH-03, PKG-DASH-04, PKG-DASH-SEEK-01, PKG-DASH-MENU-01, PKG-DASH-05, PKG-DASH-06, PKG-DASH-CLEANUP-01 |
|
||||
| artplayer-plugin-document-pip | 1.1.0 | PKG-DPIP-01, PKG-DPIP-02, PKG-DPIP-03, PKG-DPIP-04, PKG-DPIP-05, PKG-DPIP-06 |
|
||||
| artplayer-plugin-hls-control | 1.1.0 | PKG-HLS-01, PKG-HLS-02, PKG-HLS-03, PKG-HLS-04, PKG-HLS-SDK-01, PKG-HLS-05, PKG-HLS-06, PKG-HLS-PACK-01 |
|
||||
| artplayer-plugin-jassub | 1.1.0 | PKG-JASSUB-01, PKG-JASSUB-02, PKG-JASSUB-03, PKG-JASSUB-04, PKG-JASSUB-05, PKG-JASSUB-06, PKG-JASSUB-07, PKG-JASSUB-08, PKG-JASSUB-09, PKG-JASSUB-10 |
|
||||
@@ -222,8 +222,9 @@
|
||||
| PKG-DASH-04 | artplayer-plugin-dash-control<br>迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
|
||||
| PKG-DASH-SEEK-01 | artplayer-plugin-dash-control<br>修复 SDK 4.5.2 暂停跳转后的缓冲指标停滞 | PKG-DASH-04 | 按版本和实测空缓冲限定的指标同步、生命周期守卫及真实 SDK 旧红新绿 | 新插件在新旧核心及 Chromium/Firefox 原位置回归通过;不修改 SDK 方法/配置、媒体时间或删除缓冲;旧 SDK 原生缺陷与旧插件限制独立记录,不宣称第三方本体已修复 | H | done |
|
||||
| PKG-DASH-MENU-01 | artplayer-plugin-dash-control<br>定位并修复 SDK 刷新期间设置选项不可见 | PKG-DASH-SEEK-01 | 真实 SDK 菜单点击失败根因、对应兼容修复及新旧核心回归 | 保留 legacy/旧核心/SDK5.2.1 的原失败;确定菜单在点击过程中不可见的原因;不靠 force 点击、扩大超时或重复运行覆盖问题;main/legacy 质量与音轨设置及 SDK 刷新组合通过 | H | done |
|
||||
| PKG-DASH-05 | artplayer-plugin-dash-control<br>验证新旧核心和组合 | PKG-DASH-04, CORE-22, PKG-DASH-SEEK-01 | 固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
|
||||
| PKG-DASH-05 | artplayer-plugin-dash-control<br>验证新旧核心和组合 | PKG-DASH-04, CORE-22, PKG-DASH-SEEK-01, PKG-DASH-CLEANUP-01 | 固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
|
||||
| PKG-DASH-06 | artplayer-plugin-dash-control<br>验证分发并同步文档 | PKG-DASH-05, ENG-07, PKG-DASH-MENU-01 | dash.control.js、支持的 dash.js 版本与产物 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
|
||||
| PKG-DASH-CLEANUP-01 | artplayer-plugin-dash-control<br>保留菜单和SDK清理的首个原始异常 | PKG-DASH-04 | 集中DASH清理调度与首错传播,保留重入保护;falsy异常、真实控件beforeUnmount、分发及文档验证 | 七种falsy值不被吞掉或后续错误覆盖;后续资源释放和生命周期回归通过,不更改公开API、SDK归属或异步刷新边界 | M | done |
|
||||
|
||||
## 5 包迁移:artplayer-plugin-multiple-subtitles
|
||||
|
||||
@@ -702,3 +703,4 @@
|
||||
- SITE-CONSOLE-01: [记录](console-modernization.md) [记录](changes/2026-09-15-SITE-CONSOLE-01-typescript.md) [记录](baselines/site-console01-validation.json)
|
||||
- PKG-AUTO-THUMB-12: [记录](changes/2026-09-15-PKG-AUTO-THUMB-12-metadata.md) [记录](baselines/auto-thumbnail-metadata-validation.json)
|
||||
- PKG-TOOL-THUMB-07: [记录](changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md) [记录](baselines/thumbnail-cleanup-errors-validation.json)
|
||||
- PKG-DASH-CLEANUP-01: [记录](changes/2026-09-15-PKG-DASH-CLEANUP-01-errors.md) [记录](baselines/dash-cleanup-errors-validation.json)
|
||||
@@ -1,5 +1,19 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-DASH-CLEANUP-01 清理异常与职责归一
|
||||
|
||||
四处清理循环改为私有 cleanup.ts,保留首个原始异常值、剩余资源释放和重入保护。
|
||||
未修复源码6失败,修正测试输入后的未修复产物4项Chromium失败;首轮输入复用
|
||||
导致的4项setup失败单独保留。修复后源码167通过,安装三格式与历史/源码共470通过。
|
||||
|
||||
source: 12通过/0跳过;main: 96通过/0跳过;legacy: 72通过/0跳过;sdk-main: 24通过/12跳过;sdk-legacy: 24通过/12跳过。
|
||||
受控SDK的DOM验证与真实dash.js 4.5.2/5.2.1播放/换源/异步恢复分开记录;
|
||||
Windows WebKit MSE缺失的跳过不计实际播放。5组隔离类型正例和40预期负例成立,
|
||||
三格式、文档副本与安装文件字节一致。详见[记录](changes/2026-09-15-PKG-DASH-CLEANUP-01-errors.md)和[证据](baselines/dash-cleanup-errors-validation.json)。
|
||||
|
||||
任务完成,DASH-05继续doing;205 done、21 doing、43 todo,共269项。
|
||||
无新依赖、公开类型、版本或锁文件修改;本轮没有推送、部署或发布。
|
||||
|
||||
## PKG-TOOL-THUMB-07 清理异常传播修复
|
||||
|
||||
修复cleanupAll吞掉destroy监听器抛出的falsy值、或被后续异常覆盖的兼容回归。
|
||||
|
||||
@@ -282,3 +282,4 @@
|
||||
| AUTO-THUMB-METADATA-01 | resolved / 已复现 | Private metadata loading can retain its decoder indefinitely without metadata or media errors | PKG-AUTO-THUMB-12 |
|
||||
| ASR-MEDIA-01 | resolved / 已复现 | ASR accepts obsolete recognition after native media error without pause | PKG-ASR-10, PKG-ASR-05 |
|
||||
| THUMB-CLEANUP-01 | resolved / 已复现 | Thumbnail 清理吞掉 falsy 监听器异常或覆盖第一个异常值 | PKG-TOOL-THUMB-07, PKG-TOOL-THUMB-04 |
|
||||
| DASH-CLEANUP-01 | resolved / 已复现 | DASH 菜单和 SDK 清理吞掉 falsy 异常或丢失首个错误 | PKG-DASH-CLEANUP-01, PKG-DASH-05 |
|
||||
@@ -6629,6 +6629,30 @@
|
||||
"refactor/baselines/thumbnail-cleanup-errors-validation.json"
|
||||
],
|
||||
"resolutionRationale": "Frozen historical destroy contracts preserve all seven falsy values; the prepatch candidate fails exception propagation and first-value ownership. Fixed source and rebuilt main/legacy pass cleanup/resource and actual browser DOM regressions. Installed artifact and type checks pass; historical defaults and WebKit media capability gaps remain outside this narrowly resolved regression."
|
||||
},
|
||||
{
|
||||
"id": "DASH-CLEANUP-01",
|
||||
"title": "DASH 菜单和 SDK 清理吞掉 falsy 异常或丢失首个错误",
|
||||
"confirmation": "reproduced",
|
||||
"status": "resolved",
|
||||
"owners": [
|
||||
"PKG-DASH-CLEANUP-01",
|
||||
"PKG-DASH-05"
|
||||
],
|
||||
"evidence": [
|
||||
"test/dash-cleanup-errors.test.js",
|
||||
"test/browser/dash-control.spec.js",
|
||||
"refactor/changes/2026-09-15-PKG-DASH-CLEANUP-01-errors.md",
|
||||
"refactor/baselines/dash-cleanup-errors-validation.json"
|
||||
],
|
||||
"compatibleResolution": "共用独立失败标记的清理调度,保存并传播第一个原始值,同时完成剩余资源释放,保留重入/SDK归属及异步警告策略。",
|
||||
"closureCriteria": "七种falsy值首错身份、兄弟资源释放、重入/销毁专项、真实控件回调、三格式安装和真实SDK回归有证据;设备能力缺口单独保留。",
|
||||
"workspaceState": "Prepatch: six source failures and four valid Chromium failures; fixed source 167 pass, installed three formats plus source/historical 470 pass. Browser reports distinguish controlled DOM, actual SDK and Windows WebKit MSE skips.",
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-15-PKG-DASH-CLEANUP-01-errors.md",
|
||||
"refactor/baselines/dash-cleanup-errors-validation.json"
|
||||
],
|
||||
"resolutionRationale": "Corrected public beforeUnmount input reproduces swallowed values against the prepatch artifact. Separate threw and Object.is assertions cover all seven values. Rebuilt and independently installed artifacts pass lifecycle/DOM/SDK regressions; public declarations are unchanged. This resolves the cleanup defect only, not full package device/release acceptance."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,11 @@ async function main() {
|
||||
assert.equal(fs.realpathSync(root), root, 'Installed package is a workspace link')
|
||||
for (const [member, expected] of Object.entries(pkg.files))
|
||||
assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`)
|
||||
const retained = path.join(output, 'installed-artifacts', pkg.name)
|
||||
fs.cpSync(root, retained, { recursive: true })
|
||||
for (const [member, expected] of Object.entries(pkg.files))
|
||||
assert.equal(hash(fs.readFileSync(path.join(retained, member.slice(8)))), expected, `Retained installed bytes differ: ${member}`)
|
||||
pkg.installedArtifacts = retained
|
||||
}
|
||||
const matrix = []
|
||||
for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]) {
|
||||
|
||||
+21
-1
@@ -2328,7 +2328,8 @@
|
||||
"dependsOn": [
|
||||
"PKG-DASH-04",
|
||||
"CORE-22",
|
||||
"PKG-DASH-SEEK-01"
|
||||
"PKG-DASH-SEEK-01",
|
||||
"PKG-DASH-CLEANUP-01"
|
||||
],
|
||||
"status": "doing",
|
||||
"risk": "H",
|
||||
@@ -5766,6 +5767,25 @@
|
||||
"changes/2026-09-15-PKG-TOOL-THUMB-07-cleanup-errors.md",
|
||||
"baselines/thumbnail-cleanup-errors-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-DASH-CLEANUP-01",
|
||||
"phase": "5 包迁移:artplayer-plugin-dash-control",
|
||||
"title": "保留菜单和SDK清理的首个原始异常",
|
||||
"scope": [
|
||||
"artplayer-plugin-dash-control"
|
||||
],
|
||||
"dependsOn": [
|
||||
"PKG-DASH-04"
|
||||
],
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "集中DASH清理调度与首错传播,保留重入保护;falsy异常、真实控件beforeUnmount、分发及文档验证",
|
||||
"acceptance": "七种falsy值不被吞掉或后续错误覆盖;后续资源释放和生命周期回归通过,不更改公开API、SDK归属或异步刷新边界",
|
||||
"evidence": [
|
||||
"changes/2026-09-15-PKG-DASH-CLEANUP-01-errors.md",
|
||||
"baselines/dash-cleanup-errors-validation.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -99,6 +99,65 @@ async function openDash(page, core, plugin, sdk, testInfo) {
|
||||
await expect(page.locator('.art-control-dash-audio .art-selector-value')).toHaveText('en')
|
||||
}
|
||||
for (const core of ['published', 'candidate']) {
|
||||
for (const sdk of [4, 5]) {
|
||||
test(`${core} core + candidate DASH SDK ${sdk}: falsy unmount errors preserve identity and release sibling menus`, async ({ page }, testInfo) => {
|
||||
await openDash(page, core, 'candidate', sdk, testInfo)
|
||||
const result = await page.evaluate((sdk) => {
|
||||
const art = window.art
|
||||
const plugin = art.plugins.artplayerPluginDashControl
|
||||
const method = sdk === 4 ? 'getBitrateInfoListFor' : 'getRepresentationsByType'
|
||||
const original = art.dash[method]
|
||||
const results = []
|
||||
for (const failure of [undefined, null, false, 0, -0, '', Number.NaN]) {
|
||||
let armed = false
|
||||
art.controls.update({ name: 'dash-quality', selector: art.controls.cache.get('dash-quality').option.selector.map(item => ({ ...item })), beforeUnmount() {
|
||||
if (armed)
|
||||
throw failure
|
||||
} })
|
||||
armed = true
|
||||
art.dash[method] = () => []
|
||||
let threw = false
|
||||
let same = false
|
||||
try {
|
||||
plugin.update()
|
||||
}
|
||||
catch (error) {
|
||||
threw = true
|
||||
same = Object.is(error, failure)
|
||||
}
|
||||
results.push({ threw, same, audioRemoved: !art.controls['dash-audio'], settingsRemoved: !art.setting.find('dash-quality') && !art.setting.find('dash-audio') })
|
||||
armed = false
|
||||
art.dash[method] = original
|
||||
if (art.controls['dash-quality'])
|
||||
art.controls.remove('dash-quality')
|
||||
plugin.update()
|
||||
}
|
||||
let armed = false
|
||||
art.controls.update({ name: 'dash-quality', selector: art.controls.cache.get('dash-quality').option.selector.map(item => ({ ...item })), beforeUnmount() {
|
||||
if (armed)
|
||||
// eslint-disable-next-line no-throw-literal -- Verify the public callback's primitive exception identity.
|
||||
throw 0
|
||||
} })
|
||||
armed = true
|
||||
let destroyThrew = false
|
||||
let destroySame = false
|
||||
try {
|
||||
art.destroy(false)
|
||||
}
|
||||
catch (error) {
|
||||
destroyThrew = true
|
||||
destroySame = Object.is(error, 0)
|
||||
}
|
||||
const siblingRemoved = !art.controls['dash-audio']
|
||||
armed = false
|
||||
if (art.controls['dash-quality'])
|
||||
art.controls.remove('dash-quality')
|
||||
return { results, destroyThrew, destroySame, siblingRemoved }
|
||||
}, sdk)
|
||||
await testInfo.attach('dash-cleanup-errors', { contentType: 'application/json', body: JSON.stringify({ result, scope: 'Real core beforeUnmount callbacks and native DOM; controlled SDK topology, no adaptive playback claim.' }) })
|
||||
expect(result).toEqual({ results: Array.from({ length: 7 }, () => ({ threw: true, same: true, audioRemoved: true, settingsRemoved: true })), destroyThrew: true, destroySame: true, siblingRemoved: true })
|
||||
})
|
||||
}
|
||||
for (const [plugin, sdk] of [['published', 4], ['candidate', 4], ['candidate', 5]]) {
|
||||
for (const surface of ['control', 'setting']) {
|
||||
for (const type of ['quality', 'audio']) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Cleanup exception identity uses the Node lifecycle runner.
|
||||
import test from 'node:test'
|
||||
import { bothMenus, dashHost, dashImplementations } from './helpers/dash-control.js'
|
||||
|
||||
const implementations = (await dashImplementations()).filter(item => item.name.startsWith('source') || item.name.startsWith('artifact'))
|
||||
const failures = [undefined, null, false, 0, -0, '', Number.NaN]
|
||||
function attempt(callback) {
|
||||
try {
|
||||
callback()
|
||||
return { threw: false }
|
||||
}
|
||||
catch (error) {
|
||||
return { threw: true, error }
|
||||
}
|
||||
}
|
||||
|
||||
for (const { sdk, name, factory } of implementations) {
|
||||
for (const trigger of ['update', 'destroy']) {
|
||||
test(`DASH ${sdk} (${name}): ${trigger} preserves the first falsy menu cleanup failure`, (t) => {
|
||||
const warnings = []
|
||||
t.mock.method(console, 'warn', (...args) => warnings.push(args))
|
||||
for (const failure of failures) {
|
||||
const host = dashHost(sdk)
|
||||
const plugin = factory(bothMenus())(host.art)
|
||||
plugin.update()
|
||||
const calls = []
|
||||
for (const surface of ['controls', 'setting']) {
|
||||
const remove = host.art[surface].remove
|
||||
host.art[surface].remove = (key) => {
|
||||
remove(key)
|
||||
calls.push([surface, key])
|
||||
if (surface === 'controls' && key === 'dash-quality')
|
||||
throw failure
|
||||
throw new Error(`later ${surface}/${key} failure`)
|
||||
}
|
||||
}
|
||||
if (trigger === 'update') {
|
||||
host.state.levels = []
|
||||
host.state.tracks = []
|
||||
}
|
||||
const result = attempt(() => trigger === 'update' ? plugin.update() : host.art.destroy())
|
||||
assert.equal(result.threw, true)
|
||||
assert(Object.is(result.error, failure), 'Later menu cleanup errors cannot replace the first value')
|
||||
assert.deepEqual(calls, [['controls', 'dash-quality'], ['setting', 'dash-quality'], ['controls', 'dash-audio'], ['setting', 'dash-audio']])
|
||||
assert.equal(host.controls.size + host.settings.size, 0)
|
||||
if (trigger === 'destroy') {
|
||||
assert.equal([...host.listeners.values()].reduce((sum, list) => sum + list.size, 0), 0)
|
||||
assert.doesNotThrow(() => host.art.destroy())
|
||||
}
|
||||
}
|
||||
if (trigger === 'update') {
|
||||
assert.equal(warnings.length, failures.length)
|
||||
assert(warnings.every(([message, error]) => message === 'ArtPlayer DASH cleanup failed:' && error instanceof Error))
|
||||
}
|
||||
else {
|
||||
assert.equal(warnings.length, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
test(`DASH ${sdk} (${name}): SDK unsubscribe preserves a falsy first failure while releasing all resources`, () => {
|
||||
for (const failure of failures) {
|
||||
const host = dashHost(sdk)
|
||||
const events = new Map()
|
||||
host.dash.on = (name, callback) => events.set(name, callback)
|
||||
let offCalls = 0
|
||||
host.dash.off = (name) => {
|
||||
events.delete(name)
|
||||
if (++offCalls === 1)
|
||||
throw failure
|
||||
throw new Error('later SDK removal failure')
|
||||
}
|
||||
factory(bothMenus())(host.art).update()
|
||||
const count = events.size
|
||||
assert(count > 0)
|
||||
const result = attempt(() => host.art.destroy())
|
||||
assert.equal(result.threw, true)
|
||||
assert(Object.is(result.error, failure))
|
||||
assert.equal(offCalls, count)
|
||||
assert.equal(events.size, 0)
|
||||
assert.equal(host.controls.size + host.settings.size, 0)
|
||||
assert.equal([...host.listeners.values()].reduce((sum, list) => sum + list.size, 0), 0)
|
||||
assert(!host.calls.some(call => call[0] === 'dash.destroy'), 'SDK remains caller-owned')
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user