mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-11 05:26:15 -08:00
refactor(mediabunny): [PKG-MB-04] checkpoint typed shim and event cleanup
This commit is contained in:
1 parent
adba8a3d12
commit
9af253a5a9
24 files changed
+1478
-250
No files matched your search
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";function e(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class t{constructor(){this.listeners=new Map}addEventListener(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(e,t){const i=new Event(e);i.detail=t;const r=this.listeners.get(e);r&&r.forEach(e=>e(i))}}function i(e){if(!e)throw new Error("Assertion failed.")}const r=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},a=e=>e&&e[e.length-1],s=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},n=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-2**r)},o=e=>{const t=s(e);return 0===(1&t)?-(t>>1):t+1>>1},c=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),l=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),u=new TextDecoder,d=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),h={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},f=d(h),m={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},p=d(m),g={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},k=d(g),y=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,b={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},w=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class T{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}async acquire(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,await i,e}}const S=/^[0-9a-fA-F]+$/,v=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),P=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,I=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+a>>1,o=i(e[n]);o===t?(s=n,a=n-1):o<t?r=n+1:a=n-1}return s},x=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+(a-r+1)/2|0;i(e[n])<=t?(s=n,r=n+1):a=n-1}return s},C=(e,t,i)=>{const r=x(e,i(t),i);e.splice(r+1,0,t)},B=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},E=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},_=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},R=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},F=e=>{throw new Error(`Unexpected value: ${e}`)},A=(e,t,i)=>{const r=e.getUint8(t),a=e.getUint8(t+1),s=e.getUint8(t+2);return i?r|a<<8|s<<16:r<<16|a<<8|s},D=(e,t,i,r)=>{i>>>=0,i&=16777215,e.setUint8(t,i>>>16&255),e.setUint8(t+1,i>>>8&255),e.setUint8(t+2,255&i)},M=(e,t)=>({async next(){const i=await e.next();return i.done?{value:void 0,done:!0}:{value:t(i.value),done:!1}},return:()=>e.return(),throw:t=>e.throw(t),[Symbol.asyncIterator](){return this}}),N=(e,t,i)=>Math.max(t,Math.min(i,e)),O="und",L=e=>{const t=Math.round(e);return Math.abs(e/t-1)<10*Number.EPSILON?t:e},z=(e,t)=>Math.round(e/t)*t,U=(e,t)=>Math.round(e*t)/t,q=(e,t)=>Math.floor(e/t)*t,W=e=>{let t=0;for(;0!==e;)e&=e-1,t++;return t},H=/^[a-z]{3}$/,V=e=>H.test(e),j=1e6*(1+Number.EPSILON),K=(e,t)=>{const i={...e,...t};if(e.headers||t.headers){const r=e.headers?X(e.headers):{},a=t.headers?X(t.headers):{},s={...r};Object.entries(a).forEach(([e,t])=>{const i=Object.keys(s).find(t=>t.toLowerCase()===e.toLowerCase());i&&delete s[i],s[e]=t}),i.headers=s}return i},X=e=>{if(e instanceof Headers){const t={};return e.forEach((e,i)=>{t[i]=e}),t}if(Array.isArray(e)){const t={};return e.forEach(([e,i])=>{t[e]=i}),t}return e},$=async(e,t,i,r,a)=>{let s=0;for(;;)try{return await e(t,i)}catch(n){if(a())throw n;s++;const e=r(s,n,t);if(null===e)throw n;if(be._error("Retrying failed fetch. Error:",n),!Number.isFinite(e)||e<0)throw new TypeError("Retry delay must be a non-negative finite number.");if(e>0&&await pe(1e3*e),a())throw n}};class G{constructor(){this.currentPromise=Promise.resolve()}call(e){return this.currentPromise=this.currentPromise.then(e)}}let Q=null;const Y=()=>null!==Q?Q:Q=!("undefined"===typeof navigator||!(navigator.vendor?.match(/apple/i)||/AppleWebKit/.test(navigator.userAgent)&&!/Chrome/.test(navigator.userAgent)||/\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent)));let J=null;coLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e,t=e=>{throw TypeError(e)},i=(e,i,r)=>i.has(e)||t("Cannot "+r),r=(e,t,r)=>(i(e,t,"read from private field"),r?r.call(e):t.get(e));function a(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class s{constructor(){var i,r,a;i=this,a=!1,(r=e).has(i)?t("Cannot add the same private member more than once"):r instanceof WeakSet?r.add(i):r.set(i,a),this.listeners=new Map}addEventListener(t,i){r(this,e)||(this.listeners.has(t)||this.listeners.set(t,[]),this.listeners.get(t)?.push(i))}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(t,i){if(r(this,e))return;const a=Object.assign(new Event(t),{detail:i});this.listeners.get(t)?.forEach(t=>{r(this,e)||t(a)})}destroy(){var t,r,a,s;a=!0,i(t=this,r=e,"write to private field"),s?s.call(t,a):r.set(t,a),this.listeners.clear()}}function n(e){if(!e)throw new Error("Assertion failed.")}e=new WeakMap;const o=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},c=e=>e&&e[e.length-1],l=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},u=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-2**r)},d=e=>{const t=l(e);return 0===(1&t)?-(t>>1):t+1>>1},h=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),f=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),m=new TextDecoder,p=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),g={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},k=p(g),y={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},b=p(y),w={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},T=p(w),S=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,v={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},P=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class I{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}async acquire(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,await i,e}}const x=/^[0-9a-fA-F]+$/,C=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),B=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,E=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+a>>1,o=i(e[n]);o===t?(s=n,a=n-1):o<t?r=n+1:a=n-1}return s},_=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+(a-r+1)/2|0;i(e[n])<=t?(s=n,r=n+1):a=n-1}return s},R=(e,t,i)=>{const r=_(e,i(t),i);e.splice(r+1,0,t)},F=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},A=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},D=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},M=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},N=e=>{throw new Error(`Unexpected value: ${e}`)},O=(e,t,i)=>{const r=e.getUint8(t),a=e.getUint8(t+1),s=e.getUint8(t+2);return i?r|a<<8|s<<16:r<<16|a<<8|s},L=(e,t,i,r)=>{i>>>=0,i&=16777215,e.setUint8(t,i>>>16&255),e.setUint8(t+1,i>>>8&255),e.setUint8(t+2,255&i)},z=(e,t)=>({async next(){const i=await e.next();return i.done?{value:void 0,done:!0}:{value:t(i.value),done:!1}},return:()=>e.return(),throw:t=>e.throw(t),[Symbol.asyncIterator](){return this}}),U=(e,t,i)=>Math.max(t,Math.min(i,e)),q="und",W=e=>{const t=Math.round(e);return Math.abs(e/t-1)<10*Number.EPSILON?t:e},H=(e,t)=>Math.round(e/t)*t,V=(e,t)=>Math.round(e*t)/t,j=(e,t)=>Math.floor(e/t)*t,K=e=>{let t=0;for(;0!==e;)e&=e-1,t++;return t},X=/^[a-z]{3}$/,$=e=>X.test(e),G=1e6*(1+Number.EPSILON),Q=(e,t)=>{const i={...e,...t};if(e.headers||t.headers){const r=e.headers?Y(e.headers):{},a=t.headers?Y(t.headers):{},s={...r};Object.entries(a).forEach(([e,t])=>{const i=Object.keys(s).find(t=>t.toLowerCase()===e.toLowerCase());i&&delete s[i],s[e]=t}),i.headers=s}return i},Y=e=>{if(e instanceof Headers){const t={};return e.forEach((e,i)=>{t[i]=e}),t}if(Array.isArray(e)){const t={};return e.forEach(([e,i])=>{t[e]=i}),t}return e},J=async(e,t,i,r,a)=>{let s=0;for(;;)try{return await e(t,i)}catch(n){if(a())throw n;s++;const e=r(s,n,t);if(null===e)throw n;if(ve._error("Retrying failed fetch. Error:",n),!Number.isFinite(e)||e<0)throw new TypeError("Retry delay must be aLine truncated
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,i=Object.getOwnPropertyDescriptors,r=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),o=Math.pow,l=(t,i,r)=>i in t?e(t,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[i]=r,c=(e,t)=>{for(var i in t||(t={}))n.call(t,i)&&l(e,i,t[i]);if(r)for(var i of r(t))s.call(t,i)&&l(e,i,t[i]);return e},u=(e,r)=>t(e,i(r)),d=(e,t,i)=>new Promise((r,n)=>{var s=e=>{try{o(i.next(e))}catch(t){n(t)}},a=e=>{try{o(i.throw(e))}catch(t){n(t)}},o=e=>e.done?r(e.value):Promise.resolve(e.value).then(s,a);o((i=i.apply(e,t)).next())}),h=function(e,t){this[0]=e,this[1]=t},f=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof h,l=a.done;Promise.resolve(o?t[0]:t).then(i=>o?n("return"===e?e:"next",t[1]?{done:i.done,value:i.value}:i,r,s):r({value:i,done:l})).catch(e=>n("throw",e,r,s))}catch(c){s(c)}},s=(e,t,i,s)=>o[e]=a=>(t=new Promise((t,i,s)=>(s=()=>n(e,a,t,i),r?r.then(s):s())),s=()=>r===i&&(r=0),r=i=t.then(s,s),t),o={};return i=i.apply(e,t),o[a("asyncIterator")]=()=>o,s("next"),s("throw"),s("return"),o},m=e=>{var t,i=e[a("asyncIterator")],r=!1,n={};return null==i?(i=e[a("iterator")](),t=e=>n[e]=t=>i[e](t)):(i=i.call(e),t=e=>n[e]=t=>{if(r){if(r=!1,"throw"===e)throw t;return t}return r=!0,{done:!1,value:new h(new Promise(r=>{var n=i[e](t);n instanceof Object||(e=>{throw TypeError(e)})("Object expected"),r(n)}),1)}}),n[a("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},p=(e,t,i)=>(t=e[a("asyncIterator")])?t.call(e):(e=e[a("iterator")](),t={},(i=(i,r)=>(r=e[i])&&(t[i]=t=>new Promise((i,n,s)=>(t=r.call(e,t),s=t.done,Promise.resolve(t.value).then(e=>i({value:e,done:s}),n)))))("next"),i("return"),t);function g(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class k{constructor(){this.listeners=new Map}addEventListener(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(e,t){const i=new Event(e);i.detail=t;const r=this.listeners.get(e);r&&r.forEach(e=>e(i))}}function y(e){if(!e)throw new Error("Assertion failed.")}const b=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},T=e=>e&&e[e.length-1],v=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},S=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-o(2,r))},w=e=>{const t=v(e);return 0===(1&t)?-(t>>1):t+1>>1},P=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),I=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),x=new TextDecoder,C=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),B={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},E=C(B),_={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},R=C(_),F={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},A=C(F),D=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,M={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},N=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class O{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}acquire(){return d(this,null,function*(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,yield i,e})}}const L=/^[0-9a-fA-F]+$/,z=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),U=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,q=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+n>>1,o=i(e[a]);o===t?(s=a,n=a-1):o<t?r=a+1:n=a-1}return s},W=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+(n-r+1)/2|0;i(e[a])<=t?(s=a,r=a+1):n=a-1}return s},H=(e,t,i)=>{const r=W(e,i(t),i);e.splice(r+1,0,t)},V=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},j=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},K=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},X=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},$=e=>{throw new ErrorLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e,t=Object.defineProperty,i=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable,o=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),l=e=>{throw TypeError(e)},c=Math.pow,u=(e,i,r)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[i]=r,d=(e,t)=>{for(var i in t||(t={}))s.call(t,i)&&u(e,i,t[i]);if(n)for(var i of n(t))a.call(t,i)&&u(e,i,t[i]);return e},h=(e,t)=>i(e,r(t)),f=(e,t,i)=>t.has(e)||l("Cannot "+i),m=(e,t,i)=>(f(e,t,"read from private field"),i?i.call(e):t.get(e)),p=(e,t,i)=>new Promise((r,n)=>{var s=e=>{try{o(i.next(e))}catch(t){n(t)}},a=e=>{try{o(i.throw(e))}catch(t){n(t)}},o=e=>e.done?r(e.value):Promise.resolve(e.value).then(s,a);o((i=i.apply(e,t)).next())}),g=function(e,t){this[0]=e,this[1]=t},k=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof g,l=a.done;Promise.resolve(o?t[0]:t).then(i=>o?n("return"===e?e:"next",t[1]?{done:i.done,value:i.value}:i,r,s):r({value:i,done:l})).catch(e=>n("throw",e,r,s))}catch(c){s(c)}},s=(e,t,i,s)=>a[e]=a=>(t=new Promise((t,i,s)=>(s=()=>n(e,a,t,i),r?r.then(s):s())),s=()=>r===i&&(r=0),r=i=t.then(s,s),t),a={};return i=i.apply(e,t),a[o("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},y=e=>{var t,i=e[o("asyncIterator")],r=!1,n={};return null==i?(i=e[o("iterator")](),t=e=>n[e]=t=>i[e](t)):(i=i.call(e),t=e=>n[e]=t=>{if(r){if(r=!1,"throw"===e)throw t;return t}return r=!0,{done:!1,value:new g(new Promise(r=>{var n=i[e](t);n instanceof Object||l("Object expected"),r(n)}),1)}}),n[o("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},b=(e,t,i)=>(t=e[o("asyncIterator")])?t.call(e):(e=e[o("iterator")](),t={},(i=(i,r)=>(r=e[i])&&(t[i]=t=>new Promise((i,n,s)=>(t=r.call(e,t),s=t.done,Promise.resolve(t.value).then(e=>i({value:e,done:s}),n)))))("next"),i("return"),t);function T(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class v{constructor(){var t,i,r;t=this,r=!1,(i=e).has(t)?l("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(t):i.set(t,r),this.listeners=new Map}addEventListener(t,i){var r;m(this,e)||(this.listeners.has(t)||this.listeners.set(t,[]),null==(r=this.listeners.get(t))||r.push(i))}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(t,i){var r;if(m(this,e))return;const n=Object.assign(new Event(t),{detail:i});null==(r=this.listeners.get(t))||r.forEach(t=>{m(this,e)||t(n)})}destroy(){var t,i,r,n;r=!0,f(t=this,i=e,"write to private field"),n?n.call(t,r):i.set(t,r),this.listeners.clear()}}function S(e){if(!e)throw new Error("Assertion failed.")}e=new WeakMap;const w=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},P=e=>e&&e[e.length-1],I=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},x=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-c(2,r))},C=e=>{const t=I(e);return 0===(1&t)?-(t>>1):t+1>>1},B=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),E=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),_=new TextDecoder,R=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),F={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},A=R(F),D={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},M=R(D),N={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},O=R(N),L=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,z={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},U=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class q{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}acquire(){return p(this,null,function*(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,yield i,e})}}const W=/^[0-9a-fA-F]+$/,H=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),V=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,j=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+n>>1,o=i(e[a]);o===t?(s=a,n=a-1):o<t?r=a+1:n=a-1}return s},Line truncated
|
||||
@@ -4,6 +4,14 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var _closed;
|
||||
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>';
|
||||
const $quality = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>';
|
||||
function uniqBy(array, property) {
|
||||
@@ -40,34 +48,34 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
function canRender(config = {}) {
|
||||
return config.control || config.setting;
|
||||
}
|
||||
async function updateQuality(state) {
|
||||
async function updateQuality(state2) {
|
||||
const config = option.m3u8?.quality || {};
|
||||
if (!canRender(config) || !state?.levels.length) {
|
||||
if (!canRender(config) || !state2?.levels.length) {
|
||||
clearQuality();
|
||||
return;
|
||||
}
|
||||
const auto = config.auto || "Auto";
|
||||
const title = config.title || "Quality";
|
||||
const getName = config.getName || ((level) => level.name || `${level.height}P`);
|
||||
const defaultHtml = state.currentLevel ? getName(state.currentLevel) : auto;
|
||||
const defaultHtml = state2.currentLevel ? getName(state2.currentLevel) : auto;
|
||||
const selector = uniqBy(
|
||||
state.levels.map((item) => {
|
||||
state2.levels.map((item) => {
|
||||
return {
|
||||
html: getName(item),
|
||||
value: item.id,
|
||||
default: state.currentLevel?.id === item.id
|
||||
default: state2.currentLevel?.id === item.id
|
||||
};
|
||||
}),
|
||||
"html"
|
||||
).sort((a, b) => {
|
||||
const left = state.levels.find((item) => item.id === a.value);
|
||||
const right = state.levels.find((item) => item.id === b.value);
|
||||
const left = state2.levels.find((item) => item.id === a.value);
|
||||
const right = state2.levels.find((item) => item.id === b.value);
|
||||
return (right?.height || 0) - (left?.height || 0);
|
||||
});
|
||||
selector.push({
|
||||
html: auto,
|
||||
value: "auto",
|
||||
default: !state.currentLevel
|
||||
default: !state2.currentLevel
|
||||
});
|
||||
const onSelect = async (item) => {
|
||||
await shim.switchM3u8Quality(item.value);
|
||||
@@ -97,22 +105,22 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateAudio(state) {
|
||||
async function updateAudio(state2) {
|
||||
const config = option.m3u8?.audio || {};
|
||||
if (!canRender(config) || !state?.audios.length || state.audios.length < 2) {
|
||||
if (!canRender(config) || !state2?.audios.length || state2.audios.length < 2) {
|
||||
clearAudio();
|
||||
return;
|
||||
}
|
||||
const auto = config.auto || "Auto";
|
||||
const title = config.title || "Audio";
|
||||
const getName = config.getName || ((track) => track.name || track.lang || track.language);
|
||||
const defaultHtml = state.currentAudio ? getName(state.currentAudio) : auto;
|
||||
const defaultHtml = state2.currentAudio ? getName(state2.currentAudio) : auto;
|
||||
const selector = uniqBy(
|
||||
state.audios.map((item) => {
|
||||
state2.audios.map((item) => {
|
||||
return {
|
||||
html: getName(item),
|
||||
value: item.id,
|
||||
default: state.currentAudio?.id === item.id
|
||||
default: state2.currentAudio?.id === item.id
|
||||
};
|
||||
}),
|
||||
"html"
|
||||
@@ -120,7 +128,7 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
selector.push({
|
||||
html: auto,
|
||||
value: "auto",
|
||||
default: !state.currentAudio
|
||||
default: !state2.currentAudio
|
||||
});
|
||||
const onSelect = async (item) => {
|
||||
await shim.switchM3u8Audio(item.value);
|
||||
@@ -151,15 +159,15 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
}
|
||||
}
|
||||
async function update() {
|
||||
const state = await shim.getM3u8State();
|
||||
if (!state) {
|
||||
const state2 = await shim.getM3u8State();
|
||||
if (!state2) {
|
||||
clearQuality();
|
||||
clearAudio();
|
||||
return;
|
||||
}
|
||||
await Promise.all([
|
||||
updateQuality(state),
|
||||
updateAudio(state)
|
||||
updateQuality(state2),
|
||||
updateAudio(state2)
|
||||
]);
|
||||
}
|
||||
art.on("video:loadedmetadata", update);
|
||||
@@ -168,32 +176,39 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
}
|
||||
class EventTarget {
|
||||
constructor() {
|
||||
__privateAdd(this, _closed, false);
|
||||
this.listeners = /* @__PURE__ */ new Map();
|
||||
}
|
||||
addEventListener(type, fn) {
|
||||
if (!this.listeners.has(type)) {
|
||||
if (__privateGet(this, _closed))
|
||||
return;
|
||||
if (!this.listeners.has(type))
|
||||
this.listeners.set(type, []);
|
||||
}
|
||||
this.listeners.get(type).push(fn);
|
||||
this.listeners.get(type)?.push(fn);
|
||||
}
|
||||
removeEventListener(type, fn) {
|
||||
const list = this.listeners.get(type);
|
||||
if (!list)
|
||||
return;
|
||||
const index = list.indexOf(fn);
|
||||
if (index >= 0) {
|
||||
if (index >= 0)
|
||||
list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
emit(type, detail) {
|
||||
const evt = new Event(type);
|
||||
evt.detail = detail;
|
||||
const list = this.listeners.get(type);
|
||||
if (list) {
|
||||
list.forEach((fn) => fn(evt));
|
||||
}
|
||||
if (__privateGet(this, _closed))
|
||||
return;
|
||||
const event = Object.assign(new Event(type), { detail });
|
||||
this.listeners.get(type)?.forEach((fn) => {
|
||||
if (!__privateGet(this, _closed))
|
||||
fn(event);
|
||||
});
|
||||
}
|
||||
destroy() {
|
||||
__privateSet(this, _closed, true);
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
_closed = new WeakMap();
|
||||
function assert(x) {
|
||||
if (!x) {
|
||||
throw new Error("Assertion failed.");
|
||||
@@ -479,7 +494,7 @@ const mapAsyncGenerator = (generator, map) => {
|
||||
}
|
||||
};
|
||||
};
|
||||
const clamp$1 = (value, min, max) => {
|
||||
const clamp = (value, min, max) => {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
};
|
||||
const UNDETERMINED_LANGUAGE = "und";
|
||||
@@ -15172,7 +15187,7 @@ class UrlSource extends PathedSource {
|
||||
if (baseSize === null) {
|
||||
return this._length !== null ? this._length : null;
|
||||
}
|
||||
return clamp$1(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
}
|
||||
/** @internal */
|
||||
_read(start, end, minReadPosition, maxReadPosition) {
|
||||
@@ -16113,7 +16128,7 @@ class RangedSource extends Source {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return clamp$1(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
}
|
||||
/** @internal */
|
||||
_read(start, end, minReadPosition, maxReadPosition) {
|
||||
@@ -19826,13 +19841,13 @@ const getWriteFunction = (format) => {
|
||||
switch (format) {
|
||||
case "u8":
|
||||
case "u8-planar":
|
||||
return (view, offset, value) => view.setUint8(offset, clamp$1((value + 1) * 127.5, 0, 255));
|
||||
return (view, offset, value) => view.setUint8(offset, clamp((value + 1) * 127.5, 0, 255));
|
||||
case "s16":
|
||||
case "s16-planar":
|
||||
return (view, offset, value) => view.setInt16(offset, clamp$1(Math.round(value * 32767), -32768, 32767), true);
|
||||
return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32767), -32768, 32767), true);
|
||||
case "s32":
|
||||
case "s32-planar":
|
||||
return (view, offset, value) => view.setInt32(offset, clamp$1(Math.round(value * 2147483647), -2147483648, 2147483647), true);
|
||||
return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), true);
|
||||
case "f32":
|
||||
case "f32-planar":
|
||||
return (view, offset, value) => view.setFloat32(offset, value, true);
|
||||
@@ -20901,7 +20916,7 @@ class ColorAlphaMerger {
|
||||
const blob = new Blob([`(${colorAlphaMergerWorkerCode.toString()})()`], { type: "application/javascript" });
|
||||
mergerWorkerUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
const poolSize = clamp$1(navigator.hardwareConcurrency, 1, 4);
|
||||
const poolSize = clamp(navigator.hardwareConcurrency, 1, 4);
|
||||
for (let i = 0; i < poolSize; i++) {
|
||||
const worker2 = new Worker(mergerWorkerUrl);
|
||||
worker2.addEventListener("message", (event) => {
|
||||
@@ -23165,7 +23180,7 @@ class Reader {
|
||||
return null;
|
||||
}
|
||||
if (this.fileSizeNonStrict !== null) {
|
||||
return this.requestSlice(start, clamp$1(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
return this.requestSlice(start, clamp(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
} else {
|
||||
const promisedAttempt = this.requestSlice(start, maxLength);
|
||||
const handleAttempt = (attempt) => {
|
||||
@@ -23173,7 +23188,7 @@ class Reader {
|
||||
return attempt;
|
||||
}
|
||||
assert(this.fileSizeNonStrict !== null);
|
||||
return this.requestSlice(start, clamp$1(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
return this.requestSlice(start, clamp(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
};
|
||||
if (isThenable(promisedAttempt)) {
|
||||
return promisedAttempt.then(handleAttempt);
|
||||
@@ -24929,8 +24944,59 @@ class MediaBunnyEngine {
|
||||
return this.video.height;
|
||||
}
|
||||
}
|
||||
function clamp(v, min, max) {
|
||||
return Math.max(min, Math.min(max, Number(v) || 0));
|
||||
const states = /* @__PURE__ */ new WeakMap();
|
||||
function state(owner) {
|
||||
let value = states.get(owner);
|
||||
if (!value) {
|
||||
value = { closed: false, frames: /* @__PURE__ */ new Set() };
|
||||
states.set(owner, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function requestFrame(owner, callback) {
|
||||
const resource = state(owner);
|
||||
if (resource.closed)
|
||||
return 0;
|
||||
const id = requestAnimationFrame((time) => {
|
||||
resource.frames.delete(id);
|
||||
if (resource.closed)
|
||||
return;
|
||||
callback(time, {
|
||||
presentationTime: owner.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: owner.engine.videoWidth,
|
||||
height: owner.engine.videoHeight,
|
||||
mediaTime: owner.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0
|
||||
});
|
||||
});
|
||||
resource.frames.add(id);
|
||||
return id;
|
||||
}
|
||||
function cancelFrame(owner, id) {
|
||||
state(owner).frames.delete(id);
|
||||
cancelAnimationFrame(id);
|
||||
}
|
||||
function closeFrames(owner) {
|
||||
const resource = state(owner);
|
||||
if (resource.closed)
|
||||
return false;
|
||||
resource.closed = true;
|
||||
for (const id of resource.frames) cancelAnimationFrame(id);
|
||||
resource.frames.clear();
|
||||
return true;
|
||||
}
|
||||
function clampVolume(value) {
|
||||
return Math.max(0, Math.min(1, Number(value) || 0));
|
||||
}
|
||||
function timeRanges(duration, start, end) {
|
||||
if (!duration || Number.isNaN(duration) || end <= 0)
|
||||
return { length: 0, start: () => 0, end: () => 0 };
|
||||
return { length: 1, start: () => start, end: () => end };
|
||||
}
|
||||
class VideoShim {
|
||||
constructor({ art, canvas, ctx, option }) {
|
||||
@@ -24999,15 +25065,7 @@ class VideoShim {
|
||||
return this.createTimeRanges(0, this.engine.duration);
|
||||
}
|
||||
createTimeRanges(start, end) {
|
||||
const duration = this.engine.duration;
|
||||
if (!duration || Number.isNaN(duration) || end <= 0) {
|
||||
return { length: 0, start: () => 0, end: () => 0 };
|
||||
}
|
||||
return {
|
||||
length: 1,
|
||||
start: () => start,
|
||||
end: () => end
|
||||
};
|
||||
return timeRanges(this.engine.duration, start, end);
|
||||
}
|
||||
// Playback state
|
||||
get paused() {
|
||||
@@ -25049,7 +25107,7 @@ class VideoShim {
|
||||
return this._volume;
|
||||
}
|
||||
set volume(v) {
|
||||
this._volume = clamp(v, 0, 1);
|
||||
this._volume = clampVolume(v);
|
||||
this._muted = false;
|
||||
this.engine.setVolume(this._volume, this._muted);
|
||||
this.events.emit("volumechange");
|
||||
@@ -25144,24 +25202,10 @@ class VideoShim {
|
||||
return this.canvas.getBoundingClientRect();
|
||||
}
|
||||
requestVideoFrameCallback(callback) {
|
||||
const id = requestAnimationFrame((time) => {
|
||||
callback(time, {
|
||||
presentationTime: this.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: this.engine.videoWidth,
|
||||
height: this.engine.videoHeight,
|
||||
mediaTime: this.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0
|
||||
});
|
||||
});
|
||||
return id;
|
||||
return requestFrame(this, callback);
|
||||
}
|
||||
cancelVideoFrameCallback(id) {
|
||||
cancelAnimationFrame(id);
|
||||
cancelFrame(this, id);
|
||||
}
|
||||
setAttribute(name, value) {
|
||||
if (name === "src") {
|
||||
@@ -25173,11 +25217,17 @@ class VideoShim {
|
||||
} else if (name === "muted") {
|
||||
this.muted = true;
|
||||
} else {
|
||||
this.canvas.setAttribute(name, value);
|
||||
Reflect.apply(this.canvas.setAttribute, this.canvas, [name, value]);
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
this.engine.destroy();
|
||||
if (!closeFrames(this))
|
||||
return;
|
||||
try {
|
||||
this.engine.destroy();
|
||||
} finally {
|
||||
this.events.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
function artplayerProxyMediabunny(option = {}) {
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
"test:unit": "node --test test/mediabunny.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.test.js test/vast-lifecycle.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:unit": "node --test test/mediabunny.test.js test/mediabunny-shim.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.test.js test/vast-lifecycle.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:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs",
|
||||
"test": "yarn test:node && yarn test:baseline",
|
||||
"test:browser": "playwright test",
|
||||
@@ -76,7 +76,7 @@
|
||||
"test:canvas-types-package": "node refactor/scripts/canvas-package-types.mjs",
|
||||
"test:dpip": "node --test test/dpip.test.js test/dpip-lifecycle.test.js",
|
||||
"test:dpip-types-package": "node refactor/scripts/dpip-package-types.mjs",
|
||||
"test:mediabunny": "node --test test/mediabunny.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js"
|
||||
"test:mediabunny": "node --test test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -17,13 +17,31 @@ declarations, the factory signature, canvas forwarding, or package entrypoints.
|
||||
| `load-session.ts` | Ownership of the pending/active SDK Input, HEAD AbortController, deferred load events and deadline |
|
||||
| `MediaBunnyEngine.js` | Current coordinator; connects session cancellation to load, source replacement, errors and destroy |
|
||||
| `VideoEngine.js` / `AudioEngine.js` | Current native decoding, rendering, seeking, Web Audio clock and scheduling |
|
||||
| `VideoShim.js` / `EventTarget.js` | Current video-like descriptors and event forwarding |
|
||||
| `VideoShim.ts` / `EventTarget.ts` | Video-like descriptors, synchronous/Promise forwarding and owned event lifecycle |
|
||||
| `shim-values.ts` | Existing volume coercion and synthetic TimeRanges values |
|
||||
| `shim-frames.ts` | Synthetic frame callbacks, per-instance RAF ownership and terminal cleanup |
|
||||
| `engine-types.ts` / `MediaBunnyEngine.d.ts` | Explicit temporary coordinator boundary while its JavaScript implementation is migrated in MB-04 |
|
||||
| `m3u8.js` | Current ArtPlayer control/setting integration |
|
||||
|
||||
The six TypeScript modules use strict checking with `skipLibCheck: false` and no
|
||||
ambient Node types. Remaining JavaScript is explicitly scheduled in PKG-MB-04/05/06/07/08.
|
||||
The eleven TypeScript modules use strict checking with `skipLibCheck: false` and no
|
||||
ambient Node types. The adjacent coordinator declaration is a transitional boundary,
|
||||
not proof that the JavaScript coordinator has been checked. Replace it with the actual
|
||||
strict implementation before completing MB-04. Remaining JavaScript is explicitly
|
||||
scheduled in PKG-MB-04/05/06/07/08.
|
||||
Do not describe the whole package as migrated yet.
|
||||
|
||||
The shim keeps its existing own-property order and direct prototype surface because
|
||||
the entry copies those descriptors onto the canvas. Resource bookkeeping lives in a
|
||||
WeakMap rather than adding accidental canvas properties. Frame callback IDs include
|
||||
zero; destruction cancels pending callbacks and closes scheduling. The callback metadata
|
||||
remains synthetic (`presentedFrames: 0`), not a claim of decoded frame accuracy.
|
||||
|
||||
EventTarget retains duplicate listeners, first-match removal, live-array `forEach`
|
||||
mutation semantics, detail identity and listener exceptions during normal dispatch.
|
||||
Its terminal state stops callbacks later in a dispatch when a listener destroys the
|
||||
shim. Shim teardown closes events in `finally`, even if engine cleanup throws, and
|
||||
subsequent teardown is inert. Engine playback/readiness race fixes remain in progress.
|
||||
|
||||
## Input and cancellation flow
|
||||
|
||||
Each `engine.load` invalidates the previous generation and cancels its session.
|
||||
@@ -69,6 +87,7 @@ yarn test:mediabunny
|
||||
node node_modules/typescript/bin/tsc -p packages/artplayer-proxy-mediabunny/tsconfig.json
|
||||
yarn build artplayer-proxy-mediabunny
|
||||
yarn test:browser test/browser/mediabunny-inputs.spec.js test/browser/mediabunny.spec.js test/browser/mediabunny-load.spec.js
|
||||
yarn test:browser test/browser/mediabunny-shim.spec.js
|
||||
```
|
||||
|
||||
Node tests use actual SDK parsing and controlled lifecycle interleavings. Browser tests
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";function e(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class t{constructor(){this.listeners=new Map}addEventListener(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(e,t){const i=new Event(e);i.detail=t;const r=this.listeners.get(e);r&&r.forEach(e=>e(i))}}function i(e){if(!e)throw new Error("Assertion failed.")}const r=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},a=e=>e&&e[e.length-1],s=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},n=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-2**r)},o=e=>{const t=s(e);return 0===(1&t)?-(t>>1):t+1>>1},c=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),l=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),u=new TextDecoder,d=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),h={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},f=d(h),m={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},p=d(m),g={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},k=d(g),y=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,b={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},w=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class T{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}async acquire(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,await i,e}}const S=/^[0-9a-fA-F]+$/,v=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),P=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,I=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+a>>1,o=i(e[n]);o===t?(s=n,a=n-1):o<t?r=n+1:a=n-1}return s},x=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+(a-r+1)/2|0;i(e[n])<=t?(s=n,r=n+1):a=n-1}return s},C=(e,t,i)=>{const r=x(e,i(t),i);e.splice(r+1,0,t)},B=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},E=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},_=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},R=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},F=e=>{throw new Error(`Unexpected value: ${e}`)},A=(e,t,i)=>{const r=e.getUint8(t),a=e.getUint8(t+1),s=e.getUint8(t+2);return i?r|a<<8|s<<16:r<<16|a<<8|s},D=(e,t,i,r)=>{i>>>=0,i&=16777215,e.setUint8(t,i>>>16&255),e.setUint8(t+1,i>>>8&255),e.setUint8(t+2,255&i)},M=(e,t)=>({async next(){const i=await e.next();return i.done?{value:void 0,done:!0}:{value:t(i.value),done:!1}},return:()=>e.return(),throw:t=>e.throw(t),[Symbol.asyncIterator](){return this}}),N=(e,t,i)=>Math.max(t,Math.min(i,e)),O="und",L=e=>{const t=Math.round(e);return Math.abs(e/t-1)<10*Number.EPSILON?t:e},z=(e,t)=>Math.round(e/t)*t,U=(e,t)=>Math.round(e*t)/t,q=(e,t)=>Math.floor(e/t)*t,W=e=>{let t=0;for(;0!==e;)e&=e-1,t++;return t},H=/^[a-z]{3}$/,V=e=>H.test(e),j=1e6*(1+Number.EPSILON),K=(e,t)=>{const i={...e,...t};if(e.headers||t.headers){const r=e.headers?X(e.headers):{},a=t.headers?X(t.headers):{},s={...r};Object.entries(a).forEach(([e,t])=>{const i=Object.keys(s).find(t=>t.toLowerCase()===e.toLowerCase());i&&delete s[i],s[e]=t}),i.headers=s}return i},X=e=>{if(e instanceof Headers){const t={};return e.forEach((e,i)=>{t[i]=e}),t}if(Array.isArray(e)){const t={};return e.forEach(([e,i])=>{t[e]=i}),t}return e},$=async(e,t,i,r,a)=>{let s=0;for(;;)try{return await e(t,i)}catch(n){if(a())throw n;s++;const e=r(s,n,t);if(null===e)throw n;if(be._error("Retrying failed fetch. Error:",n),!Number.isFinite(e)||e<0)throw new TypeError("Retry delay must be a non-negative finite number.");if(e>0&&await pe(1e3*e),a())throw n}};class G{constructor(){this.currentPromise=Promise.resolve()}call(e){return this.currentPromise=this.currentPromise.then(e)}}let Q=null;const Y=()=>null!==Q?Q:Q=!("undefined"===typeof navigator||!(navigator.vendor?.match(/apple/i)||/AppleWebKit/.test(navigator.userAgent)&&!/Chrome/.test(navigator.userAgent)||/\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent)));let J=null;coLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e,t=e=>{throw TypeError(e)},i=(e,i,r)=>i.has(e)||t("Cannot "+r),r=(e,t,r)=>(i(e,t,"read from private field"),r?r.call(e):t.get(e));function a(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class s{constructor(){var i,r,a;i=this,a=!1,(r=e).has(i)?t("Cannot add the same private member more than once"):r instanceof WeakSet?r.add(i):r.set(i,a),this.listeners=new Map}addEventListener(t,i){r(this,e)||(this.listeners.has(t)||this.listeners.set(t,[]),this.listeners.get(t)?.push(i))}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(t,i){if(r(this,e))return;const a=Object.assign(new Event(t),{detail:i});this.listeners.get(t)?.forEach(t=>{r(this,e)||t(a)})}destroy(){var t,r,a,s;a=!0,i(t=this,r=e,"write to private field"),s?s.call(t,a):r.set(t,a),this.listeners.clear()}}function n(e){if(!e)throw new Error("Assertion failed.")}e=new WeakMap;const o=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},c=e=>e&&e[e.length-1],l=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},u=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-2**r)},d=e=>{const t=l(e);return 0===(1&t)?-(t>>1):t+1>>1},h=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),f=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),m=new TextDecoder,p=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),g={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},k=p(g),y={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},b=p(y),w={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},T=p(w),S=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,v={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},P=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class I{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}async acquire(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,await i,e}}const x=/^[0-9a-fA-F]+$/,C=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),B=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,E=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+a>>1,o=i(e[n]);o===t?(s=n,a=n-1):o<t?r=n+1:a=n-1}return s},_=(e,t,i)=>{let r=0,a=e.length-1,s=-1;for(;r<=a;){const n=r+(a-r+1)/2|0;i(e[n])<=t?(s=n,r=n+1):a=n-1}return s},R=(e,t,i)=>{const r=_(e,i(t),i);e.splice(r+1,0,t)},F=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},A=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},D=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},M=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},N=e=>{throw new Error(`Unexpected value: ${e}`)},O=(e,t,i)=>{const r=e.getUint8(t),a=e.getUint8(t+1),s=e.getUint8(t+2);return i?r|a<<8|s<<16:r<<16|a<<8|s},L=(e,t,i,r)=>{i>>>=0,i&=16777215,e.setUint8(t,i>>>16&255),e.setUint8(t+1,i>>>8&255),e.setUint8(t+2,255&i)},z=(e,t)=>({async next(){const i=await e.next();return i.done?{value:void 0,done:!0}:{value:t(i.value),done:!1}},return:()=>e.return(),throw:t=>e.throw(t),[Symbol.asyncIterator](){return this}}),U=(e,t,i)=>Math.max(t,Math.min(i,e)),q="und",W=e=>{const t=Math.round(e);return Math.abs(e/t-1)<10*Number.EPSILON?t:e},H=(e,t)=>Math.round(e/t)*t,V=(e,t)=>Math.round(e*t)/t,j=(e,t)=>Math.floor(e/t)*t,K=e=>{let t=0;for(;0!==e;)e&=e-1,t++;return t},X=/^[a-z]{3}$/,$=e=>X.test(e),G=1e6*(1+Number.EPSILON),Q=(e,t)=>{const i={...e,...t};if(e.headers||t.headers){const r=e.headers?Y(e.headers):{},a=t.headers?Y(t.headers):{},s={...r};Object.entries(a).forEach(([e,t])=>{const i=Object.keys(s).find(t=>t.toLowerCase()===e.toLowerCase());i&&delete s[i],s[e]=t}),i.headers=s}return i},Y=e=>{if(e instanceof Headers){const t={};return e.forEach((e,i)=>{t[i]=e}),t}if(Array.isArray(e)){const t={};return e.forEach(([e,i])=>{t[e]=i}),t}return e},J=async(e,t,i,r,a)=>{let s=0;for(;;)try{return await e(t,i)}catch(n){if(a())throw n;s++;const e=r(s,n,t);if(null===e)throw n;if(ve._error("Retrying failed fetch. Error:",n),!Number.isFinite(e)||e<0)throw new TypeError("Retry delay must be aLine truncated
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,i=Object.getOwnPropertyDescriptors,r=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,a=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),o=Math.pow,l=(t,i,r)=>i in t?e(t,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[i]=r,c=(e,t)=>{for(var i in t||(t={}))n.call(t,i)&&l(e,i,t[i]);if(r)for(var i of r(t))s.call(t,i)&&l(e,i,t[i]);return e},u=(e,r)=>t(e,i(r)),d=(e,t,i)=>new Promise((r,n)=>{var s=e=>{try{o(i.next(e))}catch(t){n(t)}},a=e=>{try{o(i.throw(e))}catch(t){n(t)}},o=e=>e.done?r(e.value):Promise.resolve(e.value).then(s,a);o((i=i.apply(e,t)).next())}),h=function(e,t){this[0]=e,this[1]=t},f=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof h,l=a.done;Promise.resolve(o?t[0]:t).then(i=>o?n("return"===e?e:"next",t[1]?{done:i.done,value:i.value}:i,r,s):r({value:i,done:l})).catch(e=>n("throw",e,r,s))}catch(c){s(c)}},s=(e,t,i,s)=>o[e]=a=>(t=new Promise((t,i,s)=>(s=()=>n(e,a,t,i),r?r.then(s):s())),s=()=>r===i&&(r=0),r=i=t.then(s,s),t),o={};return i=i.apply(e,t),o[a("asyncIterator")]=()=>o,s("next"),s("throw"),s("return"),o},m=e=>{var t,i=e[a("asyncIterator")],r=!1,n={};return null==i?(i=e[a("iterator")](),t=e=>n[e]=t=>i[e](t)):(i=i.call(e),t=e=>n[e]=t=>{if(r){if(r=!1,"throw"===e)throw t;return t}return r=!0,{done:!1,value:new h(new Promise(r=>{var n=i[e](t);n instanceof Object||(e=>{throw TypeError(e)})("Object expected"),r(n)}),1)}}),n[a("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},p=(e,t,i)=>(t=e[a("asyncIterator")])?t.call(e):(e=e[a("iterator")](),t={},(i=(i,r)=>(r=e[i])&&(t[i]=t=>new Promise((i,n,s)=>(t=r.call(e,t),s=t.done,Promise.resolve(t.value).then(e=>i({value:e,done:s}),n)))))("next"),i("return"),t);function g(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class k{constructor(){this.listeners=new Map}addEventListener(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(e,t){const i=new Event(e);i.detail=t;const r=this.listeners.get(e);r&&r.forEach(e=>e(i))}}function y(e){if(!e)throw new Error("Assertion failed.")}const b=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},T=e=>e&&e[e.length-1],v=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},S=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-o(2,r))},w=e=>{const t=v(e);return 0===(1&t)?-(t>>1):t+1>>1},P=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),I=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),x=new TextDecoder,C=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),B={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},E=C(B),_={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},R=C(_),F={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},A=C(F),D=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,M={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},N=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class O{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}acquire(){return d(this,null,function*(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,yield i,e})}}const L=/^[0-9a-fA-F]+$/,z=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),U=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,q=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+n>>1,o=i(e[a]);o===t?(s=a,n=a-1):o<t?r=a+1:n=a-1}return s},W=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+(n-r+1)/2|0;i(e[a])<=t?(s=a,r=a+1):n=a-1}return s},H=(e,t,i)=>{const r=W(e,i(t),i);e.splice(r+1,0,t)},V=()=>{let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}},j=(e,t)=>{const i=e.indexOf(t);-1!==i&&e.splice(i,1)},K=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return e[i]},X=(e,t)=>{for(let i=e.length-1;i>=0;i--)if(t(e[i]))return i;return-1},$=e=>{throw new ErrorLine truncated
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyMediabunny=t(),define(function(){return e.artplayerProxyMediabunny})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyMediabunny=t()}(this,function(){"use strict";var e,t=Object.defineProperty,i=Object.defineProperties,r=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable,o=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),l=e=>{throw TypeError(e)},c=Math.pow,u=(e,i,r)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[i]=r,d=(e,t)=>{for(var i in t||(t={}))s.call(t,i)&&u(e,i,t[i]);if(n)for(var i of n(t))a.call(t,i)&&u(e,i,t[i]);return e},h=(e,t)=>i(e,r(t)),f=(e,t,i)=>t.has(e)||l("Cannot "+i),m=(e,t,i)=>(f(e,t,"read from private field"),i?i.call(e):t.get(e)),p=(e,t,i)=>new Promise((r,n)=>{var s=e=>{try{o(i.next(e))}catch(t){n(t)}},a=e=>{try{o(i.throw(e))}catch(t){n(t)}},o=e=>e.done?r(e.value):Promise.resolve(e.value).then(s,a);o((i=i.apply(e,t)).next())}),g=function(e,t){this[0]=e,this[1]=t},k=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof g,l=a.done;Promise.resolve(o?t[0]:t).then(i=>o?n("return"===e?e:"next",t[1]?{done:i.done,value:i.value}:i,r,s):r({value:i,done:l})).catch(e=>n("throw",e,r,s))}catch(c){s(c)}},s=(e,t,i,s)=>a[e]=a=>(t=new Promise((t,i,s)=>(s=()=>n(e,a,t,i),r?r.then(s):s())),s=()=>r===i&&(r=0),r=i=t.then(s,s),t),a={};return i=i.apply(e,t),a[o("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},y=e=>{var t,i=e[o("asyncIterator")],r=!1,n={};return null==i?(i=e[o("iterator")](),t=e=>n[e]=t=>i[e](t)):(i=i.call(e),t=e=>n[e]=t=>{if(r){if(r=!1,"throw"===e)throw t;return t}return r=!0,{done:!1,value:new g(new Promise(r=>{var n=i[e](t);n instanceof Object||l("Object expected"),r(n)}),1)}}),n[o("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},b=(e,t,i)=>(t=e[o("asyncIterator")])?t.call(e):(e=e[o("iterator")](),t={},(i=(i,r)=>(r=e[i])&&(t[i]=t=>new Promise((i,n,s)=>(t=r.call(e,t),s=t.done,Promise.resolve(t.value).then(e=>i({value:e,done:s}),n)))))("next"),i("return"),t);function T(e,t){const i=new Map;return e.filter(e=>{const r=e[t];return void 0===r||!i.has(r)&&i.set(r,1)})}class v{constructor(){var t,i,r;t=this,r=!1,(i=e).has(t)?l("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(t):i.set(t,r),this.listeners=new Map}addEventListener(t,i){var r;m(this,e)||(this.listeners.has(t)||this.listeners.set(t,[]),null==(r=this.listeners.get(t))||r.push(i))}removeEventListener(e,t){const i=this.listeners.get(e);if(!i)return;const r=i.indexOf(t);r>=0&&i.splice(r,1)}emit(t,i){var r;if(m(this,e))return;const n=Object.assign(new Event(t),{detail:i});null==(r=this.listeners.get(t))||r.forEach(t=>{m(this,e)||t(n)})}destroy(){var t,i,r,n;r=!0,f(t=this,i=e,"write to private field"),n?n.call(t,r):i.set(t,r),this.listeners.clear()}}function S(e){if(!e)throw new Error("Assertion failed.")}e=new WeakMap;const w=e=>{const t=(e%360+360)%360;if(0===t||90===t||180===t||270===t)return t;throw new Error(`Invalid rotation ${e}.`)},P=e=>e&&e[e.length-1],I=e=>{let t=0;for(;0===e.readBits(1)&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<<t)-1+e.readBits(t)},x=(e,t)=>{const i=t+1,r=Math.floor(Math.log2(i));e.writeBits(r,0),e.writeBits(1,1),e.writeBits(r,i-c(2,r))},C=e=>{const t=I(e);return 0===(1&t)?-(t>>1):t+1>>1},B=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),E=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),_=new TextDecoder,R=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e])),F={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},A=R(F),D={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},M=R(D),N={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},O=R(N),L=e=>!!e&&!!e.primaries&&!!e.transfer&&!!e.matrix&&void 0!==e.fullRange,z={primaries:void 0,transfer:void 0,matrix:void 0,fullRange:void 0},U=e=>e instanceof ArrayBuffer||"undefined"!==typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class q{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}acquire(){return p(this,null,function*(){let e;const t=new Promise(t=>{let i=!1;e=()=>{i||(t(),this.pending--,i=!0)}}),i=this.currentPromise;return this.currentPromise=t,this.pending++,yield i,e})}}const W=/^[0-9a-fA-F]+$/,H=e=>[...e].map(e=>e.toString(16).padStart(2,"0")).join(""),V=e=>(e=(e=(e=(e=(e=e>>1&1431655765|(1431655765&e)<<1)>>2&858993459|(858993459&e)<<2)>>4&252645135|(252645135&e)<<4)>>8&16711935|(16711935&e)<<8)>>16&65535|(65535&e)<<16)>>>0,j=(e,t,i)=>{let r=0,n=e.length-1,s=-1;for(;r<=n;){const a=r+n>>1,o=i(e[a]);o===t?(s=a,n=a-1):o<t?r=a+1:n=a-1}return s},Line truncated
|
||||
@@ -4,6 +4,14 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var _closed;
|
||||
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>';
|
||||
const $quality = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="18"><path fill="#fff" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l96 0 32 0 208 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>';
|
||||
function uniqBy(array, property) {
|
||||
@@ -40,34 +48,34 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
function canRender(config = {}) {
|
||||
return config.control || config.setting;
|
||||
}
|
||||
async function updateQuality(state) {
|
||||
async function updateQuality(state2) {
|
||||
const config = option.m3u8?.quality || {};
|
||||
if (!canRender(config) || !state?.levels.length) {
|
||||
if (!canRender(config) || !state2?.levels.length) {
|
||||
clearQuality();
|
||||
return;
|
||||
}
|
||||
const auto = config.auto || "Auto";
|
||||
const title = config.title || "Quality";
|
||||
const getName = config.getName || ((level) => level.name || `${level.height}P`);
|
||||
const defaultHtml = state.currentLevel ? getName(state.currentLevel) : auto;
|
||||
const defaultHtml = state2.currentLevel ? getName(state2.currentLevel) : auto;
|
||||
const selector = uniqBy(
|
||||
state.levels.map((item) => {
|
||||
state2.levels.map((item) => {
|
||||
return {
|
||||
html: getName(item),
|
||||
value: item.id,
|
||||
default: state.currentLevel?.id === item.id
|
||||
default: state2.currentLevel?.id === item.id
|
||||
};
|
||||
}),
|
||||
"html"
|
||||
).sort((a, b) => {
|
||||
const left = state.levels.find((item) => item.id === a.value);
|
||||
const right = state.levels.find((item) => item.id === b.value);
|
||||
const left = state2.levels.find((item) => item.id === a.value);
|
||||
const right = state2.levels.find((item) => item.id === b.value);
|
||||
return (right?.height || 0) - (left?.height || 0);
|
||||
});
|
||||
selector.push({
|
||||
html: auto,
|
||||
value: "auto",
|
||||
default: !state.currentLevel
|
||||
default: !state2.currentLevel
|
||||
});
|
||||
const onSelect = async (item) => {
|
||||
await shim.switchM3u8Quality(item.value);
|
||||
@@ -97,22 +105,22 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
});
|
||||
}
|
||||
}
|
||||
async function updateAudio(state) {
|
||||
async function updateAudio(state2) {
|
||||
const config = option.m3u8?.audio || {};
|
||||
if (!canRender(config) || !state?.audios.length || state.audios.length < 2) {
|
||||
if (!canRender(config) || !state2?.audios.length || state2.audios.length < 2) {
|
||||
clearAudio();
|
||||
return;
|
||||
}
|
||||
const auto = config.auto || "Auto";
|
||||
const title = config.title || "Audio";
|
||||
const getName = config.getName || ((track) => track.name || track.lang || track.language);
|
||||
const defaultHtml = state.currentAudio ? getName(state.currentAudio) : auto;
|
||||
const defaultHtml = state2.currentAudio ? getName(state2.currentAudio) : auto;
|
||||
const selector = uniqBy(
|
||||
state.audios.map((item) => {
|
||||
state2.audios.map((item) => {
|
||||
return {
|
||||
html: getName(item),
|
||||
value: item.id,
|
||||
default: state.currentAudio?.id === item.id
|
||||
default: state2.currentAudio?.id === item.id
|
||||
};
|
||||
}),
|
||||
"html"
|
||||
@@ -120,7 +128,7 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
selector.push({
|
||||
html: auto,
|
||||
value: "auto",
|
||||
default: !state.currentAudio
|
||||
default: !state2.currentAudio
|
||||
});
|
||||
const onSelect = async (item) => {
|
||||
await shim.switchM3u8Audio(item.value);
|
||||
@@ -151,15 +159,15 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
}
|
||||
}
|
||||
async function update() {
|
||||
const state = await shim.getM3u8State();
|
||||
if (!state) {
|
||||
const state2 = await shim.getM3u8State();
|
||||
if (!state2) {
|
||||
clearQuality();
|
||||
clearAudio();
|
||||
return;
|
||||
}
|
||||
await Promise.all([
|
||||
updateQuality(state),
|
||||
updateAudio(state)
|
||||
updateQuality(state2),
|
||||
updateAudio(state2)
|
||||
]);
|
||||
}
|
||||
art.on("video:loadedmetadata", update);
|
||||
@@ -168,32 +176,39 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
}
|
||||
class EventTarget {
|
||||
constructor() {
|
||||
__privateAdd(this, _closed, false);
|
||||
this.listeners = /* @__PURE__ */ new Map();
|
||||
}
|
||||
addEventListener(type, fn) {
|
||||
if (!this.listeners.has(type)) {
|
||||
if (__privateGet(this, _closed))
|
||||
return;
|
||||
if (!this.listeners.has(type))
|
||||
this.listeners.set(type, []);
|
||||
}
|
||||
this.listeners.get(type).push(fn);
|
||||
this.listeners.get(type)?.push(fn);
|
||||
}
|
||||
removeEventListener(type, fn) {
|
||||
const list = this.listeners.get(type);
|
||||
if (!list)
|
||||
return;
|
||||
const index = list.indexOf(fn);
|
||||
if (index >= 0) {
|
||||
if (index >= 0)
|
||||
list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
emit(type, detail) {
|
||||
const evt = new Event(type);
|
||||
evt.detail = detail;
|
||||
const list = this.listeners.get(type);
|
||||
if (list) {
|
||||
list.forEach((fn) => fn(evt));
|
||||
}
|
||||
if (__privateGet(this, _closed))
|
||||
return;
|
||||
const event = Object.assign(new Event(type), { detail });
|
||||
this.listeners.get(type)?.forEach((fn) => {
|
||||
if (!__privateGet(this, _closed))
|
||||
fn(event);
|
||||
});
|
||||
}
|
||||
destroy() {
|
||||
__privateSet(this, _closed, true);
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
_closed = new WeakMap();
|
||||
function assert(x) {
|
||||
if (!x) {
|
||||
throw new Error("Assertion failed.");
|
||||
@@ -479,7 +494,7 @@ const mapAsyncGenerator = (generator, map) => {
|
||||
}
|
||||
};
|
||||
};
|
||||
const clamp$1 = (value, min, max) => {
|
||||
const clamp = (value, min, max) => {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
};
|
||||
const UNDETERMINED_LANGUAGE = "und";
|
||||
@@ -15172,7 +15187,7 @@ class UrlSource extends PathedSource {
|
||||
if (baseSize === null) {
|
||||
return this._length !== null ? this._length : null;
|
||||
}
|
||||
return clamp$1(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
}
|
||||
/** @internal */
|
||||
_read(start, end, minReadPosition, maxReadPosition) {
|
||||
@@ -16113,7 +16128,7 @@ class RangedSource extends Source {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return clamp$1(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
|
||||
}
|
||||
/** @internal */
|
||||
_read(start, end, minReadPosition, maxReadPosition) {
|
||||
@@ -19826,13 +19841,13 @@ const getWriteFunction = (format) => {
|
||||
switch (format) {
|
||||
case "u8":
|
||||
case "u8-planar":
|
||||
return (view, offset, value) => view.setUint8(offset, clamp$1((value + 1) * 127.5, 0, 255));
|
||||
return (view, offset, value) => view.setUint8(offset, clamp((value + 1) * 127.5, 0, 255));
|
||||
case "s16":
|
||||
case "s16-planar":
|
||||
return (view, offset, value) => view.setInt16(offset, clamp$1(Math.round(value * 32767), -32768, 32767), true);
|
||||
return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32767), -32768, 32767), true);
|
||||
case "s32":
|
||||
case "s32-planar":
|
||||
return (view, offset, value) => view.setInt32(offset, clamp$1(Math.round(value * 2147483647), -2147483648, 2147483647), true);
|
||||
return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), true);
|
||||
case "f32":
|
||||
case "f32-planar":
|
||||
return (view, offset, value) => view.setFloat32(offset, value, true);
|
||||
@@ -20901,7 +20916,7 @@ class ColorAlphaMerger {
|
||||
const blob = new Blob([`(${colorAlphaMergerWorkerCode.toString()})()`], { type: "application/javascript" });
|
||||
mergerWorkerUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
const poolSize = clamp$1(navigator.hardwareConcurrency, 1, 4);
|
||||
const poolSize = clamp(navigator.hardwareConcurrency, 1, 4);
|
||||
for (let i = 0; i < poolSize; i++) {
|
||||
const worker2 = new Worker(mergerWorkerUrl);
|
||||
worker2.addEventListener("message", (event) => {
|
||||
@@ -23165,7 +23180,7 @@ class Reader {
|
||||
return null;
|
||||
}
|
||||
if (this.fileSizeNonStrict !== null) {
|
||||
return this.requestSlice(start, clamp$1(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
return this.requestSlice(start, clamp(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
} else {
|
||||
const promisedAttempt = this.requestSlice(start, maxLength);
|
||||
const handleAttempt = (attempt) => {
|
||||
@@ -23173,7 +23188,7 @@ class Reader {
|
||||
return attempt;
|
||||
}
|
||||
assert(this.fileSizeNonStrict !== null);
|
||||
return this.requestSlice(start, clamp$1(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
return this.requestSlice(start, clamp(this.fileSizeNonStrict - start, minLength, maxLength));
|
||||
};
|
||||
if (isThenable(promisedAttempt)) {
|
||||
return promisedAttempt.then(handleAttempt);
|
||||
@@ -24929,8 +24944,59 @@ class MediaBunnyEngine {
|
||||
return this.video.height;
|
||||
}
|
||||
}
|
||||
function clamp(v, min, max) {
|
||||
return Math.max(min, Math.min(max, Number(v) || 0));
|
||||
const states = /* @__PURE__ */ new WeakMap();
|
||||
function state(owner) {
|
||||
let value = states.get(owner);
|
||||
if (!value) {
|
||||
value = { closed: false, frames: /* @__PURE__ */ new Set() };
|
||||
states.set(owner, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function requestFrame(owner, callback) {
|
||||
const resource = state(owner);
|
||||
if (resource.closed)
|
||||
return 0;
|
||||
const id = requestAnimationFrame((time) => {
|
||||
resource.frames.delete(id);
|
||||
if (resource.closed)
|
||||
return;
|
||||
callback(time, {
|
||||
presentationTime: owner.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: owner.engine.videoWidth,
|
||||
height: owner.engine.videoHeight,
|
||||
mediaTime: owner.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0
|
||||
});
|
||||
});
|
||||
resource.frames.add(id);
|
||||
return id;
|
||||
}
|
||||
function cancelFrame(owner, id) {
|
||||
state(owner).frames.delete(id);
|
||||
cancelAnimationFrame(id);
|
||||
}
|
||||
function closeFrames(owner) {
|
||||
const resource = state(owner);
|
||||
if (resource.closed)
|
||||
return false;
|
||||
resource.closed = true;
|
||||
for (const id of resource.frames) cancelAnimationFrame(id);
|
||||
resource.frames.clear();
|
||||
return true;
|
||||
}
|
||||
function clampVolume(value) {
|
||||
return Math.max(0, Math.min(1, Number(value) || 0));
|
||||
}
|
||||
function timeRanges(duration, start, end) {
|
||||
if (!duration || Number.isNaN(duration) || end <= 0)
|
||||
return { length: 0, start: () => 0, end: () => 0 };
|
||||
return { length: 1, start: () => start, end: () => end };
|
||||
}
|
||||
class VideoShim {
|
||||
constructor({ art, canvas, ctx, option }) {
|
||||
@@ -24999,15 +25065,7 @@ class VideoShim {
|
||||
return this.createTimeRanges(0, this.engine.duration);
|
||||
}
|
||||
createTimeRanges(start, end) {
|
||||
const duration = this.engine.duration;
|
||||
if (!duration || Number.isNaN(duration) || end <= 0) {
|
||||
return { length: 0, start: () => 0, end: () => 0 };
|
||||
}
|
||||
return {
|
||||
length: 1,
|
||||
start: () => start,
|
||||
end: () => end
|
||||
};
|
||||
return timeRanges(this.engine.duration, start, end);
|
||||
}
|
||||
// Playback state
|
||||
get paused() {
|
||||
@@ -25049,7 +25107,7 @@ class VideoShim {
|
||||
return this._volume;
|
||||
}
|
||||
set volume(v) {
|
||||
this._volume = clamp(v, 0, 1);
|
||||
this._volume = clampVolume(v);
|
||||
this._muted = false;
|
||||
this.engine.setVolume(this._volume, this._muted);
|
||||
this.events.emit("volumechange");
|
||||
@@ -25144,24 +25202,10 @@ class VideoShim {
|
||||
return this.canvas.getBoundingClientRect();
|
||||
}
|
||||
requestVideoFrameCallback(callback) {
|
||||
const id = requestAnimationFrame((time) => {
|
||||
callback(time, {
|
||||
presentationTime: this.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: this.engine.videoWidth,
|
||||
height: this.engine.videoHeight,
|
||||
mediaTime: this.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0
|
||||
});
|
||||
});
|
||||
return id;
|
||||
return requestFrame(this, callback);
|
||||
}
|
||||
cancelVideoFrameCallback(id) {
|
||||
cancelAnimationFrame(id);
|
||||
cancelFrame(this, id);
|
||||
}
|
||||
setAttribute(name, value) {
|
||||
if (name === "src") {
|
||||
@@ -25173,11 +25217,17 @@ class VideoShim {
|
||||
} else if (name === "muted") {
|
||||
this.muted = true;
|
||||
} else {
|
||||
this.canvas.setAttribute(name, value);
|
||||
Reflect.apply(this.canvas.setAttribute, this.canvas, [name, value]);
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
this.engine.destroy();
|
||||
if (!closeFrames(this))
|
||||
return;
|
||||
try {
|
||||
this.engine.destroy();
|
||||
} finally {
|
||||
this.events.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
function artplayerProxyMediabunny(option = {}) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Event Target Implementation
|
||||
* Simple event system for video events
|
||||
*/
|
||||
export default class EventTarget {
|
||||
constructor() {
|
||||
this.listeners = new Map()
|
||||
}
|
||||
|
||||
addEventListener(type, fn) {
|
||||
if (!this.listeners.has(type)) {
|
||||
this.listeners.set(type, [])
|
||||
}
|
||||
this.listeners.get(type).push(fn)
|
||||
}
|
||||
|
||||
removeEventListener(type, fn) {
|
||||
const list = this.listeners.get(type)
|
||||
if (!list)
|
||||
return
|
||||
|
||||
const index = list.indexOf(fn)
|
||||
if (index >= 0) {
|
||||
list.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
emit(type, detail) {
|
||||
const evt = new Event(type)
|
||||
evt.detail = detail
|
||||
|
||||
const list = this.listeners.get(type)
|
||||
if (list) {
|
||||
list.forEach(fn => fn(evt))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
export type MediaEvent = Event & { detail: unknown }
|
||||
export type MediaListener = (event: MediaEvent) => unknown
|
||||
|
||||
export default class EventTarget {
|
||||
declare listeners: Map<string, MediaListener[]>
|
||||
#closed = false
|
||||
|
||||
constructor() {
|
||||
this.listeners = new Map()
|
||||
}
|
||||
|
||||
addEventListener(type: string, fn: MediaListener): void {
|
||||
if (this.#closed)
|
||||
return
|
||||
if (!this.listeners.has(type))
|
||||
this.listeners.set(type, [])
|
||||
this.listeners.get(type)?.push(fn)
|
||||
}
|
||||
|
||||
removeEventListener(type: string, fn: MediaListener): void {
|
||||
const list = this.listeners.get(type)
|
||||
if (!list)
|
||||
return
|
||||
const index = list.indexOf(fn)
|
||||
if (index >= 0)
|
||||
list.splice(index, 1)
|
||||
}
|
||||
|
||||
emit(type: string, detail?: unknown): void {
|
||||
if (this.#closed)
|
||||
return
|
||||
const event = Object.assign(new Event(type), { detail })
|
||||
// Keep historical live-array forEach semantics and uncaught listener exceptions.
|
||||
this.listeners.get(type)?.forEach((fn) => {
|
||||
if (!this.#closed)
|
||||
fn(event)
|
||||
})
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.#closed = true
|
||||
this.listeners.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { EngineOptions, EnginePort } from './engine-types'
|
||||
|
||||
declare const MediaBunnyEngine: new (options: EngineOptions) => EnginePort
|
||||
export default MediaBunnyEngine
|
||||
+53
-62
@@ -1,16 +1,22 @@
|
||||
import EventTarget from './EventTarget.js'
|
||||
/**
|
||||
* Video Element Shim
|
||||
* Simulates HTMLVideoElement interface for MediaBunny
|
||||
*/
|
||||
import type { EngineOptions, EnginePort, ProxyOptions, ShimHost } from './engine-types'
|
||||
import type { MediaListener } from './EventTarget'
|
||||
import EventTarget from './EventTarget'
|
||||
import MediaBunnyEngine from './MediaBunnyEngine.js'
|
||||
|
||||
function clamp(v, min, max) {
|
||||
return Math.max(min, Math.min(max, Number(v) || 0))
|
||||
}
|
||||
import { cancelFrame, closeFrames, requestFrame } from './shim-frames'
|
||||
import { clampVolume, timeRanges } from './shim-values'
|
||||
|
||||
export default class VideoShim {
|
||||
constructor({ art, canvas, ctx, option }) {
|
||||
declare art: ShimHost
|
||||
declare canvas: HTMLCanvasElement
|
||||
declare option: ProxyOptions
|
||||
declare events: EventTarget
|
||||
declare engine: EnginePort
|
||||
declare _src: unknown
|
||||
declare _volume: number
|
||||
declare _muted: boolean
|
||||
declare _playbackRate: number
|
||||
|
||||
constructor({ art, canvas, ctx, option }: Omit<EngineOptions, 'events' | 'option'> & { art: ShimHost, option: ProxyOptions }) {
|
||||
this.art = art
|
||||
this.canvas = canvas
|
||||
this.option = option
|
||||
@@ -52,11 +58,11 @@ export default class VideoShim {
|
||||
}
|
||||
|
||||
// Event methods
|
||||
addEventListener(type, fn) {
|
||||
addEventListener(type: string, fn: MediaListener) {
|
||||
this.events.addEventListener(type, fn)
|
||||
}
|
||||
|
||||
removeEventListener(type, fn) {
|
||||
removeEventListener(type: string, fn: MediaListener) {
|
||||
this.events.removeEventListener(type, fn)
|
||||
}
|
||||
|
||||
@@ -65,7 +71,7 @@ export default class VideoShim {
|
||||
return this._src
|
||||
}
|
||||
|
||||
set src(v) {
|
||||
set src(v: unknown) {
|
||||
this._src = v
|
||||
if (v)
|
||||
this.engine.load(v)
|
||||
@@ -80,7 +86,7 @@ export default class VideoShim {
|
||||
return this.engine.currentTime
|
||||
}
|
||||
|
||||
set currentTime(t) {
|
||||
set currentTime(t: unknown) {
|
||||
this.engine.seek(Number(t) || 0)
|
||||
}
|
||||
|
||||
@@ -101,16 +107,8 @@ export default class VideoShim {
|
||||
return this.createTimeRanges(0, this.engine.duration)
|
||||
}
|
||||
|
||||
createTimeRanges(start, end) {
|
||||
const duration = this.engine.duration
|
||||
if (!duration || Number.isNaN(duration) || end <= 0) {
|
||||
return { length: 0, start: () => 0, end: () => 0 }
|
||||
}
|
||||
return {
|
||||
length: 1,
|
||||
start: () => start,
|
||||
end: () => end,
|
||||
}
|
||||
createTimeRanges(start: number, end: number): TimeRanges {
|
||||
return timeRanges(this.engine.duration, start, end)
|
||||
}
|
||||
|
||||
// Playback state
|
||||
@@ -148,7 +146,7 @@ export default class VideoShim {
|
||||
return this._playbackRate
|
||||
}
|
||||
|
||||
set playbackRate(v) {
|
||||
set playbackRate(v: unknown) {
|
||||
const rate = Number(v)
|
||||
if (Number.isNaN(rate) || rate <= 0)
|
||||
return
|
||||
@@ -163,8 +161,8 @@ export default class VideoShim {
|
||||
return this._volume
|
||||
}
|
||||
|
||||
set volume(v) {
|
||||
this._volume = clamp(v, 0, 1)
|
||||
set volume(v: unknown) {
|
||||
this._volume = clampVolume(v)
|
||||
this._muted = false
|
||||
this.engine.setVolume(this._volume, this._muted)
|
||||
this.events.emit('volumechange')
|
||||
@@ -174,7 +172,7 @@ export default class VideoShim {
|
||||
return this._muted
|
||||
}
|
||||
|
||||
set muted(v) {
|
||||
set muted(v: unknown) {
|
||||
this._muted = !!v
|
||||
this.engine.setVolume(this._volume, this._muted)
|
||||
this.events.emit('volumechange')
|
||||
@@ -198,11 +196,11 @@ export default class VideoShim {
|
||||
return this.engine.getHlsState()
|
||||
}
|
||||
|
||||
switchM3u8Quality(value) {
|
||||
switchM3u8Quality(value: unknown) {
|
||||
return this.engine.selectHlsQuality(value)
|
||||
}
|
||||
|
||||
switchM3u8Audio(value) {
|
||||
switchM3u8Audio(value: unknown) {
|
||||
return this.engine.selectHlsAudio(value)
|
||||
}
|
||||
|
||||
@@ -220,7 +218,7 @@ export default class VideoShim {
|
||||
return this.option.poster || ''
|
||||
}
|
||||
|
||||
set poster(v) {
|
||||
set poster(v: string) {
|
||||
this.option.poster = v
|
||||
}
|
||||
|
||||
@@ -228,52 +226,52 @@ export default class VideoShim {
|
||||
return this.option.autoplay || false
|
||||
}
|
||||
|
||||
set autoplay(v) {}
|
||||
set autoplay(v: unknown) {}
|
||||
|
||||
get loop() {
|
||||
return this.option.loop || false
|
||||
}
|
||||
|
||||
set loop(v) {}
|
||||
set loop(v: unknown) {}
|
||||
|
||||
get controls() {
|
||||
return false
|
||||
}
|
||||
|
||||
set controls(v) {}
|
||||
set controls(v: unknown) {}
|
||||
|
||||
get playsInline() {
|
||||
return true
|
||||
}
|
||||
|
||||
set playsInline(v) {}
|
||||
set playsInline(v: unknown) {}
|
||||
|
||||
get crossOrigin() {
|
||||
return this.option.crossOrigin || ''
|
||||
}
|
||||
|
||||
set crossOrigin(v) {}
|
||||
set crossOrigin(v: unknown) {}
|
||||
|
||||
get preload() {
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
set preload(v) {}
|
||||
set preload(v: unknown) {}
|
||||
|
||||
get defaultMuted() {
|
||||
return false
|
||||
}
|
||||
|
||||
set defaultMuted(v) {}
|
||||
set defaultMuted(v: unknown) {}
|
||||
|
||||
get defaultPlaybackRate() {
|
||||
return 1
|
||||
}
|
||||
|
||||
set defaultPlaybackRate(v) {}
|
||||
set defaultPlaybackRate(v: unknown) {}
|
||||
|
||||
// Methods
|
||||
canPlayType(_type) {
|
||||
canPlayType(_type: string) {
|
||||
return 'maybe'
|
||||
}
|
||||
|
||||
@@ -281,29 +279,15 @@ export default class VideoShim {
|
||||
return this.canvas.getBoundingClientRect()
|
||||
}
|
||||
|
||||
requestVideoFrameCallback(callback) {
|
||||
const id = requestAnimationFrame((time) => {
|
||||
callback(time, {
|
||||
presentationTime: this.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: this.engine.videoWidth,
|
||||
height: this.engine.videoHeight,
|
||||
mediaTime: this.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0,
|
||||
})
|
||||
})
|
||||
return id
|
||||
requestVideoFrameCallback(callback: VideoFrameRequestCallback): number {
|
||||
return requestFrame(this, callback)
|
||||
}
|
||||
|
||||
cancelVideoFrameCallback(id) {
|
||||
cancelAnimationFrame(id)
|
||||
cancelVideoFrameCallback(id: number): void {
|
||||
cancelFrame(this, id)
|
||||
}
|
||||
|
||||
setAttribute(name, value) {
|
||||
setAttribute(name: string, value: unknown) {
|
||||
if (name === 'src') {
|
||||
this.src = value
|
||||
}
|
||||
@@ -317,11 +301,18 @@ export default class VideoShim {
|
||||
this.muted = true
|
||||
}
|
||||
else {
|
||||
this.canvas.setAttribute(name, value)
|
||||
Reflect.apply(this.canvas.setAttribute, this.canvas, [name, value])
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.engine.destroy()
|
||||
destroy(): void {
|
||||
if (!closeFrames(this))
|
||||
return
|
||||
try {
|
||||
this.engine.destroy()
|
||||
}
|
||||
finally {
|
||||
this.events.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import type factory from '../types/artplayer-proxy-mediabunny'
|
||||
import type EventTarget from './EventTarget'
|
||||
import type { MediaEvent } from './EventTarget'
|
||||
import type { getHlsState } from './hls-state'
|
||||
|
||||
export type ProxyOptions = NonNullable<Parameters<typeof factory>[0]>
|
||||
export type HlsState = Awaited<ReturnType<typeof getHlsState>>
|
||||
|
||||
export interface ShimHost {
|
||||
constructor: { config: { events: readonly string[] } }
|
||||
option: { url?: unknown }
|
||||
emit: (type: string, event: MediaEvent) => unknown
|
||||
}
|
||||
|
||||
export interface EngineOptions {
|
||||
canvas: HTMLCanvasElement
|
||||
ctx: CanvasRenderingContext2D | null
|
||||
events: EventTarget
|
||||
option?: ProxyOptions
|
||||
}
|
||||
|
||||
// Transitional boundary for the coordinator; replaced by its strict implementation in MB-04.
|
||||
export interface EnginePort {
|
||||
readonly currentTime: number
|
||||
readonly duration: number
|
||||
readonly videoWidth: number
|
||||
readonly videoHeight: number
|
||||
paused: boolean
|
||||
ended: boolean
|
||||
seeking: boolean
|
||||
readyState: number
|
||||
networkState: number
|
||||
error: { code: number, message: string } | null
|
||||
play: () => Promise<void>
|
||||
pause: () => void
|
||||
load: (source: unknown) => Promise<void>
|
||||
seek: (time: number) => Promise<void>
|
||||
destroy: () => void
|
||||
setVolume: (volume: number, muted: boolean) => void
|
||||
setPlaybackRate: (rate: number) => void
|
||||
getHlsState: () => Promise<HlsState>
|
||||
selectHlsQuality: (value: unknown) => Promise<void>
|
||||
selectHlsAudio: (value: unknown) => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { EnginePort } from './engine-types'
|
||||
|
||||
interface FrameOwner {
|
||||
engine: Pick<EnginePort, 'currentTime' | 'videoWidth' | 'videoHeight'>
|
||||
}
|
||||
|
||||
interface FrameState {
|
||||
closed: boolean
|
||||
frames: Set<number>
|
||||
}
|
||||
|
||||
// Keep resource bookkeeping off the observable shim own-property/canvas surface.
|
||||
const states = new WeakMap<FrameOwner, FrameState>()
|
||||
function state(owner: FrameOwner): FrameState {
|
||||
let value = states.get(owner)
|
||||
if (!value) {
|
||||
value = { closed: false, frames: new Set() }
|
||||
states.set(owner, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function requestFrame(owner: FrameOwner, callback: VideoFrameRequestCallback): number {
|
||||
const resource = state(owner)
|
||||
if (resource.closed)
|
||||
return 0
|
||||
const id = requestAnimationFrame((time) => {
|
||||
resource.frames.delete(id)
|
||||
if (resource.closed)
|
||||
return
|
||||
callback(time, {
|
||||
presentationTime: owner.engine.currentTime,
|
||||
expectedDisplayTime: time + 16.6,
|
||||
width: owner.engine.videoWidth,
|
||||
height: owner.engine.videoHeight,
|
||||
mediaTime: owner.engine.currentTime,
|
||||
presentedFrames: 0,
|
||||
processingDuration: 0,
|
||||
captureTime: time,
|
||||
receiveTime: time,
|
||||
rtpTimestamp: 0,
|
||||
})
|
||||
})
|
||||
resource.frames.add(id)
|
||||
return id
|
||||
}
|
||||
|
||||
export function cancelFrame(owner: FrameOwner, id: number): void {
|
||||
state(owner).frames.delete(id)
|
||||
cancelAnimationFrame(id)
|
||||
}
|
||||
|
||||
export function closeFrames(owner: FrameOwner): boolean {
|
||||
const resource = state(owner)
|
||||
if (resource.closed)
|
||||
return false
|
||||
resource.closed = true
|
||||
for (const id of resource.frames) cancelAnimationFrame(id)
|
||||
resource.frames.clear()
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export function clampVolume(value: unknown): number {
|
||||
return Math.max(0, Math.min(1, Number(value) || 0))
|
||||
}
|
||||
|
||||
export function timeRanges(duration: number, start: number, end: number): TimeRanges {
|
||||
if (!duration || Number.isNaN(duration) || end <= 0)
|
||||
return { length: 0, start: () => 0, end: () => 0 }
|
||||
return { length: 1, start: () => start, end: () => end }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"sourceCommit": "adba8a3d127472ad7ae146e64dcb67568639520e",
|
||||
"sourceSha256LF": {
|
||||
"packages/artplayer-proxy-mediabunny/src/MediaBunnyEngine.js": "5ce3bc85572121ec9068fbc051ac4845d3a3991e670e10dafcaede1c1609f1fb",
|
||||
"packages/artplayer-proxy-mediabunny/src/VideoShim.js": "f01c954e8a98fb70138dded78b99eb2765f5cd1e8a98dafbb35ffbf8f7741dbb",
|
||||
"packages/artplayer-proxy-mediabunny/src/EventTarget.js": "c1854510af5a37301b4bd3b1fbcc28f7e0f105f50cb67324c5c05a76f2d054fb",
|
||||
"packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js": "f9d5571f75f47ee1714d04c951836c1a6c903f4e465fab2a267dcb4948c0de7b"
|
||||
},
|
||||
"scope": "MB-03 completion before MB-04 coordination edits; not a published release"
|
||||
}
|
||||
@@ -0,0 +1,708 @@
|
||||
{
|
||||
"task": "PKG-MB-04",
|
||||
"status": "doing",
|
||||
"checkpoint": "Shim/event/RAF lifecycle only; coordinator remains incomplete",
|
||||
"parentCommit": "adba8a3d127472ad7ae146e64dcb67568639520e",
|
||||
"sourceSha256LF": {
|
||||
"packages/artplayer-proxy-mediabunny/src/AudioEngine.js": "910d6831372e4895d55dec78010f281a199bc88f99735630a5a3b621f192ac35",
|
||||
"packages/artplayer-proxy-mediabunny/src/engine-types.ts": "815e99c75c63abe126dceddbde6d79f6cd5fc435ce2de76d508d5ea407419ffa",
|
||||
"packages/artplayer-proxy-mediabunny/src/EventTarget.ts": "4cea57e59b0a1118744a55b0a87c1df28cb0d69c26c50f9275039fe7540c9720",
|
||||
"packages/artplayer-proxy-mediabunny/src/hls-state.ts": "ff9ffd742f9763349df1f50eac6b6bd488a80d05aaab8ca198f387f0d17d5e25",
|
||||
"packages/artplayer-proxy-mediabunny/src/index.js": "4e1be8937908ff6e6ef8a4e863e22b4468312cf31d66ac299322e475757773bb",
|
||||
"packages/artplayer-proxy-mediabunny/src/input.ts": "d25b1c4ee4aee358c86fd8c554b8b36bfc23fdbd3c27fa21d2cd7e812c98bf9c",
|
||||
"packages/artplayer-proxy-mediabunny/src/load-session.ts": "560fbd5935fc787bde5f413fcaa41b8829506179c5328ce3cb51e734bbba8cec",
|
||||
"packages/artplayer-proxy-mediabunny/src/m3u8.js": "4639f09e0c743dc2b24078f48ca1e58eaaedaa3f5e6b863583cbd4bc4a7d3142",
|
||||
"packages/artplayer-proxy-mediabunny/src/media.ts": "e745cd8e750e05d0fcfa4bc588f7eb5a5576f3be20f5e67b65b704f57d539ae9",
|
||||
"packages/artplayer-proxy-mediabunny/src/MediaBunnyEngine.d.ts": "9c1e2b3cf9ffe5aa1c50a0e6bc13c1e986b8178440393efbaa22d6013b850bca",
|
||||
"packages/artplayer-proxy-mediabunny/src/MediaBunnyEngine.js": "5ce3bc85572121ec9068fbc051ac4845d3a3991e670e10dafcaede1c1609f1fb",
|
||||
"packages/artplayer-proxy-mediabunny/src/preflight.ts": "d296234312f23b97a1e0a054f1d7dbbc988d5240c5fd5089c36df1677c7b7348",
|
||||
"packages/artplayer-proxy-mediabunny/src/shim-frames.ts": "172b222f997a711c2cc7741f189f05127a4ac5099c1cbe2729e903b36b4ce24a",
|
||||
"packages/artplayer-proxy-mediabunny/src/shim-values.ts": "a93ee17e87cc7db24f4b02d866af8af206d3007d01def446906fb769c20de17d",
|
||||
"packages/artplayer-proxy-mediabunny/src/tracks.ts": "6da68530c1fc7105ab17ed742d87d93e84447e822e84386591f9c3e6d0bec733",
|
||||
"packages/artplayer-proxy-mediabunny/src/VideoEngine.js": "5ac862b0fd9c196dc6656f8908a214c1b3a0a53a7e85ff845d9068888831986b",
|
||||
"packages/artplayer-proxy-mediabunny/src/VideoShim.ts": "c3dab22ac6fe89a19b6f1d7f786a4e087bc8be69888836f2b136d04734b5f9df",
|
||||
"packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.legacy.js": "4e284e02970cf2f42b809ade1210ebe69d40c329597a0d01e0d25ad0e2a326a3",
|
||||
"packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.mjs": "a2e60ecdf57b7dfd4403674ae9ed704c91b181fc197611e213b053ff3c949003",
|
||||
"packages/artplayer-proxy-mediabunny/ARCHITECTURE.md": "af6520c10d7ba23110eb42d44b3b023d1e1c5a0fc659d51f720bd8903a18b98d",
|
||||
"package.json": "32ed93187014731cf8a28ac4b759260486fa058a02dce397d08a65964ae9de20",
|
||||
"test/mediabunny-shim.test.js": "2675b3b6575c26505d1a3646e96a7abffe909db07416a046dc105ca30635484e",
|
||||
"test/browser/mediabunny-shim.spec.js": "92b10cc955b0ab397eede78a1af386ae02ef92a1c0631ccddc4f539f4e4532cd"
|
||||
},
|
||||
"node": {
|
||||
"total": 68,
|
||||
"shim": 12,
|
||||
"sameShimAssertionsOnPreviousMain": {
|
||||
"passed": 9,
|
||||
"failed": 3
|
||||
},
|
||||
"mainShimAndLoad": 32,
|
||||
"legacyShimAndLoad": 32,
|
||||
"artifactScope": "Each 32-case main/legacy command contains 28 candidate artifact assertions and four frozen normal-event controls."
|
||||
},
|
||||
"browser": {
|
||||
"total": 39,
|
||||
"outcomes": {
|
||||
"native-input-playback-and-seek": 10,
|
||||
"candidate-trackless-readiness-gap": 2,
|
||||
"unsupported-capability-control": 6,
|
||||
"native-pending-stream-cancellation": 9,
|
||||
"native-synthetic-raf": 9,
|
||||
"native-mp4-playback-observed": 2,
|
||||
"verified-unsupported-capability-control": 1
|
||||
},
|
||||
"records": [
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "webm",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "blob",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "stream",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": true
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "audio",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "hls",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "trackless",
|
||||
"outcome": "candidate-trackless-readiness-gap",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "webm",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "blob",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "stream",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": true
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "audio",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "hls",
|
||||
"outcome": "native-input-playback-and-seek",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "trackless",
|
||||
"outcome": "candidate-trackless-readiness-gap",
|
||||
"cleanup": {
|
||||
"audioContext": "closed",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "webm",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "blob",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "stream",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": true
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "audio",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "hls",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"input": "trackless",
|
||||
"outcome": "unsupported-capability-control",
|
||||
"cleanup": {
|
||||
"audioContext": "not-created",
|
||||
"pendingFrames": 0,
|
||||
"streamCancelled": false
|
||||
},
|
||||
"scope": "Identified proxy with native browser media and controlled ArtPlayer host using actual core utilities/config; frame timestamps sampled against the SDK audio clock, not acoustic/long-run AV-sync acceptance. Cleanup checks settled playback, not pending-operation races. Candidate trackless readiness remains MB-READY-01, not release acceptance."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "destroy",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "source",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old",
|
||||
"new"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "timeout",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "destroy",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "source",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old",
|
||||
"new"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "timeout",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "destroy",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "source",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old",
|
||||
"new"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"ending": "timeout",
|
||||
"outcome": "native-pending-stream-cancellation",
|
||||
"cancellations": [
|
||||
"old"
|
||||
],
|
||||
"scope": "Real browser ReadableStream and actual SDK Input disposal before first media bytes; real timeout; controlled ArtPlayer host; does not assert decoder, late frame or audio-node cleanup."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "callback",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "first",
|
||||
"time": "number",
|
||||
"mediaTime": 0,
|
||||
"presentedFrames": 0
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "destroy",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "independent",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "second"
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "callback",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "first",
|
||||
"time": "number",
|
||||
"mediaTime": 0,
|
||||
"presentedFrames": 0
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "destroy",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "independent",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "second"
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "callback",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "first",
|
||||
"time": "number",
|
||||
"mediaTime": 0,
|
||||
"presentedFrames": 0
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "destroy",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"scenario": "independent",
|
||||
"outcome": "native-synthetic-raf",
|
||||
"result": {
|
||||
"idType": "number",
|
||||
"calls": [
|
||||
{
|
||||
"owner": "second"
|
||||
}
|
||||
],
|
||||
"listeners": 0
|
||||
},
|
||||
"scope": "Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed."
|
||||
},
|
||||
{
|
||||
"project": "chromium",
|
||||
"browser": "153.0.8010.12",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"outcome": "native-mp4-playback-observed",
|
||||
"scope": "Two actual published proxies with actual core5.4.0; native MP4 pixels/clock or explicitly unsupported Windows WebKit APIs. No candidate, HLS or AV-sync acceptance."
|
||||
},
|
||||
{
|
||||
"project": "firefox",
|
||||
"browser": "155.0",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"outcome": "native-mp4-playback-observed",
|
||||
"scope": "Two actual published proxies with actual core5.4.0; native MP4 pixels/clock or explicitly unsupported Windows WebKit APIs. No candidate, HLS or AV-sync acceptance."
|
||||
},
|
||||
{
|
||||
"project": "webkit",
|
||||
"browser": "26.6",
|
||||
"platform": "win32",
|
||||
"implementation": "candidate-artifact",
|
||||
"sha256": "8ac3fb3531f109475adeb0230f155113e6a0210e32c0be08527be4b2772e63a3",
|
||||
"outcome": "verified-unsupported-capability-control",
|
||||
"scope": "Two actual published proxies with actual core5.4.0; native MP4 pixels/clock or explicitly unsupported Windows WebKit APIs. No candidate, HLS or AV-sync acceptance."
|
||||
}
|
||||
],
|
||||
"report": "refactor/.cache/mb04-shim-results/report.json",
|
||||
"reportSha256": "07856e69bad4f4c33f6a06506a3ca21caa23c5fc0e915a166825e8596a56865f",
|
||||
"results": "refactor/.cache/mb04-shim-results/results"
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"command": "yarn test:mediabunny",
|
||||
"result": "pass",
|
||||
"tests": 68,
|
||||
"log": "refactor/.cache/mb04-shim-tests-final.log"
|
||||
},
|
||||
{
|
||||
"command": "node --test test/mediabunny-shim.test.js with adba8a3d main",
|
||||
"result": "expected historical failures",
|
||||
"passed": 9,
|
||||
"failed": 3,
|
||||
"log": "refactor/.cache/mb04-shim-frozen.log"
|
||||
},
|
||||
{
|
||||
"command": "package strict typecheck and focused eslint --no-fix",
|
||||
"result": "pass",
|
||||
"packageStrictProductionModules": 11,
|
||||
"scope": "Coordinator JS remains behind an explicit declaration boundary"
|
||||
},
|
||||
{
|
||||
"command": "yarn build artplayer-proxy-mediabunny",
|
||||
"result": "pass",
|
||||
"formats": [
|
||||
"main",
|
||||
"legacy",
|
||||
"esm"
|
||||
],
|
||||
"log": "refactor/.cache/mb04-shim-build.log"
|
||||
},
|
||||
{
|
||||
"command": "main/legacy shim and load regressions",
|
||||
"result": "pass",
|
||||
"eachRun": 32,
|
||||
"candidateArtifactCasesPerRun": 28,
|
||||
"frozenControlsPerRun": 4
|
||||
},
|
||||
{
|
||||
"command": "four MediaBunny browser specs with built main and ARTPLAYER_MB_BROWSER_CANDIDATE=1",
|
||||
"result": "pass",
|
||||
"tests": 39,
|
||||
"log": "refactor/.cache/mb04-shim-browser.log"
|
||||
},
|
||||
{
|
||||
"command": "yarn ci:check",
|
||||
"result": "pass",
|
||||
"tests": {
|
||||
"unit": 1074,
|
||||
"engineering": 14,
|
||||
"baseline": 141,
|
||||
"mainTotal": 1229,
|
||||
"repeatedContractExecutions": 44
|
||||
},
|
||||
"strictProductionTypeScriptFiles": 301,
|
||||
"log": "refactor/.cache/mb04-shim-ci.log"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"PKG-MB-04 is still doing. Readiness/play/seek coordination remains; event silence alone is not evidence that pending engine operations stopped.",
|
||||
"Coordinator .d.ts is a temporary bridge, not typechecking of its JS implementation.",
|
||||
"Synthetic RAF is not decoded video frame evidence.",
|
||||
"Trackless and Windows WebKit capability cases keep their unresolved scope; no release acceptance."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# PKG-MB-04 检查点:VideoShim 与事件资源
|
||||
|
||||
任务仍doing;本检查点不是整个主协调器迁移完成。进入04前的实际源码和main归属为
|
||||
adba8a3d,冻结于[接续基线](../baselines/mb-coordination-source.json)。
|
||||
|
||||
VideoShim/EventTarget转为strict TS,抽出shim-values纯值与shim-frames资源模块,
|
||||
engine-types描述临时协调器边界。MediaBunnyEngine.d.ts仅暂时连接JS协调器;后续必须
|
||||
由实际MediaBunnyEngine.ts替代,不能把这份声明当作实现已类型检查。
|
||||
生产TS增至11个(全项目301),仍有五个本包JS文件。
|
||||
|
||||
## 已复现并修正
|
||||
|
||||
1. destroy后保留的synthetic RAF仍会调用用户回调;现回收所有本实例ID(含0),
|
||||
即使已出队的回调函数晚执行也无副作用。销毁后不再分配新的RAF。
|
||||
2. destroy未释放事件监听器,当前emit的后续监听器还能执行;现terminal状态清空Map,
|
||||
并阻止当前派发后续回调。正常forEach动态数组语义、重复listener和首次删除保留。
|
||||
3. engine.destroy抛错时事件/RAF仍残留;现frame状态先关闭、事件由finally关闭,
|
||||
原异常仍向调用方传播;再次destroy不重复调用已失败的清理。
|
||||
|
||||
Canvas和shim的own/prototype属性顺序、描述符、每次读取重新绑定的方法、原生canvas方法优先,
|
||||
以及play返回原Promise、pause/load同步void等都有实际旧/新对象对照。
|
||||
初始volume不clamp、setter强制转换/取消mute、历史noop setter、synthetic TimeRanges和
|
||||
presentedFrames=0继续保留。资源表使用WeakMap,不新增被复制到canvas的内部字段。
|
||||
|
||||
## 检查范围
|
||||
|
||||
同一12项shim测试在adba8a3d实际main上9通过/3失败;候选12通过。
|
||||
四组正常契约同时比较原冻结1.2.0与候选;本轮共68项包专项,包含原24历史、20加载和12输入。
|
||||
新增用例接入test:unit与test:mediabunny。三格式构建,main/legacy各32项加载+shim检查。
|
||||
浏览器在实际RAF上检查正常callback、destroy和另一实例不受影响;不把synthetic callback当作解码帧。
|
||||
最终验证及产物哈希见[检查点证据](../baselines/mb-shim-checkpoint.json)。
|
||||
|
||||
## 接续工作
|
||||
|
||||
立即接着04的主协调器:重复/无轨道就绪、错误后的metadata回写、play拒绝状态恢复、
|
||||
pending play遇到pause/destroy、连续seek及resume意图。事件关闭只消除了销毁后的事件出口,
|
||||
不证明旧Engine没有继续video.start、audio.play或改状态;不能用事件已经静默来掩盖这些未修复问题。
|
||||
原MB-LIFE-01/MB-READY-01保持open。后续05/06仍处理底层帧与音频操作。
|
||||
|
||||
包内ARCHITECTURE.md同步当前地图,回退本检查点commit即可恢复04前shim;不要改动历史冻结输入。
|
||||
本地checkpoint不计为任务完成,不推送、不发布。
|
||||
|
||||
最终完整CI1229项(1074单元+14工程+141基线)通过,另44重复契约;301生产TS。
|
||||
main/legacy各32项中28项实际候选断言、4项冻结正常事件对照。39项浏览器无失败/重试/跳过,
|
||||
其中12播放、9Stream取消、9synthetic RAF、7能力对照及2未关闭trackless就绪。
|
||||
全部最终源码/测试/产物及报告哈希已核验,docs/compiled与dist三格式逐字节一致。
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 115 / doing 7 / blocked 0 / done 100 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 114 / doing 8 / blocked 0 / done 100 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
| PKG-MB-01 | artplayer-proxy-mediabunny<br>核对 shim 与媒体契约 | BASE-05 | 全部 option、art.mediabunny、canvas/shim 属性及事件表 | 公开暴露成员和实际发布依赖/资源范围明确;核实 third-party.json 所记录依赖 MPL-2.0 元数据与实际 bundle 的许可/通知范围,不从根 MIT 推断 | H | done |
|
||||
| PKG-MB-02 | artplayer-proxy-mediabunny<br>建立真实媒体与事件基线 | PKG-MB-01, ENG-05, ENG-08 | MP4/WebM/HLS/Blob/Stream、seek/错误/轨道切换样本 | ready 事件顺序、音画同步、超时和释放可重跑 | H | done |
|
||||
| PKG-MB-03 | artplayer-proxy-mediabunny<br>整理 input 与加载取消 | PKG-MB-02, CORE-09 | input.js 源检测、输入资源、Range 与取消 | HLS 检测在 input 边界,旧来源类型和超时保持 | H | done |
|
||||
| PKG-MB-04 | artplayer-proxy-mediabunny<br>整理 VideoShim/EventTarget/Engine 协调 | PKG-MB-03, CORE-06, CORE-11 | shim 描述符、事件桥、加载状态和操作隔离 | 公开属性/同步异步返回与事件重入顺序保持 | H | todo |
|
||||
| PKG-MB-04 | artplayer-proxy-mediabunny<br>整理 VideoShim/EventTarget/Engine 协调 | PKG-MB-03, CORE-06, CORE-11 | shim 描述符、事件桥、加载状态和操作隔离 | 公开属性/同步异步返回与事件重入顺序保持 | H | doing |
|
||||
| PKG-MB-05 | artplayer-proxy-mediabunny<br>整理视频解码、seek 和帧释放 | PKG-MB-04 | VideoEngine 调度、队列、晚帧处理和资源释放 | 暂停/seek/切源无旧帧覆盖,默认丢帧策略不变 | H | todo |
|
||||
| PKG-MB-06 | artplayer-proxy-mediabunny<br>整理音频解码、时钟与同步 | PKG-MB-04 | AudioEngine 时钟、缓冲和 AudioContext 归属 | AV sync、倍速、无音轨、静音/音量和暂停恢复通过 | H | todo |
|
||||
| PKG-MB-07 | artplayer-proxy-mediabunny<br>整理 HLS 配对轨道与 selector | PKG-MB-05, PKG-MB-06, CORE-14 | m3u8 配对、质量/音频选择及拓扑清理 | 实际选择高亮、切到无轨道来源清理、无重复 readiness | H | todo |
|
||||
@@ -523,4 +523,5 @@
|
||||
- PKG-MB-01: [记录](baselines/mb-release.json) [记录](baselines/mb-surface.json) [记录](baselines/mb-contract.md) [记录](baselines/mb-contract-validation.json) [记录](mb-validation.md) [记录](changes/2026-09-12-PKG-MB-01-contract.md)
|
||||
- PKG-MB-02: [记录](changes/2026-09-12-PKG-MB-02-checkpoint.md) [记录](baselines/mb-media-checkpoint.json) [记录](mb-validation.md) [记录](changes/2026-09-12-PKG-MB-02-baseline.md) [记录](baselines/mb-behavior-validation.json)
|
||||
- PKG-MB-03: [记录](changes/2026-09-12-PKG-MB-03-input.md) [记录](baselines/mb-input-validation.json)
|
||||
- PKG-MB-04: [记录](baselines/mb-coordination-source.json) [记录](changes/2026-09-12-PKG-MB-04-shim-checkpoint.md) [记录](baselines/mb-shim-checkpoint.json)
|
||||
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md)
|
||||
@@ -1,5 +1,17 @@
|
||||
# 进度与证据
|
||||
|
||||
## 当前检查点:PKG-MB-04 VideoShim、事件与RAF资源
|
||||
|
||||
新增五个生产TS模块,保持shim/canvas布局和正常事件/Promise/属性契约;修复销毁后的RAF、
|
||||
事件监听器和清理异常残留。同一12项shim断言在adba8a3d main上9通过/3失败,候选全通过。
|
||||
包专项68项通过;main/legacy各32项(28候选+4冻结正常对照),三格式构建通过。
|
||||
39项候选浏览器检查通过:12播放、9原生Stream取消、9synthetic RAF、7能力失败对照、
|
||||
2待修复无轨道就绪。完整CI1229项通过,另44重复契约,301生产TS,声明桥未计为JS实现迁移。
|
||||
见[检查点](changes/2026-09-12-PKG-MB-04-shim-checkpoint.md)及[证据](baselines/mb-shim-checkpoint.json)。
|
||||
MB-04保持doing;立即继续MediaBunnyEngine实际TS与readiness/play/seek协调。事件出口已关闭
|
||||
不代表底层晚操作已停止,MB-LIFE-01/MB-READY-01仍open。222项:100 done、8 doing、114 todo。
|
||||
本地checkpoint,不推送/发布;Canvas/Ambilight类型决定仍待确认。
|
||||
|
||||
## 最新完成:PKG-MB-03 输入、Range与加载取消
|
||||
|
||||
六个strict TS模块已接入生产,修复切源/销毁/超时后的待加载Input、HEAD及timer资源与陈旧回调。
|
||||
|
||||
+3
-2
@@ -3648,7 +3648,7 @@
|
||||
"PKG-MB-07",
|
||||
"PKG-MB-09"
|
||||
],
|
||||
"compatibleResolution": "Reproduce pending operations and destroy/source-switch races before migrating each owner; preserve public event and promise behavior while preventing stale resource use. MB-02 now reproduces late load rejection and uncancelled waiting/loadstart timers after destroy in all three historical implementations using a controlled host. Other interleavings and full-core native behavior are not yet verified. Expanded MB-02 baseline reproduces play-after-pause/destroy, rejected-play false state, seek-after-destroy and retained successful-load timeout timers in all three actual historical implementations; Range and active-timeout normal controls remain separate. MB-03 candidate owns pending Input before track selection and cancels load/HEAD/timers on source replacement, timeout and destroy; source/main/legacy regressions pass. Pending play/seek, late decoder work and full readiness coordination remain MB-04/05/06, so this risk stays open.",
|
||||
"compatibleResolution": "Reproduce pending operations and destroy/source-switch races before migrating each owner; preserve public event and promise behavior while preventing stale resource use. MB-02 now reproduces late load rejection and uncancelled waiting/loadstart timers after destroy in all three historical implementations using a controlled host. Other interleavings and full-core native behavior are not yet verified. Expanded MB-02 baseline reproduces play-after-pause/destroy, rejected-play false state, seek-after-destroy and retained successful-load timeout timers in all three actual historical implementations; Range and active-timeout normal controls remain separate. MB-03 candidate owns pending Input before track selection and cancels load/HEAD/timers on source replacement, timeout and destroy; source/main/legacy regressions pass. Pending play/seek, late decoder work and full readiness coordination remain MB-04/05/06, so this risk stays open. MB-04 shim checkpoint fixes synthetic RAF retention and terminal event-listener cleanup, including engine cleanup exceptions. Closing the event bridge does not prove pending engine operations stopped; coordination remains in progress.",
|
||||
"closureCriteria": "Historical failing interleavings and candidate cancellation/cleanup pass with real media and event evidence, including long playback and track topology.",
|
||||
"status": "open",
|
||||
"evidence": [
|
||||
@@ -3659,7 +3659,8 @@
|
||||
"refactor/baselines/mb-media-checkpoint.json",
|
||||
"refactor/changes/2026-09-12-PKG-MB-02-baseline.md",
|
||||
"refactor/baselines/mb-behavior-validation.json",
|
||||
"refactor/changes/2026-09-12-PKG-MB-03-input.md"
|
||||
"refactor/changes/2026-09-12-PKG-MB-03-input.md",
|
||||
"refactor/changes/2026-09-12-PKG-MB-04-shim-checkpoint.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+6
-2
@@ -3460,11 +3460,15 @@
|
||||
"CORE-06",
|
||||
"CORE-11"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "doing",
|
||||
"risk": "H",
|
||||
"deliverable": "shim 描述符、事件桥、加载状态和操作隔离",
|
||||
"acceptance": "公开属性/同步异步返回与事件重入顺序保持",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"baselines/mb-coordination-source.json",
|
||||
"changes/2026-09-12-PKG-MB-04-shim-checkpoint.md",
|
||||
"baselines/mb-shim-checkpoint.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-MB-05",
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { mbCandidate } from '../helpers/mediabunny.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const implementation = await mbCandidate()
|
||||
for (const scenario of ['callback', 'destroy', 'independent']) {
|
||||
test(`MediaBunny ${implementation.name}: native synthetic RAF ${scenario}`, async ({ page }, testInfo) => {
|
||||
await page.goto('/test/player.html?core=published')
|
||||
await page.addScriptTag({ content: `(() => { const module = { exports: {} }; const exports = module.exports;
|
||||
${implementation.code}; window.mbFactory = module.exports.default || module.exports; })();` })
|
||||
const result = await page.evaluate(async (scenario) => {
|
||||
function create() {
|
||||
const listeners = new Map()
|
||||
const host = {
|
||||
constructor: window.Artplayer,
|
||||
option: { url: '', autoSize: false },
|
||||
template: { $player: document.querySelector('.player') },
|
||||
on(name, callback) {
|
||||
const list = listeners.get(name) || []
|
||||
list.push(callback)
|
||||
listeners.set(name, list)
|
||||
},
|
||||
emit(name, ...args) {
|
||||
for (const callback of listeners.get(name) || []) callback(...args)
|
||||
},
|
||||
}
|
||||
const canvas = window.mbFactory()(host)
|
||||
return { host, canvas }
|
||||
}
|
||||
const first = create()
|
||||
const second = scenario === 'independent' ? create() : null
|
||||
const calls = []
|
||||
const id = first.canvas.requestVideoFrameCallback((time, metadata) => calls.push({ owner: 'first', time: typeof time, mediaTime: metadata.mediaTime, presentedFrames: metadata.presentedFrames }))
|
||||
second?.canvas.requestVideoFrameCallback(() => calls.push({ owner: 'second' }))
|
||||
if (scenario !== 'callback') {
|
||||
first.host.emit('destroy')
|
||||
first.canvas.requestVideoFrameCallback(() => calls.push({ owner: 'after-destroy' }))
|
||||
}
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
first.host.emit('destroy')
|
||||
second?.host.emit('destroy')
|
||||
return { idType: typeof id, calls, listeners: first.canvas.events.listeners.size }
|
||||
}, scenario)
|
||||
expect(result.idType).toBe('number')
|
||||
expect(result.listeners).toBe(0)
|
||||
expect(result.calls).toEqual(scenario === 'callback' ? [{ owner: 'first', time: 'number', mediaTime: 0, presentedFrames: 0 }] : scenario === 'independent' ? [{ owner: 'second' }] : [])
|
||||
await testInfo.attach('mediabunny-shim-frames', { contentType: 'application/json', body: JSON.stringify({ implementation: implementation.name, sha256: hash(implementation.code), scenario, result, scope: 'Real browser requestAnimationFrame and actual proxy instances; synthetic metadata is preserved, no decoding or video frame accuracy is claimed.' }) })
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Exercise actual proxy instances, including historical event semantics.
|
||||
import test from 'node:test'
|
||||
import { mbCandidate, mbEnvironment, mbHistorical } from './helpers/mediabunny.js'
|
||||
|
||||
const candidate = await mbCandidate()
|
||||
const frozen = (await mbHistorical()).find(item => item.name === 'frozen-workspace')
|
||||
function environment(implementation = candidate, option = {}) {
|
||||
const env = mbEnvironment(implementation)
|
||||
const canvas = env.factory(option)(env.art)
|
||||
return { ...env, canvas, shim: env.art.mediabunny, events: canvas.events }
|
||||
}
|
||||
|
||||
test('MediaBunny candidate retains exact shim own/prototype surface, canvas descriptors and bound forwarding', () => {
|
||||
const old = environment(frozen)
|
||||
const next = environment()
|
||||
assert.deepEqual(Object.keys(next.canvas), Object.keys(old.canvas))
|
||||
assert.deepEqual(Object.getOwnPropertyNames(next.shim), Object.getOwnPropertyNames(old.shim))
|
||||
assert.deepEqual(Object.getOwnPropertyNames(Object.getPrototypeOf(next.shim)), Object.getOwnPropertyNames(Object.getPrototypeOf(old.shim)))
|
||||
for (const name of Object.keys(next.canvas)) {
|
||||
const a = Object.getOwnPropertyDescriptor(next.canvas, name)
|
||||
const b = Object.getOwnPropertyDescriptor(old.canvas, name)
|
||||
assert.deepEqual([a.enumerable, a.configurable, a.writable, typeof a.get, typeof a.set], [b.enumerable, b.configurable, b.writable, typeof b.get, typeof b.set], name)
|
||||
}
|
||||
assert.notEqual(next.canvas.play, next.canvas.play)
|
||||
assert.throws(() => Reflect.set(next.canvas, 'duration', 3), { name: 'TypeError' })
|
||||
assert.equal(next.canvas.getContext('2d'), next.context)
|
||||
next.canvas.setAttribute('src', 'native-only.mp4')
|
||||
assert.equal(next.canvas.src, null)
|
||||
assert.deepEqual(next.nativeCalls.at(-1), ['attribute', 'src', 'native-only.mp4'])
|
||||
})
|
||||
|
||||
for (const implementation of [frozen, candidate]) {
|
||||
test(`MediaBunny ${implementation.name}: event dispatch retains duplicates, first removal, detail identity and live-array mutation`, () => {
|
||||
const env = environment(implementation)
|
||||
const calls = []
|
||||
const detail = { value: 7 }
|
||||
const duplicate = (event) => {
|
||||
assert(event instanceof Event)
|
||||
assert.equal(event.detail, detail)
|
||||
calls.push('duplicate')
|
||||
}
|
||||
env.events.addEventListener('custom', duplicate)
|
||||
env.events.addEventListener('custom', duplicate)
|
||||
assert.equal(env.events.removeEventListener('custom', duplicate), undefined)
|
||||
env.events.addEventListener('custom', () => {
|
||||
calls.push('mutate')
|
||||
env.events.removeEventListener('custom', duplicate)
|
||||
env.events.addEventListener('custom', () => calls.push('new'))
|
||||
})
|
||||
env.events.addEventListener('custom', () => calls.push('last'))
|
||||
assert.equal(env.events.emit('custom', detail), undefined)
|
||||
assert.deepEqual(calls, ['duplicate', 'mutate', 'new'])
|
||||
})
|
||||
|
||||
test(`MediaBunny ${implementation.name}: event errors propagate and explicit nested emits preserve ordering`, () => {
|
||||
const env = environment(implementation)
|
||||
const calls = []
|
||||
env.events.addEventListener('outer', () => {
|
||||
calls.push('outer')
|
||||
env.events.emit('inner')
|
||||
})
|
||||
env.events.addEventListener('inner', () => calls.push('inner'))
|
||||
env.events.addEventListener('outer', () => calls.push('tail'))
|
||||
env.events.emit('outer')
|
||||
assert.deepEqual(calls, ['outer', 'inner', 'tail'])
|
||||
const error = new Error('user callback')
|
||||
env.events.addEventListener('failure', () => {
|
||||
throw error
|
||||
})
|
||||
env.events.addEventListener('failure', () => calls.push('unreachable'))
|
||||
assert.throws(() => env.events.emit('failure'), item => item === error)
|
||||
assert.equal(calls.includes('unreachable'), false)
|
||||
})
|
||||
|
||||
test(`MediaBunny ${implementation.name}: coercion, no-op setters and pseudo ranges keep normal behavior`, () => {
|
||||
const option = { volume: 2, autoplay: true, loop: true, crossOrigin: 'anonymous', poster: 'first.png' }
|
||||
const env = environment(implementation, option)
|
||||
assert.equal(env.canvas.option, option)
|
||||
assert.equal(env.canvas.volume, 2)
|
||||
env.canvas.volume = '0.4'
|
||||
assert.equal(env.canvas.volume, 0.4)
|
||||
assert.equal(env.canvas.muted, false)
|
||||
env.canvas.muted = 'yes'
|
||||
assert.equal(env.canvas.muted, true)
|
||||
env.canvas.playbackRate = '2'
|
||||
env.canvas.playbackRate = 0
|
||||
assert.equal(env.canvas.playbackRate, 2)
|
||||
env.canvas.autoplay = false
|
||||
env.canvas.loop = false
|
||||
env.canvas.crossOrigin = 'use-credentials'
|
||||
assert.deepEqual([env.canvas.autoplay, env.canvas.loop, env.canvas.crossOrigin], [true, true, 'anonymous'])
|
||||
env.canvas.poster = 'next.png'
|
||||
assert.equal(option.poster, 'next.png')
|
||||
Object.defineProperty(env.canvas.engine, 'duration', { value: 12 })
|
||||
Object.defineProperty(env.canvas.engine, 'currentTime', { value: 3 })
|
||||
assert.equal(env.canvas.buffered.end(99), 12)
|
||||
assert.equal(env.canvas.played.end(-1), 3)
|
||||
assert.equal(env.canvas.seekable.start(99), 0)
|
||||
assert.equal(env.canvas.canPlayType('invalid/media'), 'maybe')
|
||||
})
|
||||
|
||||
test(`MediaBunny ${implementation.name}: public forwarding retains exact Promise identity and synchronous void methods`, async () => {
|
||||
const env = environment(implementation)
|
||||
const calls = []
|
||||
const result = Promise.resolve()
|
||||
env.canvas.engine.play = () => result
|
||||
env.canvas.engine.pause = () => calls.push('pause')
|
||||
env.canvas.engine.load = source => calls.push(['load', source])
|
||||
env.canvas.engine.seek = time => calls.push(['seek', time])
|
||||
assert.equal(env.canvas.play(), result)
|
||||
assert.equal(env.canvas.pause(), undefined)
|
||||
env.canvas.src = 'movie.mp4'
|
||||
assert.equal(env.canvas.load(), undefined)
|
||||
env.canvas.currentTime = '3.5'
|
||||
assert.deepEqual(calls, ['pause', ['load', 'movie.mp4'], ['load', 'movie.mp4'], ['seek', 3.5]])
|
||||
await result
|
||||
})
|
||||
}
|
||||
|
||||
test('MediaBunny candidate destroy cancels synthetic frame callbacks including handle zero', () => {
|
||||
const env = environment()
|
||||
let calls = 0
|
||||
const id = env.canvas.requestVideoFrameCallback(() => calls++)
|
||||
assert.equal(id, 0)
|
||||
const queued = env.frames.get(id)
|
||||
env.art.emit('destroy')
|
||||
assert.equal(env.frames.size, 0)
|
||||
queued(100)
|
||||
assert.equal(calls, 0)
|
||||
env.canvas.requestVideoFrameCallback(() => calls++)
|
||||
assert.equal(env.frames.size, 0)
|
||||
})
|
||||
|
||||
test('MediaBunny candidate teardown stops the current dispatch after a listener destroys the shim', () => {
|
||||
const env = environment()
|
||||
const calls = []
|
||||
env.events.addEventListener('custom', () => {
|
||||
calls.push('destroy')
|
||||
env.shim.destroy()
|
||||
})
|
||||
env.events.addEventListener('custom', () => calls.push('obsolete'))
|
||||
env.events.emit('custom')
|
||||
assert.deepEqual(calls, ['destroy'])
|
||||
assert.equal(env.events.listeners.size, 0)
|
||||
const cursor = env.emitted.length
|
||||
env.events.emit('volumechange')
|
||||
assert.equal(env.emitted.length, cursor)
|
||||
})
|
||||
|
||||
test('MediaBunny candidate teardown releases event and frame resources even when engine cleanup throws', () => {
|
||||
const env = environment()
|
||||
const failure = new Error('cleanup failed')
|
||||
env.canvas.requestVideoFrameCallback(() => {})
|
||||
env.canvas.engine.destroy = () => {
|
||||
throw failure
|
||||
}
|
||||
assert.throws(() => env.shim.destroy(), error => error === failure)
|
||||
assert.equal(env.events.listeners.size, 0)
|
||||
assert.equal(env.frames.size, 0)
|
||||
assert.doesNotThrow(() => env.shim.destroy())
|
||||
})
|
||||
Reference in new issue
Block a user