mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-17 10:01:20 -08:00
refactor(mediabunny): [PKG-MB-08] checkpoint typed entry and compatible media declarations
This commit is contained in:
1 parent
8a2b56e829
commit
5e28dfee95
39 files changed
+3007
-287
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";var e,t,i,r,a,s,n,o,c,l,u,h,d,f,m,p,g,k,y,b,w,T,S,v,P,I,x,C,B,E,_,R,F,A=e=>{throw TypeError(e)},M=(e,t,i)=>t.has(e)||A("Cannot "+i),D=(e,t,i)=>(M(e,t,"read from private field"),i?i.call(e):t.get(e)),N=(e,t,i)=>t.has(e)?A("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),O=(e,t,i,r)=>(M(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),L=(e,t,i)=>(M(e,t,"access private method"),i),z=(e,t,i,r)=>({set _(r){O(e,t,r,i)},get _(){return D(e,t,r)}});function U(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function q(e,t,i){const r={control:!1,setting:!1};let a=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function n(){a=null,U([()=>s("control"),()=>s("setting")])}return{update:function(o,c,l,u){if(!o)return void n();a=o;const h=()=>a===o&&l(),d=async e=>h()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html;c.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:d})):s("control"),h()&&(c.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:d})):s("setting"))},clear:n}}function W(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function H({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,a=0,s=0;const n=q(e,"mediabunny-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>'),o=q(e,"mediabunny-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>'),c=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;async function l(){if(r||t.engine.destroyed)return;const e=++a,s=t.engine.loadSeq,l=()=>e===a&&c(s);try{const e=await t.getM3u8State();if(!l())return;const r=i.m3u8?.quality||{},a=function(e,t,i){if(!t.control&&!t.setting||!e?.levels.length)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||`${e.height}P`),s=e.currentLevel?a(e.currentLevel):r,n=[];for(const l of e.levels){if(!i())return null;n.push({html:a(l),value:l.id,default:e.currentLevel?.id===l.id})}const o=new Map(e.levels.map(e=>[e.id,e.height])),c=W(n).sort((e,t)=>("auto"===t.value?0:o.get(t.value)||0)-("auto"===e.value?0:o.get(e.value)||0));return c.push({html:r,value:"auto",default:!e.currentLevel}),{title:t.title||"Quality",html:s,selector:c}}(e,r,l);if(!l())return;if(n.update(a,r,()=>c(s),(e,t)=>u("quality",e,t)),!l())return;const h=i.m3u8?.audio||{},d=function(e,t,i){if(!t.control&&!t.setting||!e||e.audios.length<2)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||e.lang||e.language),s=e.currentAudio?a(e.currentAudio):r,n=[];for(const c of e.audios){if(!i())return null;n.push({html:a(c),value:c.id,default:e.currentAudio?.id===c.id})}const o=W(n);return o.push({html:r,value:"auto",default:!e.currentAudio}),{title:t.title||"Audio",html:s,selector:o}}(e,h,l);l()&&o.update(d,h,()=>c(s),(e,t)=>u("audio",e,t))}catch(h){if(l())throw h}}async function u(i,r,n){const o=++s,u=t.engine.loadSeq;a++;const d=()=>o===s&&c(u);try{"quality"===i?await t.switchM3u8Quality(r.value):await t.switchM3u8Audio(r.value),d()&&(await l(),d()&&(e.notice.show=`${n}: ${r.html}`))}catch(f){if(d()&&(await h(),d()))throw f}return r.html}function h(){return l().catch(e=>console.warn("MediaBunny HLS menu update:",e))}function d(){a++,s++;try{U([n.clear,o.clear])}catch(e){console.warn("MediaBunny HLS menu cleanup:",e)}}return e.on("video:loadedmetadata",h),e.on("restart",h),e.on("video:loadstart",d),e.on("video:error",d),e.on("destroy",function t(){if(!r){r=!0,d();try{U([()=>e.off("video:loadedmetadata",h),()=>e.off("restart",h),()=>e.off("video:loadstart",d),()=>e.off("video:error",d),()=>e.off("destLine 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,i,r,a,s,n,o,c,l,u,h,d,f,m,p,g,k,y,b,w,T,S,v,P,I,x,C,B,E,_,R,F,A=e=>{throw TypeError(e)},M=(e,t,i)=>t.has(e)||A("Cannot "+i),D=(e,t,i)=>(M(e,t,"read from private field"),i?i.call(e):t.get(e)),N=(e,t,i)=>t.has(e)?A("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),O=(e,t,i,r)=>(M(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),L=(e,t,i)=>(M(e,t,"access private method"),i),z=(e,t,i,r)=>({set _(r){O(e,t,r,i)},get _(){return D(e,t,r)}});function U(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function q(e,t,i){const r={control:!1,setting:!1};let a=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function n(){a=null,U([()=>s("control"),()=>s("setting")])}return{update:function(o,c,l,u){if(!o)return void n();a=o;const h=()=>a===o&&l(),d=async e=>h()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html;c.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:d})):s("control"),h()&&(c.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:d})):s("setting"))},clear:n}}function W(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function H({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,a=0,s=0;const n=q(e,"mediabunny-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>'),o=q(e,"mediabunny-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>'),c=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;async function l(){if(r||t.engine.destroyed)return;const e=++a,s=t.engine.loadSeq,l=()=>e===a&&c(s);try{const e=await t.getM3u8State();if(!l())return;const r=i.m3u8?.quality||{},a=function(e,t,i){if(!t.control&&!t.setting||!e?.levels.length)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||`${e.height}P`),s=e.currentLevel?a(e.currentLevel):r,n=[];for(const l of e.levels){if(!i())return null;n.push({html:a(l),value:l.id,default:e.currentLevel?.id===l.id})}const o=new Map(e.levels.map(e=>[e.id,e.height])),c=W(n).sort((e,t)=>("auto"===t.value?0:o.get(t.value)||0)-("auto"===e.value?0:o.get(e.value)||0));return c.push({html:r,value:"auto",default:!e.currentLevel}),{title:t.title||"Quality",html:s,selector:c}}(e,r,l);if(!l())return;if(n.update(a,r,()=>c(s),(e,t)=>u("quality",e,t)),!l())return;const h=i.m3u8?.audio||{},d=function(e,t,i){if(!t.control&&!t.setting||!e||e.audios.length<2)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||e.lang||e.language),s=e.currentAudio?a(e.currentAudio):r,n=[];for(const c of e.audios){if(!i())return null;n.push({html:a(c),value:c.id,default:e.currentAudio?.id===c.id})}const o=W(n);return o.push({html:r,value:"auto",default:!e.currentAudio}),{title:t.title||"Audio",html:s,selector:o}}(e,h,l);l()&&o.update(d,h,()=>c(s),(e,t)=>u("audio",e,t))}catch(h){if(l())throw h}}async function u(i,r,n){const o=++s,u=t.engine.loadSeq;a++;const d=()=>o===s&&c(u);try{"quality"===i?await t.switchM3u8Quality(r.value):await t.switchM3u8Audio(r.value),d()&&(await l(),d()&&(e.notice.show=`${n}: ${r.html}`))}catch(f){if(d()&&(await h(),d()))throw f}return r.html}function h(){return l().catch(e=>console.warn("MediaBunny HLS menu update:",e))}function d(){a++,s++;try{U([n.clear,o.clear])}catch(e){console.warn("MediaBunny HLS menu cleanup:",e)}}function f(){if(!r){r=!0,d();try{U([()=>e.off("video:loadedmetadata",h),()=>e.off("restart",h),()=>e.off("video:loadstart",d),()=>e.off("video:error",d),()=>e.off("destroy",f)])}catch(t){console.warn("MediaBunny HLS listener cleanup:",t)}}}try{e.on("video:loadedmetadata",h),e.on("restarLine 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,t,i,r,n,s,a,o,l,c,u,d,h,f,m,p,g,k,y,b,T,v,w,S,P,I,x,C,B,E,_,R,F,A=Object.defineProperty,M=Object.defineProperties,D=Object.getOwnPropertyDescriptors,N=Object.getOwnPropertySymbols,O=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,z=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),U=e=>{throw TypeError(e)},q=Math.pow,W=(e,t,i)=>t in e?A(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i,H=(e,t)=>{for(var i in t||(t={}))O.call(t,i)&&W(e,i,t[i]);if(N)for(var i of N(t))L.call(t,i)&&W(e,i,t[i]);return e},V=(e,t)=>M(e,D(t)),j=(e,t,i)=>t.has(e)||U("Cannot "+i),K=(e,t,i)=>(j(e,t,"read from private field"),i?i.call(e):t.get(e)),X=(e,t,i)=>t.has(e)?U("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),$=(e,t,i,r)=>(j(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),G=(e,t,i)=>(j(e,t,"access private method"),i),Q=(e,t,i,r)=>({set _(r){$(e,t,r,i)},get _(){return K(e,t,r)}}),Y=(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())}),J=function(e,t){this[0]=e,this[1]=t},Z=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof J,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[z("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},ee=e=>{var t,i=e[z("asyncIterator")],r=!1,n={};return null==i?(i=e[z("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 J(new Promise(r=>{var n=i[e](t);n instanceof Object||U("Object expected"),r(n)}),1)}}),n[z("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},te=(e,t,i)=>(t=e[z("asyncIterator")])?t.call(e):(e=e[z("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 ie(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function re(e,t,i){const r={control:!1,setting:!1};let n=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function a(){n=null,ie([()=>s("control"),()=>s("setting")])}return{update:function(o,l,c,u){if(!o)return void a();n=o;const d=()=>n===o&&c(),h=e=>Y(null,null,function*(){return d()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html});l.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:h})):s("control"),d()&&(l.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:h})):s("setting"))},clear:a}}function ne(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function se({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,n=0,s=0;const a=re(e,"mediabunny-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>'),o=re(e,"mediabunny-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>'),l=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;function c(){return Y(this,null,function*(){var e,s;if(r||t.engine.destroyed)return;const c=++n,d=t.engine.loadSeq,h=()=>c===n&&l(d);try{const r=yield t.getM3u8State();if(!h())return;const n=(null==(e=i.m3u8)?void 0:e.quality)||{},c=function(e,t,i){vaLine 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,i,r,n,s,a,o,l,c,u,d,h,f,m,p,g,k,y,b,T,v,w,S,P,I,x,C,B,E,_,R,F,A=Object.defineProperty,M=Object.defineProperties,D=Object.getOwnPropertyDescriptors,N=Object.getOwnPropertySymbols,O=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,z=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),U=e=>{throw TypeError(e)},q=Math.pow,W=(e,t,i)=>t in e?A(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i,H=(e,t)=>{for(var i in t||(t={}))O.call(t,i)&&W(e,i,t[i]);if(N)for(var i of N(t))L.call(t,i)&&W(e,i,t[i]);return e},V=(e,t)=>M(e,D(t)),j=(e,t,i)=>t.has(e)||U("Cannot "+i),K=(e,t,i)=>(j(e,t,"read from private field"),i?i.call(e):t.get(e)),X=(e,t,i)=>t.has(e)?U("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),$=(e,t,i,r)=>(j(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),G=(e,t,i)=>(j(e,t,"access private method"),i),Q=(e,t,i,r)=>({set _(r){$(e,t,r,i)},get _(){return K(e,t,r)}}),Y=(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())}),J=function(e,t){this[0]=e,this[1]=t},Z=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof J,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[z("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},ee=e=>{var t,i=e[z("asyncIterator")],r=!1,n={};return null==i?(i=e[z("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 J(new Promise(r=>{var n=i[e](t);n instanceof Object||U("Object expected"),r(n)}),1)}}),n[z("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},te=(e,t,i)=>(t=e[z("asyncIterator")])?t.call(e):(e=e[z("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 ie(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function re(e,t,i){const r={control:!1,setting:!1};let n=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function a(){n=null,ie([()=>s("control"),()=>s("setting")])}return{update:function(o,l,c,u){if(!o)return void a();n=o;const d=()=>n===o&&c(),h=e=>Y(null,null,function*(){return d()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html});l.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:h})):s("control"),d()&&(l.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:h})):s("setting"))},clear:a}}function ne(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function se({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,n=0,s=0;const a=re(e,"mediabunny-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>'),o=re(e,"mediabunny-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>'),l=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;function c(){return Y(this,null,function*(){var e,s;if(r||t.engine.destroyed)return;const c=++n,d=t.engine.loadSeq,h=()=>c===n&&l(d);try{const r=yield t.getM3u8State();if(!h())return;const n=(null==(e=i.m3u8)?void 0:e.quality)||{},c=function(e,t,i){vaLine truncated
|
||||
@@ -21,8 +21,38 @@ var __privateWrapper = (obj, member, setter, getter) => ({
|
||||
}
|
||||
});
|
||||
var _closed, _closed2, _generation, _contexts, _nodes, _pump, _playTask, _playing, _loading, _onError, _AudioEngine_instances, load_fn, startPump_fn, play_fn, _generation2, _iteration, _loading2, _createSink, _usedSink, _destroyed, _preparing, _poster, _renderer, _onError2, _VideoEngine_instances, current_fn, context_fn, report_fn, release_fn, draw_fn, load_fn2, reset_fn, _playback;
|
||||
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 bridgeCanvas(canvas, shim) {
|
||||
const methods = /* @__PURE__ */ new Map();
|
||||
const native = canvas;
|
||||
const members = shim;
|
||||
for (const name in canvas) {
|
||||
if (typeof native[name] === "function") {
|
||||
const method = native[name];
|
||||
methods.set(name, method.bind(canvas));
|
||||
}
|
||||
}
|
||||
const names = /* @__PURE__ */ new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim))
|
||||
]);
|
||||
for (const name of names) {
|
||||
if (name === "constructor" || name in canvas)
|
||||
continue;
|
||||
Object.defineProperty(canvas, name, {
|
||||
get() {
|
||||
const value = members[name];
|
||||
return typeof value === "function" ? value.bind(shim) : value;
|
||||
},
|
||||
set(value) {
|
||||
members[name] = value;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
for (const [name, method] of methods)
|
||||
native[name] = (...args) => method(...args);
|
||||
}
|
||||
function releaseAll(actions) {
|
||||
const failures = [];
|
||||
for (const action of actions) {
|
||||
@@ -35,6 +65,8 @@ function releaseAll(actions) {
|
||||
if (failures.length)
|
||||
throw failures[0];
|
||||
}
|
||||
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 createMenu(art, name, icon) {
|
||||
const owned = { control: false, setting: false };
|
||||
let model = null;
|
||||
@@ -218,12 +250,49 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
console.warn("MediaBunny HLS listener cleanup:", error);
|
||||
}
|
||||
}
|
||||
art.on("video:loadedmetadata", refresh);
|
||||
art.on("restart", refresh);
|
||||
art.on("video:loadstart", invalidate);
|
||||
art.on("video:error", invalidate);
|
||||
art.on("destroy", destroy);
|
||||
return { update };
|
||||
try {
|
||||
art.on("video:loadedmetadata", refresh);
|
||||
art.on("restart", refresh);
|
||||
art.on("video:loadstart", invalidate);
|
||||
art.on("video:error", invalidate);
|
||||
art.on("destroy", destroy);
|
||||
} catch (error) {
|
||||
destroy();
|
||||
throw error;
|
||||
}
|
||||
return { update, destroy };
|
||||
}
|
||||
function entryLifecycle(art, canvas, shim, option) {
|
||||
let closed = false;
|
||||
let controls;
|
||||
function resize() {
|
||||
if (closed || !art.template?.$player || art.option.autoSize)
|
||||
return;
|
||||
Object.assign(canvas.style, { width: "100%", height: "100%", objectFit: "contain" });
|
||||
}
|
||||
function destroy() {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
releaseAll([
|
||||
() => controls?.destroy(),
|
||||
() => {
|
||||
if (art.mediabunny === shim)
|
||||
delete art.mediabunny;
|
||||
},
|
||||
() => shim.destroy(),
|
||||
() => art.off?.("resize", resize),
|
||||
() => art.off?.("video:loadedmetadata", resize),
|
||||
() => art.off?.("destroy", destroy)
|
||||
]);
|
||||
}
|
||||
function install() {
|
||||
art.on("resize", resize);
|
||||
art.on("video:loadedmetadata", resize);
|
||||
controls = setupM3u8Controls({ art, shim, option });
|
||||
art.on("destroy", destroy);
|
||||
}
|
||||
return { install, destroy };
|
||||
}
|
||||
class EventTarget {
|
||||
constructor() {
|
||||
@@ -26143,68 +26212,28 @@ class VideoShim {
|
||||
}
|
||||
}
|
||||
function artplayerProxyMediabunny(option = {}) {
|
||||
return (art) => {
|
||||
const { constructor } = art;
|
||||
const { createElement } = constructor.utils;
|
||||
return (player) => {
|
||||
const art = player;
|
||||
const { createElement } = art.constructor.utils;
|
||||
const canvas = createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const shim = new VideoShim({
|
||||
art,
|
||||
canvas,
|
||||
ctx,
|
||||
option
|
||||
});
|
||||
art.mediabunny = shim;
|
||||
const originalCanvasMethods = {};
|
||||
for (const prop in canvas) {
|
||||
if (typeof canvas[prop] === "function") {
|
||||
originalCanvasMethods[prop] = canvas[prop].bind(canvas);
|
||||
const shim = new VideoShim({ art, canvas, ctx: canvas.getContext("2d"), option });
|
||||
const lifecycle = entryLifecycle(art, canvas, shim, option);
|
||||
try {
|
||||
art.mediabunny = shim;
|
||||
bridgeCanvas(canvas, shim);
|
||||
lifecycle.install();
|
||||
} catch (error) {
|
||||
try {
|
||||
lifecycle.destroy();
|
||||
} catch (failure) {
|
||||
console.warn("MediaBunny entry setup cleanup:", failure);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const propertyNames = /* @__PURE__ */ new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim))
|
||||
]);
|
||||
for (const prop of propertyNames) {
|
||||
if (prop === "constructor")
|
||||
continue;
|
||||
if (!(prop in canvas)) {
|
||||
Object.defineProperty(canvas, prop, {
|
||||
get() {
|
||||
const value = shim[prop];
|
||||
return typeof value === "function" ? value.bind(shim) : value;
|
||||
},
|
||||
set(v) {
|
||||
shim[prop] = v;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const prop in originalCanvasMethods) {
|
||||
canvas[prop] = (...args) => originalCanvasMethods[prop](...args);
|
||||
}
|
||||
function resize() {
|
||||
const player = art.template?.$player;
|
||||
if (!player || art.option.autoSize)
|
||||
return;
|
||||
Object.assign(canvas.style, {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
});
|
||||
}
|
||||
art.on("resize", resize);
|
||||
art.on("video:loadedmetadata", resize);
|
||||
setupM3u8Controls({ art, shim, option });
|
||||
art.on("destroy", () => {
|
||||
delete art.mediabunny;
|
||||
shim.destroy();
|
||||
});
|
||||
return canvas;
|
||||
};
|
||||
}
|
||||
Object.defineProperty(artplayerProxyMediabunny, "default", { value: artplayerProxyMediabunny, writable: true, configurable: true });
|
||||
export {
|
||||
artplayerProxyMediabunny as default
|
||||
};
|
||||
+3
-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-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-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-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-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,8 @@
|
||||
"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-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js"
|
||||
"test:mediabunny": "node --test test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js",
|
||||
"test:mediabunny-types-package": "node refactor/scripts/mb-package-types.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,4 +2,5 @@ node_modules
|
||||
.DS_Store
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
src
|
||||
src
|
||||
tsconfig.json
|
||||
@@ -1,14 +1,19 @@
|
||||
# MediaBunny proxy maintenance
|
||||
|
||||
The public entry is `src/index.js`: an optional-options factory returns the existing
|
||||
The public entry is `src/index.ts`: an optional-options factory returns the existing
|
||||
ArtPlayer proxy callback and an HTMLCanvasElement. Public declarations remain in
|
||||
`types/artplayer-proxy-mediabunny.d.ts`. The input migration does not change those
|
||||
declarations, the factory signature, canvas forwarding, or package entrypoints.
|
||||
`types/artplayer-proxy-mediabunny.d.ts`, with a separate ESM declaration entry.
|
||||
The exact optional factory and HTMLCanvasElement result remain unchanged. The media
|
||||
view in `types/media.d.ts` is opt-in and checked against the real VideoShim class.
|
||||
|
||||
## Current module map
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `index.ts` | Optional factory, Canvas construction, alias installation and setup rollback |
|
||||
| `canvas-bridge.ts` | Historical own/direct-prototype forwarding and native Canvas receiver binding |
|
||||
| `entry-lifecycle.ts` | Entry resize/metadata/destroy subscriptions, alias ownership and shim cleanup |
|
||||
| `cleanup.ts` | Attempt every release and preserve the first failure |
|
||||
| `input.ts` | String/Blob/ReadableStream normalization, SDK Source/SourceRef pass-through, HLS detection and Input construction |
|
||||
| `media.ts` | Internal SDK-backed media and track-mode types; not public consumer types |
|
||||
| `tracks.ts` | Primary video and pairable audio selection, metadata/computed/live duration |
|
||||
@@ -38,11 +43,20 @@ declarations, the factory signature, canvas forwarding, or package entrypoints.
|
||||
| `m3u8-menu.ts` | Owned control/setting surfaces and stale callback guards |
|
||||
| `m3u8-types.ts` | Narrow internal UI/shim contracts without changing public factory declarations |
|
||||
|
||||
The thirty production TypeScript modules use strict checking with `skipLibCheck: false`
|
||||
The thirty-four production TypeScript modules use strict checking with `skipLibCheck: false`
|
||||
and no ambient Node types. The coordinator and both decoder engines are checked
|
||||
implementations; there are no remaining adjacent JavaScript declaration bridges.
|
||||
Only the public factory remains JavaScript, scheduled in PKG-MB-08.
|
||||
Do not describe the whole package as migrated yet.
|
||||
All owned production JavaScript has been migrated. This source milestone does not complete
|
||||
PKG-MB-08: unsupported decoder fallback/readiness still needs its remaining review.
|
||||
|
||||
The entry preserves native Canvas method priority, detached createElement invocation,
|
||||
two reads of enumerable native method accessors, direct-prototype forwarding, bound shim
|
||||
methods and strict assignment errors for getter-only properties. Avoid replacing these
|
||||
with generic Reflect forwarding or a broad HTMLVideoElement cast. Entry bookkeeping stays
|
||||
off the canvas surface. Destroy unregisters its callbacks and only removes art.mediabunny
|
||||
when the alias still belongs to that entry. A custom historical host without off keeps inert
|
||||
callbacks; a real Artplayer host removes them. Partial setup attempts all cleanup while
|
||||
preserving the original setup error. HLS cleanup cannot strand the remaining entry resources.
|
||||
|
||||
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
|
||||
@@ -228,13 +242,39 @@ resolution selects `@types/dom-webcodecs@0.1.19` for both dependency paths; the
|
||||
itself is unchanged. This is a build-time declaration correction, not a browser polyfill.
|
||||
The package typecheck adds ESNext.Disposable for SDK declarations while keeping the
|
||||
ES2020 target; production source does not use `using` or require Symbol.dispose.
|
||||
Public consumer declarations do not import these SDK types; preserve that boundary
|
||||
until the separate consumer-compatibility task verifies any proposed change.
|
||||
Public declarations do not import SDK types or modern DOM VideoFrame callback names.
|
||||
The optional media view names the synthetic frame metadata directly; SDK track objects
|
||||
are unknown until consumers narrow them using their own SDK version. `VideoShim implements
|
||||
MediaBunnyShim` checks that this view matches implementation rather than masking JS with
|
||||
an adjacent declaration. Explicit getter return types retain coercive unknown setters
|
||||
internally while giving consumers numeric/boolean reads.
|
||||
|
||||
Default Result remains exactly HTMLCanvasElement, allowing callers to substitute a plain
|
||||
canvas initializer or an entire historical factory. MediaBunnyCanvas combines the opt-in
|
||||
media view with native Canvas methods winning collisions. MediaBunnyPlayer has an optional
|
||||
mediabunny alias because it is absent before initialization and after destruction; there
|
||||
is no global augmentation of Artplayer. Named exports are types only.
|
||||
|
||||
The .d.mts wrapper fixes NodeNext ESM default resolution. CommonJS keeps the historical
|
||||
default declaration facade, and typesVersions resolves /legacy in TS 4.3/classic Node
|
||||
resolution. Runtime remains directly callable and adds a nonenumerable, writable default
|
||||
self alias for the 1.0.0 CommonJS usage. That property is deliberately not mandatory on
|
||||
the default factory type. Whole-factory assignments are tested bidirectionally against
|
||||
both frozen published declarations, in an actual isolated install as well as workspace
|
||||
consumers. Do not switch to export= or a required default member without those checks.
|
||||
|
||||
`canPlayType()` still always returns maybe; buffered/played/seekable and RAF metadata remain
|
||||
synthetic. None is proof that a codec or browser can play the input. Missing Web Audio or
|
||||
WebCodecs in Windows WebKit remains an unsupported-capability control. Existing but
|
||||
undecodable tracks can still take historical clock-only fallback; remaining MB-08 work
|
||||
must distinguish valid audio-only/video-only playback from false readiness with no usable
|
||||
decoder, including selection replacement. Native/long-play/device acceptance remains MB-09.
|
||||
|
||||
## Validation and remaining work
|
||||
|
||||
```sh
|
||||
yarn test:mediabunny
|
||||
yarn test:mediabunny-types-package
|
||||
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
|
||||
@@ -242,6 +282,7 @@ yarn test:browser test/browser/mediabunny-shim.spec.js
|
||||
yarn test:browser test/browser/mediabunny-video.spec.js
|
||||
yarn test:browser test/browser/mediabunny-audio.spec.js
|
||||
yarn test:browser test/browser/mediabunny-hls.spec.js
|
||||
yarn test:browser test/browser/mediabunny-entry.spec.js
|
||||
```
|
||||
|
||||
Node tests use actual SDK parsing and controlled lifecycle interleavings. Browser tests
|
||||
|
||||
@@ -10,6 +10,46 @@ mediabunny proxy for ArtPlayer, including `m3u8` playback through Mediabunny's H
|
||||
|
||||
MIT © Harvey Zhao
|
||||
|
||||
## TypeScript
|
||||
|
||||
The default factory still accepts optional options and returns an initializer whose
|
||||
result is exactly `HTMLCanvasElement`. Existing factory replacements remain assignable.
|
||||
Use the optional media view when accessing the proxy's media methods:
|
||||
|
||||
```ts
|
||||
import type { MediaBunnyPlayer } from 'artplayer-proxy-mediabunny'
|
||||
import Artplayer from 'artplayer'
|
||||
import mediabunny from 'artplayer-proxy-mediabunny'
|
||||
|
||||
const art: MediaBunnyPlayer = new Artplayer({
|
||||
container: '#player',
|
||||
url: 'movie.mp4',
|
||||
proxy: mediabunny({ volume: 0.7 }),
|
||||
})
|
||||
|
||||
art.on('ready', async () => {
|
||||
const shim = art.mediabunny
|
||||
if (!shim)
|
||||
return
|
||||
const state = await shim.getM3u8State()
|
||||
console.log(state?.currentLevel?.height)
|
||||
})
|
||||
```
|
||||
|
||||
`MediaBunnyShim`, `MediaBunnyCanvas`, HLS track/state and synthetic frame callback types
|
||||
are also available as type-only exports. The player alias is optional and removed on
|
||||
destruction. Native Canvas event/attribute methods take precedence over shim methods.
|
||||
SDK track objects need explicit narrowing with your application's SDK types.
|
||||
|
||||
The `canPlayType()` response remains `maybe` for compatibility; it does not test codec
|
||||
support. TimeRanges and frame callback metadata are synthetic. Actual playback requires
|
||||
supported codecs and browser media APIs; a `legacy` bundle does not polyfill those APIs.
|
||||
Decoder fallback and broader browser/device validation remain in progress.
|
||||
|
||||
ESM default imports and `/legacy` type resolution now support the tested TS 5.9 and
|
||||
TS 4.3 modes. CommonJS supports both direct calls and the old `require(...).default`
|
||||
runtime path. The default factory type keeps its historical callable signature.
|
||||
|
||||
## Maintenance
|
||||
|
||||
See [ARCHITECTURE.md](ARCHITECTURE.md) for current modules, resource ownership, compatibility boundaries and validation commands.
|
||||
@@ -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,t,i,r,a,s,n,o,c,l,u,h,d,f,m,p,g,k,y,b,w,T,S,v,P,I,x,C,B,E,_,R,F,A=e=>{throw TypeError(e)},M=(e,t,i)=>t.has(e)||A("Cannot "+i),D=(e,t,i)=>(M(e,t,"read from private field"),i?i.call(e):t.get(e)),N=(e,t,i)=>t.has(e)?A("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),O=(e,t,i,r)=>(M(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),L=(e,t,i)=>(M(e,t,"access private method"),i),z=(e,t,i,r)=>({set _(r){O(e,t,r,i)},get _(){return D(e,t,r)}});function U(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function q(e,t,i){const r={control:!1,setting:!1};let a=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function n(){a=null,U([()=>s("control"),()=>s("setting")])}return{update:function(o,c,l,u){if(!o)return void n();a=o;const h=()=>a===o&&l(),d=async e=>h()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html;c.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:d})):s("control"),h()&&(c.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:d})):s("setting"))},clear:n}}function W(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function H({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,a=0,s=0;const n=q(e,"mediabunny-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>'),o=q(e,"mediabunny-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>'),c=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;async function l(){if(r||t.engine.destroyed)return;const e=++a,s=t.engine.loadSeq,l=()=>e===a&&c(s);try{const e=await t.getM3u8State();if(!l())return;const r=i.m3u8?.quality||{},a=function(e,t,i){if(!t.control&&!t.setting||!e?.levels.length)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||`${e.height}P`),s=e.currentLevel?a(e.currentLevel):r,n=[];for(const l of e.levels){if(!i())return null;n.push({html:a(l),value:l.id,default:e.currentLevel?.id===l.id})}const o=new Map(e.levels.map(e=>[e.id,e.height])),c=W(n).sort((e,t)=>("auto"===t.value?0:o.get(t.value)||0)-("auto"===e.value?0:o.get(e.value)||0));return c.push({html:r,value:"auto",default:!e.currentLevel}),{title:t.title||"Quality",html:s,selector:c}}(e,r,l);if(!l())return;if(n.update(a,r,()=>c(s),(e,t)=>u("quality",e,t)),!l())return;const h=i.m3u8?.audio||{},d=function(e,t,i){if(!t.control&&!t.setting||!e||e.audios.length<2)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||e.lang||e.language),s=e.currentAudio?a(e.currentAudio):r,n=[];for(const c of e.audios){if(!i())return null;n.push({html:a(c),value:c.id,default:e.currentAudio?.id===c.id})}const o=W(n);return o.push({html:r,value:"auto",default:!e.currentAudio}),{title:t.title||"Audio",html:s,selector:o}}(e,h,l);l()&&o.update(d,h,()=>c(s),(e,t)=>u("audio",e,t))}catch(h){if(l())throw h}}async function u(i,r,n){const o=++s,u=t.engine.loadSeq;a++;const d=()=>o===s&&c(u);try{"quality"===i?await t.switchM3u8Quality(r.value):await t.switchM3u8Audio(r.value),d()&&(await l(),d()&&(e.notice.show=`${n}: ${r.html}`))}catch(f){if(d()&&(await h(),d()))throw f}return r.html}function h(){return l().catch(e=>console.warn("MediaBunny HLS menu update:",e))}function d(){a++,s++;try{U([n.clear,o.clear])}catch(e){console.warn("MediaBunny HLS menu cleanup:",e)}}return e.on("video:loadedmetadata",h),e.on("restart",h),e.on("video:loadstart",d),e.on("video:error",d),e.on("destroy",function t(){if(!r){r=!0,d();try{U([()=>e.off("video:loadedmetadata",h),()=>e.off("restart",h),()=>e.off("video:loadstart",d),()=>e.off("video:error",d),()=>e.off("destLine 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,i,r,a,s,n,o,c,l,u,h,d,f,m,p,g,k,y,b,w,T,S,v,P,I,x,C,B,E,_,R,F,A=e=>{throw TypeError(e)},M=(e,t,i)=>t.has(e)||A("Cannot "+i),D=(e,t,i)=>(M(e,t,"read from private field"),i?i.call(e):t.get(e)),N=(e,t,i)=>t.has(e)?A("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),O=(e,t,i,r)=>(M(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),L=(e,t,i)=>(M(e,t,"access private method"),i),z=(e,t,i,r)=>({set _(r){O(e,t,r,i)},get _(){return D(e,t,r)}});function U(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function q(e,t,i){const r={control:!1,setting:!1};let a=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function n(){a=null,U([()=>s("control"),()=>s("setting")])}return{update:function(o,c,l,u){if(!o)return void n();a=o;const h=()=>a===o&&l(),d=async e=>h()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html;c.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:d})):s("control"),h()&&(c.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:d})):s("setting"))},clear:n}}function W(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function H({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,a=0,s=0;const n=q(e,"mediabunny-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>'),o=q(e,"mediabunny-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>'),c=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;async function l(){if(r||t.engine.destroyed)return;const e=++a,s=t.engine.loadSeq,l=()=>e===a&&c(s);try{const e=await t.getM3u8State();if(!l())return;const r=i.m3u8?.quality||{},a=function(e,t,i){if(!t.control&&!t.setting||!e?.levels.length)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||`${e.height}P`),s=e.currentLevel?a(e.currentLevel):r,n=[];for(const l of e.levels){if(!i())return null;n.push({html:a(l),value:l.id,default:e.currentLevel?.id===l.id})}const o=new Map(e.levels.map(e=>[e.id,e.height])),c=W(n).sort((e,t)=>("auto"===t.value?0:o.get(t.value)||0)-("auto"===e.value?0:o.get(e.value)||0));return c.push({html:r,value:"auto",default:!e.currentLevel}),{title:t.title||"Quality",html:s,selector:c}}(e,r,l);if(!l())return;if(n.update(a,r,()=>c(s),(e,t)=>u("quality",e,t)),!l())return;const h=i.m3u8?.audio||{},d=function(e,t,i){if(!t.control&&!t.setting||!e||e.audios.length<2)return null;const r=t.auto||"Auto",a=t.getName||(e=>e.name||e.lang||e.language),s=e.currentAudio?a(e.currentAudio):r,n=[];for(const c of e.audios){if(!i())return null;n.push({html:a(c),value:c.id,default:e.currentAudio?.id===c.id})}const o=W(n);return o.push({html:r,value:"auto",default:!e.currentAudio}),{title:t.title||"Audio",html:s,selector:o}}(e,h,l);l()&&o.update(d,h,()=>c(s),(e,t)=>u("audio",e,t))}catch(h){if(l())throw h}}async function u(i,r,n){const o=++s,u=t.engine.loadSeq;a++;const d=()=>o===s&&c(u);try{"quality"===i?await t.switchM3u8Quality(r.value):await t.switchM3u8Audio(r.value),d()&&(await l(),d()&&(e.notice.show=`${n}: ${r.html}`))}catch(f){if(d()&&(await h(),d()))throw f}return r.html}function h(){return l().catch(e=>console.warn("MediaBunny HLS menu update:",e))}function d(){a++,s++;try{U([n.clear,o.clear])}catch(e){console.warn("MediaBunny HLS menu cleanup:",e)}}function f(){if(!r){r=!0,d();try{U([()=>e.off("video:loadedmetadata",h),()=>e.off("restart",h),()=>e.off("video:loadstart",d),()=>e.off("video:error",d),()=>e.off("destroy",f)])}catch(t){console.warn("MediaBunny HLS listener cleanup:",t)}}}try{e.on("video:loadedmetadata",h),e.on("restarLine 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,t,i,r,n,s,a,o,l,c,u,d,h,f,m,p,g,k,y,b,T,v,w,S,P,I,x,C,B,E,_,R,F,A=Object.defineProperty,M=Object.defineProperties,D=Object.getOwnPropertyDescriptors,N=Object.getOwnPropertySymbols,O=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,z=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),U=e=>{throw TypeError(e)},q=Math.pow,W=(e,t,i)=>t in e?A(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i,H=(e,t)=>{for(var i in t||(t={}))O.call(t,i)&&W(e,i,t[i]);if(N)for(var i of N(t))L.call(t,i)&&W(e,i,t[i]);return e},V=(e,t)=>M(e,D(t)),j=(e,t,i)=>t.has(e)||U("Cannot "+i),K=(e,t,i)=>(j(e,t,"read from private field"),i?i.call(e):t.get(e)),X=(e,t,i)=>t.has(e)?U("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),$=(e,t,i,r)=>(j(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),G=(e,t,i)=>(j(e,t,"access private method"),i),Q=(e,t,i,r)=>({set _(r){$(e,t,r,i)},get _(){return K(e,t,r)}}),Y=(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())}),J=function(e,t){this[0]=e,this[1]=t},Z=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof J,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[z("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},ee=e=>{var t,i=e[z("asyncIterator")],r=!1,n={};return null==i?(i=e[z("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 J(new Promise(r=>{var n=i[e](t);n instanceof Object||U("Object expected"),r(n)}),1)}}),n[z("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},te=(e,t,i)=>(t=e[z("asyncIterator")])?t.call(e):(e=e[z("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 ie(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function re(e,t,i){const r={control:!1,setting:!1};let n=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function a(){n=null,ie([()=>s("control"),()=>s("setting")])}return{update:function(o,l,c,u){if(!o)return void a();n=o;const d=()=>n===o&&c(),h=e=>Y(null,null,function*(){return d()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html});l.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:h})):s("control"),d()&&(l.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:h})):s("setting"))},clear:a}}function ne(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function se({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,n=0,s=0;const a=re(e,"mediabunny-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>'),o=re(e,"mediabunny-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>'),l=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;function c(){return Y(this,null,function*(){var e,s;if(r||t.engine.destroyed)return;const c=++n,d=t.engine.loadSeq,h=()=>c===n&&l(d);try{const r=yield t.getM3u8State();if(!h())return;const n=(null==(e=i.m3u8)?void 0:e.quality)||{},c=function(e,t,i){vaLine 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,i,r,n,s,a,o,l,c,u,d,h,f,m,p,g,k,y,b,T,v,w,S,P,I,x,C,B,E,_,R,F,A=Object.defineProperty,M=Object.defineProperties,D=Object.getOwnPropertyDescriptors,N=Object.getOwnPropertySymbols,O=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,z=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),U=e=>{throw TypeError(e)},q=Math.pow,W=(e,t,i)=>t in e?A(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i,H=(e,t)=>{for(var i in t||(t={}))O.call(t,i)&&W(e,i,t[i]);if(N)for(var i of N(t))L.call(t,i)&&W(e,i,t[i]);return e},V=(e,t)=>M(e,D(t)),j=(e,t,i)=>t.has(e)||U("Cannot "+i),K=(e,t,i)=>(j(e,t,"read from private field"),i?i.call(e):t.get(e)),X=(e,t,i)=>t.has(e)?U("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,i),$=(e,t,i,r)=>(j(e,t,"write to private field"),r?r.call(e,i):t.set(e,i),i),G=(e,t,i)=>(j(e,t,"access private method"),i),Q=(e,t,i,r)=>({set _(r){$(e,t,r,i)},get _(){return K(e,t,r)}}),Y=(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())}),J=function(e,t){this[0]=e,this[1]=t},Z=(e,t,i)=>{var r,n=(e,t,r,s)=>{try{var a=i[e](t),o=(t=a.value)instanceof J,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[z("asyncIterator")]=()=>a,s("next"),s("throw"),s("return"),a},ee=e=>{var t,i=e[z("asyncIterator")],r=!1,n={};return null==i?(i=e[z("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 J(new Promise(r=>{var n=i[e](t);n instanceof Object||U("Object expected"),r(n)}),1)}}),n[z("iterator")]=()=>n,t("next"),"throw"in i?t("throw"):n.throw=e=>{throw e},"return"in i&&t("return"),n},te=(e,t,i)=>(t=e[z("asyncIterator")])?t.call(e):(e=e[z("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 ie(e){const t=[];for(const r of e)try{r()}catch(i){t.push(i)}if(t.length)throw t[0]}function re(e,t,i){const r={control:!1,setting:!1};let n=null;function s(i){r[i]&&(r[i]=!1,"control"===i?e.controls.cache.has(t)&&e.controls.remove(t):e.setting.find(t)&&e.setting.remove(t))}function a(){n=null,ie([()=>s("control"),()=>s("setting")])}return{update:function(o,l,c,u){if(!o)return void a();n=o;const d=()=>n===o&&c(),h=e=>Y(null,null,function*(){return d()&&o.selector.some(t=>t.value===e.value)?u(e,o.title):e.html});l.control?(r.control=!0,e.controls.update({name:t,position:"right",html:o.html,style:{padding:"0 10px"},selector:o.selector,onSelect:h})):s("control"),d()&&(l.setting?(r.setting=!0,e.setting.update({name:t,tooltip:o.html,html:o.title,icon:i,width:200,selector:o.selector,onSelect:h})):s("setting"))},clear:a}}function ne(e){const t=new Map,i=[];for(const r of e){const e=t.get(r.html);void 0!==r.html&&void 0!==e?r.default&&(i[e]=r):(t.set(r.html,i.length),i.push(r))}return i}function se({art:e,shim:t,option:i}){if(!i.m3u8)return;let r=!1,n=0,s=0;const a=re(e,"mediabunny-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>'),o=re(e,"mediabunny-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>'),l=e=>!r&&!t.engine.destroyed&&t.engine.loadSeq===e;function c(){return Y(this,null,function*(){var e,s;if(r||t.engine.destroyed)return;const c=++n,d=t.engine.loadSeq,h=()=>c===n&&l(d);try{const r=yield t.getM3u8State();if(!h())return;const n=(null==(e=i.m3u8)?void 0:e.quality)||{},c=function(e,t,i){vaLine truncated
|
||||
@@ -21,8 +21,38 @@ var __privateWrapper = (obj, member, setter, getter) => ({
|
||||
}
|
||||
});
|
||||
var _closed, _closed2, _generation, _contexts, _nodes, _pump, _playTask, _playing, _loading, _onError, _AudioEngine_instances, load_fn, startPump_fn, play_fn, _generation2, _iteration, _loading2, _createSink, _usedSink, _destroyed, _preparing, _poster, _renderer, _onError2, _VideoEngine_instances, current_fn, context_fn, report_fn, release_fn, draw_fn, load_fn2, reset_fn, _playback;
|
||||
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 bridgeCanvas(canvas, shim) {
|
||||
const methods = /* @__PURE__ */ new Map();
|
||||
const native = canvas;
|
||||
const members = shim;
|
||||
for (const name in canvas) {
|
||||
if (typeof native[name] === "function") {
|
||||
const method = native[name];
|
||||
methods.set(name, method.bind(canvas));
|
||||
}
|
||||
}
|
||||
const names = /* @__PURE__ */ new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim))
|
||||
]);
|
||||
for (const name of names) {
|
||||
if (name === "constructor" || name in canvas)
|
||||
continue;
|
||||
Object.defineProperty(canvas, name, {
|
||||
get() {
|
||||
const value = members[name];
|
||||
return typeof value === "function" ? value.bind(shim) : value;
|
||||
},
|
||||
set(value) {
|
||||
members[name] = value;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
for (const [name, method] of methods)
|
||||
native[name] = (...args) => method(...args);
|
||||
}
|
||||
function releaseAll(actions) {
|
||||
const failures = [];
|
||||
for (const action of actions) {
|
||||
@@ -35,6 +65,8 @@ function releaseAll(actions) {
|
||||
if (failures.length)
|
||||
throw failures[0];
|
||||
}
|
||||
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 createMenu(art, name, icon) {
|
||||
const owned = { control: false, setting: false };
|
||||
let model = null;
|
||||
@@ -218,12 +250,49 @@ function setupM3u8Controls({ art, shim, option }) {
|
||||
console.warn("MediaBunny HLS listener cleanup:", error);
|
||||
}
|
||||
}
|
||||
art.on("video:loadedmetadata", refresh);
|
||||
art.on("restart", refresh);
|
||||
art.on("video:loadstart", invalidate);
|
||||
art.on("video:error", invalidate);
|
||||
art.on("destroy", destroy);
|
||||
return { update };
|
||||
try {
|
||||
art.on("video:loadedmetadata", refresh);
|
||||
art.on("restart", refresh);
|
||||
art.on("video:loadstart", invalidate);
|
||||
art.on("video:error", invalidate);
|
||||
art.on("destroy", destroy);
|
||||
} catch (error) {
|
||||
destroy();
|
||||
throw error;
|
||||
}
|
||||
return { update, destroy };
|
||||
}
|
||||
function entryLifecycle(art, canvas, shim, option) {
|
||||
let closed = false;
|
||||
let controls;
|
||||
function resize() {
|
||||
if (closed || !art.template?.$player || art.option.autoSize)
|
||||
return;
|
||||
Object.assign(canvas.style, { width: "100%", height: "100%", objectFit: "contain" });
|
||||
}
|
||||
function destroy() {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
releaseAll([
|
||||
() => controls?.destroy(),
|
||||
() => {
|
||||
if (art.mediabunny === shim)
|
||||
delete art.mediabunny;
|
||||
},
|
||||
() => shim.destroy(),
|
||||
() => art.off?.("resize", resize),
|
||||
() => art.off?.("video:loadedmetadata", resize),
|
||||
() => art.off?.("destroy", destroy)
|
||||
]);
|
||||
}
|
||||
function install() {
|
||||
art.on("resize", resize);
|
||||
art.on("video:loadedmetadata", resize);
|
||||
controls = setupM3u8Controls({ art, shim, option });
|
||||
art.on("destroy", destroy);
|
||||
}
|
||||
return { install, destroy };
|
||||
}
|
||||
class EventTarget {
|
||||
constructor() {
|
||||
@@ -26143,68 +26212,28 @@ class VideoShim {
|
||||
}
|
||||
}
|
||||
function artplayerProxyMediabunny(option = {}) {
|
||||
return (art) => {
|
||||
const { constructor } = art;
|
||||
const { createElement } = constructor.utils;
|
||||
return (player) => {
|
||||
const art = player;
|
||||
const { createElement } = art.constructor.utils;
|
||||
const canvas = createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const shim = new VideoShim({
|
||||
art,
|
||||
canvas,
|
||||
ctx,
|
||||
option
|
||||
});
|
||||
art.mediabunny = shim;
|
||||
const originalCanvasMethods = {};
|
||||
for (const prop in canvas) {
|
||||
if (typeof canvas[prop] === "function") {
|
||||
originalCanvasMethods[prop] = canvas[prop].bind(canvas);
|
||||
const shim = new VideoShim({ art, canvas, ctx: canvas.getContext("2d"), option });
|
||||
const lifecycle = entryLifecycle(art, canvas, shim, option);
|
||||
try {
|
||||
art.mediabunny = shim;
|
||||
bridgeCanvas(canvas, shim);
|
||||
lifecycle.install();
|
||||
} catch (error) {
|
||||
try {
|
||||
lifecycle.destroy();
|
||||
} catch (failure) {
|
||||
console.warn("MediaBunny entry setup cleanup:", failure);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const propertyNames = /* @__PURE__ */ new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim))
|
||||
]);
|
||||
for (const prop of propertyNames) {
|
||||
if (prop === "constructor")
|
||||
continue;
|
||||
if (!(prop in canvas)) {
|
||||
Object.defineProperty(canvas, prop, {
|
||||
get() {
|
||||
const value = shim[prop];
|
||||
return typeof value === "function" ? value.bind(shim) : value;
|
||||
},
|
||||
set(v) {
|
||||
shim[prop] = v;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const prop in originalCanvasMethods) {
|
||||
canvas[prop] = (...args) => originalCanvasMethods[prop](...args);
|
||||
}
|
||||
function resize() {
|
||||
const player = art.template?.$player;
|
||||
if (!player || art.option.autoSize)
|
||||
return;
|
||||
Object.assign(canvas.style, {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
});
|
||||
}
|
||||
art.on("resize", resize);
|
||||
art.on("video:loadedmetadata", resize);
|
||||
setupM3u8Controls({ art, shim, option });
|
||||
art.on("destroy", () => {
|
||||
delete art.mediabunny;
|
||||
shim.destroy();
|
||||
});
|
||||
return canvas;
|
||||
};
|
||||
}
|
||||
Object.defineProperty(artplayerProxyMediabunny, "default", { value: artplayerProxyMediabunny, writable: true, configurable: true });
|
||||
export {
|
||||
artplayerProxyMediabunny as default
|
||||
};
|
||||
@@ -19,14 +19,24 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.ts",
|
||||
"import": "./dist/artplayer-proxy-mediabunny.mjs",
|
||||
"require": "./dist/artplayer-proxy-mediabunny.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.mts",
|
||||
"default": "./dist/artplayer-proxy-mediabunny.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.ts",
|
||||
"default": "./dist/artplayer-proxy-mediabunny.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.ts",
|
||||
"import": "./dist/artplayer-proxy-mediabunny.legacy.js",
|
||||
"require": "./dist/artplayer-proxy-mediabunny.legacy.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.mts",
|
||||
"default": "./dist/artplayer-proxy-mediabunny.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.ts",
|
||||
"default": "./dist/artplayer-proxy-mediabunny.legacy.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-proxy-mediabunny.js",
|
||||
@@ -34,6 +44,11 @@
|
||||
"types": "./types/artplayer-proxy-mediabunny.d.ts",
|
||||
"legacy": "./dist/artplayer-proxy-mediabunny.legacy.js",
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"legacy": ["types/artplayer-proxy-mediabunny.d.ts"]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"mediabunny": "^1.43.1"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { MediaBunnyShim, SyntheticFrameCallback } from '../types/artplayer-proxy-mediabunny'
|
||||
import type { EngineOptions, EnginePort, ProxyOptions, ShimHost } from './engine-types'
|
||||
import type { MediaListener } from './EventTarget'
|
||||
import EventTarget from './EventTarget'
|
||||
@@ -5,7 +6,7 @@ import MediaBunnyEngine from './MediaBunnyEngine.js'
|
||||
import { cancelFrame, closeFrames, requestFrame } from './shim-frames'
|
||||
import { clampVolume, timeRanges } from './shim-values'
|
||||
|
||||
export default class VideoShim {
|
||||
export default class VideoShim implements MediaBunnyShim {
|
||||
declare art: ShimHost
|
||||
declare canvas: HTMLCanvasElement
|
||||
declare option: ProxyOptions
|
||||
@@ -82,7 +83,7 @@ export default class VideoShim {
|
||||
}
|
||||
|
||||
// Time
|
||||
get currentTime() {
|
||||
get currentTime(): number {
|
||||
return this.engine.currentTime
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ export default class VideoShim {
|
||||
}
|
||||
|
||||
// Playback rate
|
||||
get playbackRate() {
|
||||
get playbackRate(): number {
|
||||
return this._playbackRate
|
||||
}
|
||||
|
||||
@@ -158,7 +159,7 @@ export default class VideoShim {
|
||||
}
|
||||
|
||||
// Volume
|
||||
get volume() {
|
||||
get volume(): number {
|
||||
return this._volume
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ export default class VideoShim {
|
||||
this.events.emit('volumechange')
|
||||
}
|
||||
|
||||
get muted() {
|
||||
get muted(): boolean {
|
||||
return this._muted
|
||||
}
|
||||
|
||||
@@ -223,56 +224,56 @@ export default class VideoShim {
|
||||
this.option.poster = v
|
||||
}
|
||||
|
||||
get autoplay() {
|
||||
get autoplay(): boolean {
|
||||
return this.option.autoplay || false
|
||||
}
|
||||
|
||||
set autoplay(v: unknown) {}
|
||||
|
||||
get loop() {
|
||||
get loop(): boolean {
|
||||
return this.option.loop || false
|
||||
}
|
||||
|
||||
set loop(v: unknown) {}
|
||||
|
||||
get controls() {
|
||||
get controls(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
set controls(v: unknown) {}
|
||||
|
||||
get playsInline() {
|
||||
get playsInline(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
set playsInline(v: unknown) {}
|
||||
|
||||
get crossOrigin() {
|
||||
get crossOrigin(): string {
|
||||
return this.option.crossOrigin || ''
|
||||
}
|
||||
|
||||
set crossOrigin(v: unknown) {}
|
||||
|
||||
get preload() {
|
||||
get preload(): string {
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
set preload(v: unknown) {}
|
||||
|
||||
get defaultMuted() {
|
||||
get defaultMuted(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
set defaultMuted(v: unknown) {}
|
||||
|
||||
get defaultPlaybackRate() {
|
||||
get defaultPlaybackRate(): number {
|
||||
return 1
|
||||
}
|
||||
|
||||
set defaultPlaybackRate(v: unknown) {}
|
||||
|
||||
// Methods
|
||||
canPlayType(_type: string) {
|
||||
canPlayType(_type: string): 'maybe' {
|
||||
return 'maybe'
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ export default class VideoShim {
|
||||
return this.canvas.getBoundingClientRect()
|
||||
}
|
||||
|
||||
requestVideoFrameCallback(callback: VideoFrameRequestCallback): number {
|
||||
requestVideoFrameCallback(callback: SyntheticFrameCallback): number {
|
||||
return requestFrame(this, callback)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import type VideoShim from './VideoShim'
|
||||
|
||||
export function bridgeCanvas(canvas: HTMLCanvasElement, shim: VideoShim): void {
|
||||
const methods = new Map<string, (...args: unknown[]) => unknown>()
|
||||
// Dynamic forwarding is intentional: own and direct-prototype members are historical API.
|
||||
const native = canvas as unknown as Record<string, unknown>
|
||||
const members = shim as unknown as Record<string, unknown>
|
||||
for (const name in canvas) {
|
||||
if (typeof native[name] === 'function') {
|
||||
// Preserve the historical second read, including accessor side effects and binding errors.
|
||||
const method = native[name] as (...args: unknown[]) => unknown
|
||||
methods.set(name, method.bind(canvas))
|
||||
}
|
||||
}
|
||||
const names = new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim)),
|
||||
])
|
||||
for (const name of names) {
|
||||
if (name === 'constructor' || name in canvas)
|
||||
continue
|
||||
Object.defineProperty(canvas, name, {
|
||||
get() {
|
||||
const value = members[name]
|
||||
return typeof value === 'function' ? value.bind(shim) : value
|
||||
},
|
||||
set(value: unknown) {
|
||||
members[name] = value
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
})
|
||||
}
|
||||
for (const [name, method] of methods)
|
||||
native[name] = (...args: unknown[]) => method(...args)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export function releaseAll(actions: (() => void)[]): void {
|
||||
const failures: unknown[] = []
|
||||
for (const action of actions) {
|
||||
try {
|
||||
action()
|
||||
}
|
||||
catch (error) { failures.push(error) }
|
||||
}
|
||||
if (failures.length)
|
||||
throw failures[0]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { ProxyOptions, ShimHost } from './engine-types'
|
||||
import type { MenuHost } from './m3u8-types'
|
||||
import type VideoShim from './VideoShim'
|
||||
import { releaseAll } from './cleanup'
|
||||
import { setupM3u8Controls } from './m3u8'
|
||||
|
||||
export interface EntryHost extends ShimHost, MenuHost {
|
||||
constructor: ShimHost['constructor'] & { utils: { createElement: (tag: 'canvas') => HTMLCanvasElement } }
|
||||
option: ShimHost['option'] & { autoSize?: boolean }
|
||||
template?: { $player?: HTMLElement }
|
||||
mediabunny?: unknown
|
||||
}
|
||||
|
||||
export function entryLifecycle(art: EntryHost, canvas: HTMLCanvasElement, shim: VideoShim, option: ProxyOptions) {
|
||||
let closed = false
|
||||
let controls: ReturnType<typeof setupM3u8Controls>
|
||||
function resize(): void {
|
||||
if (closed || !art.template?.$player || art.option.autoSize)
|
||||
return
|
||||
Object.assign(canvas.style, { width: '100%', height: '100%', objectFit: 'contain' })
|
||||
}
|
||||
function destroy(): void {
|
||||
if (closed)
|
||||
return
|
||||
closed = true
|
||||
releaseAll([
|
||||
() => controls?.destroy(),
|
||||
() => {
|
||||
if (art.mediabunny === shim)
|
||||
delete art.mediabunny
|
||||
},
|
||||
() => shim.destroy(),
|
||||
() => art.off?.('resize', resize),
|
||||
() => art.off?.('video:loadedmetadata', resize),
|
||||
() => art.off?.('destroy', destroy),
|
||||
])
|
||||
}
|
||||
function install(): void {
|
||||
art.on('resize', resize)
|
||||
art.on('video:loadedmetadata', resize)
|
||||
controls = setupM3u8Controls({ art, shim, option })
|
||||
art.on('destroy', destroy)
|
||||
}
|
||||
return { install, destroy }
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
import { setupM3u8Controls } from './m3u8.js'
|
||||
/**
|
||||
* ArtPlayer MediaBunny Proxy
|
||||
* Main entry point
|
||||
*/
|
||||
import VideoShim from './VideoShim.js'
|
||||
|
||||
export default function artplayerProxyMediabunny(option = {}) {
|
||||
return (art) => {
|
||||
const { constructor } = art
|
||||
const { createElement } = constructor.utils
|
||||
|
||||
// Create canvas element
|
||||
const canvas = createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// Create video shim
|
||||
const shim = new VideoShim({
|
||||
art,
|
||||
canvas,
|
||||
ctx,
|
||||
option,
|
||||
})
|
||||
art.mediabunny = shim
|
||||
|
||||
// Proxy canvas methods to shim
|
||||
const originalCanvasMethods = {}
|
||||
for (const prop in canvas) {
|
||||
if (typeof canvas[prop] === 'function') {
|
||||
originalCanvasMethods[prop] = canvas[prop].bind(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
// Get all properties from shim instance and prototype
|
||||
const propertyNames = new Set([
|
||||
...Object.getOwnPropertyNames(shim),
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(shim)),
|
||||
])
|
||||
|
||||
// Add shim properties to canvas
|
||||
for (const prop of propertyNames) {
|
||||
if (prop === 'constructor')
|
||||
continue
|
||||
if (!(prop in canvas)) {
|
||||
Object.defineProperty(canvas, prop, {
|
||||
get() {
|
||||
const value = shim[prop]
|
||||
return typeof value === 'function' ? value.bind(shim) : value
|
||||
},
|
||||
set(v) {
|
||||
shim[prop] = v
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original canvas methods
|
||||
for (const prop in originalCanvasMethods) {
|
||||
canvas[prop] = (...args) => originalCanvasMethods[prop](...args)
|
||||
}
|
||||
|
||||
// Handle resize
|
||||
function resize() {
|
||||
const player = art.template?.$player
|
||||
if (!player || art.option.autoSize)
|
||||
return
|
||||
|
||||
Object.assign(canvas.style, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'contain',
|
||||
})
|
||||
}
|
||||
|
||||
art.on('resize', resize)
|
||||
art.on('video:loadedmetadata', resize)
|
||||
setupM3u8Controls({ art, shim, option })
|
||||
|
||||
// Cleanup on destroy
|
||||
art.on('destroy', () => {
|
||||
delete art.mediabunny
|
||||
shim.destroy()
|
||||
})
|
||||
|
||||
return canvas
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
import type { ProxyOptions } from './engine-types'
|
||||
import type { EntryHost } from './entry-lifecycle'
|
||||
import { bridgeCanvas } from './canvas-bridge'
|
||||
import { entryLifecycle } from './entry-lifecycle'
|
||||
import VideoShim from './VideoShim'
|
||||
|
||||
export default function artplayerProxyMediabunny(option: ProxyOptions = {}) {
|
||||
return (player: Artplayer): HTMLCanvasElement => {
|
||||
// Runtime constructor utilities and the owned alias exceed the historical consumer facade.
|
||||
const art = player as unknown as EntryHost
|
||||
const { createElement } = art.constructor.utils
|
||||
const canvas = createElement('canvas')
|
||||
const shim = new VideoShim({ art, canvas, ctx: canvas.getContext('2d'), option })
|
||||
const lifecycle = entryLifecycle(art, canvas, shim, option)
|
||||
try {
|
||||
art.mediabunny = shim
|
||||
bridgeCanvas(canvas, shim)
|
||||
lifecycle.install()
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
lifecycle.destroy()
|
||||
}
|
||||
catch (failure) { console.warn('MediaBunny entry setup cleanup:', failure) }
|
||||
throw error
|
||||
}
|
||||
return canvas
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the 1.0.0 CommonJS namespace path without changing the callable factory type.
|
||||
Object.defineProperty(artplayerProxyMediabunny, 'default', { value: artplayerProxyMediabunny, writable: true, configurable: true })
|
||||
@@ -1,16 +1,5 @@
|
||||
import type { DisplayConfig, Label, MenuHost, MenuModel, SelectorItem } from './m3u8-types'
|
||||
|
||||
export function releaseAll(actions: (() => void)[]): void {
|
||||
const failures: unknown[] = []
|
||||
for (const action of actions) {
|
||||
try {
|
||||
action()
|
||||
}
|
||||
catch (error) { failures.push(error) }
|
||||
}
|
||||
if (failures.length)
|
||||
throw failures[0]
|
||||
}
|
||||
import { releaseAll } from './cleanup'
|
||||
|
||||
export default function createMenu(art: MenuHost, name: string, icon: string) {
|
||||
const owned = { control: false, setting: false }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Label, SelectorItem, SetupOptions } from './m3u8-types'
|
||||
import $audio from '../../artplayer-plugin-hls-control/src/audio.svg?raw'
|
||||
import $quality from '../../artplayer-plugin-hls-control/src/quality.svg?raw'
|
||||
import createMenu, { releaseAll } from './m3u8-menu'
|
||||
import { releaseAll } from './cleanup'
|
||||
import createMenu from './m3u8-menu'
|
||||
import { audioModel, qualityModel } from './m3u8-model'
|
||||
|
||||
export function setupM3u8Controls({ art, shim, option }: SetupOptions) {
|
||||
@@ -95,10 +96,16 @@ export function setupM3u8Controls({ art, shim, option }: SetupOptions) {
|
||||
}
|
||||
catch (error) { console.warn('MediaBunny HLS listener cleanup:', error) }
|
||||
}
|
||||
art.on('video:loadedmetadata', refresh)
|
||||
art.on('restart', refresh)
|
||||
art.on('video:loadstart', invalidate)
|
||||
art.on('video:error', invalidate)
|
||||
art.on('destroy', destroy)
|
||||
return { update }
|
||||
try {
|
||||
art.on('video:loadedmetadata', refresh)
|
||||
art.on('restart', refresh)
|
||||
art.on('video:loadstart', invalidate)
|
||||
art.on('video:error', invalidate)
|
||||
art.on('destroy', destroy)
|
||||
}
|
||||
catch (error) {
|
||||
destroy()
|
||||
throw error
|
||||
}
|
||||
return { update, destroy }
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { SyntheticFrameCallback } from '../types/artplayer-proxy-mediabunny'
|
||||
import type { EnginePort } from './engine-types'
|
||||
|
||||
interface FrameOwner {
|
||||
@@ -20,7 +21,7 @@ function state(owner: FrameOwner): FrameState {
|
||||
return value
|
||||
}
|
||||
|
||||
export function requestFrame(owner: FrameOwner, callback: VideoFrameRequestCallback): number {
|
||||
export function requestFrame(owner: FrameOwner, callback: SyntheticFrameCallback): number {
|
||||
const resource = state(owner)
|
||||
if (resource.closed)
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
import type { Option, Result } from './artplayer-proxy-mediabunny.js'
|
||||
|
||||
declare const artplayerProxyMediabunny: (option?: Option) => (art: Artplayer) => Result
|
||||
export default artplayerProxyMediabunny
|
||||
export type { HlsAudio, HlsLevel, HlsState, MediaBunnyCanvas, MediaBunnyPlayer, MediaBunnyShim, MediaListener, Option, Result, SyntheticFrameCallback, SyntheticFrameMetadata } from './artplayer-proxy-mediabunny.js'
|
||||
@@ -1,6 +1,6 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
interface Option {
|
||||
export interface Option {
|
||||
m3u8?: {
|
||||
quality?: {
|
||||
control?: boolean
|
||||
@@ -101,7 +101,9 @@ interface Option {
|
||||
crossOrigin?: string
|
||||
}
|
||||
|
||||
type Result = HTMLCanvasElement
|
||||
export type Result = HTMLCanvasElement
|
||||
|
||||
export type { HlsAudio, HlsLevel, HlsState, MediaBunnyCanvas, MediaBunnyPlayer, MediaBunnyShim, MediaListener, SyntheticFrameCallback, SyntheticFrameMetadata } from './media'
|
||||
|
||||
declare const artplayerProxyMediabunny: (option?: Option) => (art: Artplayer) => Result
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
export interface HlsLevel {
|
||||
id: number
|
||||
index: number
|
||||
name: string | null
|
||||
height: number
|
||||
bitrate: number
|
||||
/** SDK object; narrow with the SDK version used by your application. */
|
||||
track: unknown
|
||||
}
|
||||
|
||||
export interface HlsAudio {
|
||||
id: number
|
||||
index: number
|
||||
name: string | null
|
||||
lang: string
|
||||
language: string
|
||||
bitrate: number
|
||||
/** SDK object; narrow with the SDK version used by your application. */
|
||||
track: unknown
|
||||
}
|
||||
|
||||
export interface HlsState {
|
||||
levels: HlsLevel[]
|
||||
audios: HlsAudio[]
|
||||
currentLevel: HlsLevel | null
|
||||
currentAudio: HlsAudio | null
|
||||
videoMode: 'auto' | 'manual'
|
||||
audioMode: 'auto' | 'manual'
|
||||
}
|
||||
|
||||
/** Historical RAF estimates, not decoded-frame or network timing measurements. */
|
||||
export interface SyntheticFrameMetadata {
|
||||
/** Historical media time in seconds, unlike the native video API. */
|
||||
presentationTime: number
|
||||
expectedDisplayTime: number
|
||||
width: number
|
||||
height: number
|
||||
mediaTime: number
|
||||
presentedFrames: number
|
||||
processingDuration: number
|
||||
captureTime: number
|
||||
receiveTime: number
|
||||
rtpTimestamp: number
|
||||
}
|
||||
|
||||
export type SyntheticFrameCallback = (now: number, metadata: SyntheticFrameMetadata) => void
|
||||
export type MediaListener = (event: Event & { detail: unknown }) => unknown
|
||||
|
||||
/** Opt-in media surface of art.mediabunny; decoder internals are not part of this view. */
|
||||
export interface MediaBunnyShim {
|
||||
canvas: HTMLCanvasElement
|
||||
/** Runtime also accepts SDK sources; the default Option keeps its historical input union. */
|
||||
src: unknown
|
||||
readonly currentSrc: unknown
|
||||
currentTime: number
|
||||
readonly duration: number
|
||||
/** Synthetic full-duration/current-time ranges, not measured network buffers. */
|
||||
readonly buffered: TimeRanges
|
||||
readonly played: TimeRanges
|
||||
readonly seekable: TimeRanges
|
||||
readonly paused: boolean
|
||||
readonly playing: boolean
|
||||
readonly ended: boolean
|
||||
readonly seeking: boolean
|
||||
readonly readyState: number
|
||||
readonly networkState: number
|
||||
readonly error: { code: number, message: string } | null
|
||||
volume: number
|
||||
muted: boolean
|
||||
playbackRate: number
|
||||
readonly videoWidth: number
|
||||
readonly videoHeight: number
|
||||
poster: string
|
||||
/** The following setters are inert; use Option for autoplay/loop/crossOrigin. */
|
||||
autoplay: boolean
|
||||
loop: boolean
|
||||
controls: boolean
|
||||
playsInline: boolean
|
||||
crossOrigin: string
|
||||
preload: string
|
||||
defaultMuted: boolean
|
||||
defaultPlaybackRate: number
|
||||
play: () => Promise<void>
|
||||
pause: () => void
|
||||
load: () => void
|
||||
/** Always returns "maybe"; it does not probe codec/browser support. */
|
||||
canPlayType: (type: string) => 'maybe'
|
||||
getM3u8State: () => Promise<HlsState | null>
|
||||
switchM3u8Quality: (value: unknown) => Promise<void>
|
||||
switchM3u8Audio: (value: unknown) => Promise<void>
|
||||
createTimeRanges: (start: number, end: number) => TimeRanges
|
||||
requestVideoFrameCallback: (callback: SyntheticFrameCallback) => number
|
||||
cancelVideoFrameCallback: (id: number) => void
|
||||
addEventListener: (type: string, listener: MediaListener) => void
|
||||
removeEventListener: (type: string, listener: MediaListener) => void
|
||||
getBoundingClientRect: () => DOMRect
|
||||
setAttribute: (name: string, value: unknown) => void
|
||||
destroy: () => void
|
||||
}
|
||||
|
||||
/** Native Canvas methods win collisions, including DOM event and attribute methods. */
|
||||
export type MediaBunnyCanvas = HTMLCanvasElement & Omit<MediaBunnyShim, keyof HTMLCanvasElement>
|
||||
|
||||
/** The alias is installed by the proxy and removed on player destruction. */
|
||||
export type MediaBunnyPlayer = Artplayer & { mediabunny?: MediaBunnyShim }
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"task": "PKG-MB-08",
|
||||
"sourceCommit": "8a2b56e829a3fa9af5e440a11cede8d1775ded10",
|
||||
"sourceSha256LF": {
|
||||
"packages/artplayer-proxy-mediabunny/src/index.js": "4e1be8937908ff6e6ef8a4e863e22b4468312cf31d66ac299322e475757773bb",
|
||||
"packages/artplayer-proxy-mediabunny/src/VideoShim.ts": "1fef3028a3937182c31503129655efdb88118ea0f6d3be96e9be99dcbcc5fdc7",
|
||||
"packages/artplayer-proxy-mediabunny/src/engine-types.ts": "6714070f954f7345f33cd93655facc6aa94fd34fa1be0375361962d54fa6e180",
|
||||
"packages/artplayer-proxy-mediabunny/types/artplayer-proxy-mediabunny.d.ts": "af2083224d0760752d83eb2ac86b1dea5586455b81d47e4ba70f9f6a874a75d8",
|
||||
"packages/artplayer-proxy-mediabunny/package.json": "96b09ce19ea7914485aa9780329a0cfc43b18e38232ea1dd92d2e6dc27310055",
|
||||
"packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js": "303b1a5d71b661c7d973c52850f5e68766990c86c59fe438f0070bd38edbfb0e"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
# PKG-MB-08 检查点:入口、Canvas 转发与公开声明
|
||||
|
||||
任务保持doing。本包自有源码已全部迁移为34个strict TS模块,但不可解码轨道的回退与
|
||||
就绪能力审查尚未完成;不能据此把整个MB-08标done。进入本轮的源码/产物冻结于
|
||||
[入口基线](../baselines/mb-entry-source.json),对应8a2b56e8。
|
||||
|
||||
## 实现与兼容边界
|
||||
|
||||
入口从index.js迁至index.ts,Canvas转发、入口生命周期和通用清理分别进入
|
||||
canvas-bridge.ts、entry-lifecycle.ts、cleanup.ts。后者从HLS菜单层下移,避免入口依赖
|
||||
UI层的通用释放实现。HLS初始化支持部分注册失败时撤销,返回内部destroy供入口回滚。
|
||||
|
||||
保留createElement脱离receiver调用、原生可枚举方法getter读取两次、Canvas原生方法优先、
|
||||
shim own/直接prototype转发、每次读取绑定方法、getter-only写入TypeError等旧行为。
|
||||
资源状态不增加Canvas公开字段。销毁取消入口resize/metadata/destroy监听,已保存回调失效;
|
||||
只删除仍属于当前shim的art.mediabunny,保留调用方替换的别名。一个清理步骤抛错仍尝试其余步骤,
|
||||
setup失败保留原始异常。缺少off的历史自定义host仍可用,残留回调在销毁后保持无副作用。
|
||||
|
||||
公开默认工厂继续是可选Option到同步initializer、精确HTMLCanvasElement的签名;不将Result
|
||||
缩窄成Canvas与shim交叉类型,不增加强制default属性,也不全局增强Artplayer。
|
||||
新增type-only Option/Result、MediaBunnyShim、MediaBunnyCanvas、MediaBunnyPlayer、HLS状态和
|
||||
SyntheticFrameCallback等。MediaBunnyPlayer的别名可缺省;Canvas视图明确原生DOM方法优先。
|
||||
VideoShim实际implements媒体视图,getter显式返回类型防止unknown setter污染读取推导。
|
||||
|
||||
声明不引入MediaBunny SDK的现代DOM/Disposable依赖;track对象保持unknown,由消费者按
|
||||
自用SDK缩窄。SyntheticFrameMetadata保留历史估算语义,尤其presentationTime是媒体秒数。
|
||||
canPlayType仍恒为maybe,TimeRanges不代表真实网络缓冲;新类型不制造能力承诺。
|
||||
|
||||
新增.d.mts及条件types路由修正NodeNext ESM不可调用问题,typesVersions补齐经典解析的legacy。
|
||||
CommonJS继续保留旧default声明门面;运行时直接工厂增加不可枚举、可写、可配置的default自身别名,
|
||||
同时支持1.0.0的namespace.default和1.2.0的直接调用。整工厂双向赋值使用实际两个npm冻结声明检查。
|
||||
|
||||
## 验证与迭代
|
||||
|
||||
15条入口断言在旧main上6通过/9失败;候选包专项241通过,main/legacy各217通过,其中
|
||||
各213条候选断言和4条冻结正常控制。HLS测试将清理断言限定为其自己拥有的5个listener;
|
||||
新增入口断言单独检查额外3个listener,不再错误假设resize回调应一直残留。
|
||||
|
||||
新增12浏览器用例使用真实Canvas与发布版/候选核心,覆盖方法receiver、DOM事件/属性优先、
|
||||
清理和替换别名。旧main同12项全部复现缺陷;候选通过。首轮夹具错误地把后续核心尺寸处理后的
|
||||
inline style当作入口独占结果,现单独调用已注册入口resize callback验证其行为;首轮18份失败
|
||||
report/results保留,并合并了两组重复场景。未增加重试或放宽超时。最终8个MediaBunny浏览器文件
|
||||
共120通过:新增12项为Canvas/生命周期检查,原有29项为Windows WebKit能力对照,均不冒充播放。
|
||||
|
||||
新增yarn test:mediabunny-types-package:实际pack核心与代理,在工作区外安装两个冻结发布包及
|
||||
候选,offline/frozen重装、锁文件和安装文件哈希检查。17组编译/导出矩阵通过;候选7组各拒绝
|
||||
15条非法用法,并验证原始两份声明的整工厂双向赋值。保留旧版NodeNext ESM的准确诊断作为负例。
|
||||
最初pack发现tsconfig泄漏,补入.npmignore后修复。旧TS不读取SDK声明,所有解析文件限于安装
|
||||
目录或编译器标准库。该检查并非MB-10完整分发/许可验收。
|
||||
|
||||
最终源码、打包安装、构建与浏览器证据见[检查点记录](../baselines/mb-entry-checkpoint.json)。
|
||||
完整CI1402项(1247单元+14工程+141基线)通过,另44重复契约,324生产TS。
|
||||
首轮全局noUncheckedIndexedAccess发现消费者夹具直接读取空数组首项,改为可选访问后
|
||||
完整CI和17组实际安装矩阵重跑通过;运行时产物未变化。本轮未添加依赖,Yarn和SDK锁版本不变。
|
||||
|
||||
## 立即接续
|
||||
|
||||
继续检查已有track但codec/canDecode不可用时,VideoEngine/AudioEngine的历史空轨道回退。
|
||||
必须保留有效audio-only/video-only播放,阻止没有可用解码器时假就绪,并覆盖加载、HLS替换、
|
||||
晚能力查询、pause/source/destroy和错误事件顺序。不要仅凭新增接口或现有WebKit负例宣布修复。
|
||||
长播放/AV sync/设备及组合由MB-09,示例、安装分发及MPL/source notice由MB-10继续。
|
||||
本检查点独立本地提交,回退该提交即可恢复本轮前状态;冻结历史输入保持不变,不推送或发布。
|
||||
|
||||
接续缺陷已用实际SDK解析的video-only MP4复现:受控canDecode=false与AudioContext下,
|
||||
旧/候选均为videoSink=null、audioSink=null,却readyState=4并发loadedmetadata/data/canplay/through;
|
||||
replaceTracks后同样假就绪。该受控复现见检查点记录,不能当作真实浏览器解码结果。
|
||||
@@ -0,0 +1,11 @@
|
||||
import Artplayer from 'artplayer'
|
||||
import factory from 'artplayer-proxy-mediabunny'
|
||||
|
||||
type Option = Parameters<typeof factory>[0]
|
||||
const art = new Artplayer({ container: '#player', url: 'movie.mp4', proxy: factory() })
|
||||
const replacement: typeof factory = (_option?: Option) => (_art: Artplayer) => document.createElement('canvas')
|
||||
const canvas: ReturnType<ReturnType<typeof factory>> = document.createElement('canvas')
|
||||
factory()
|
||||
factory(undefined)
|
||||
factory({ volume: 0.7, source: new Blob([]), loadTimeout: 1000 })(art)
|
||||
void [replacement, canvas]
|
||||
@@ -0,0 +1,34 @@
|
||||
import Artplayer from 'artplayer'
|
||||
import factory from 'artplayer-proxy-mediabunny'
|
||||
|
||||
const art = new Artplayer({ container: '#player', url: 'movie.mp4', proxy: factory() })
|
||||
type Option = NonNullable<Parameters<typeof factory>[0]>
|
||||
type HistoricalFactory = (option?: Option) => (art: Artplayer) => HTMLCanvasElement
|
||||
const assignOld: HistoricalFactory = factory
|
||||
const assignNew: typeof factory = (_option?: Option) => (_art: Artplayer) => document.createElement('canvas')
|
||||
const initializer: ReturnType<typeof factory> = () => document.createElement('canvas')
|
||||
const canvas: ReturnType<ReturnType<typeof factory>> = document.createElement('canvas')
|
||||
const options: Option = {
|
||||
source: new ReadableStream<Uint8Array>(),
|
||||
loadTimeout: 1000,
|
||||
timeupdateInterval: 250,
|
||||
avSyncTolerance: 0.12,
|
||||
dropLateFrames: false,
|
||||
poster: 'poster.jpg',
|
||||
preflightRange: false,
|
||||
volume: 0.7,
|
||||
muted: false,
|
||||
autoplay: false,
|
||||
loop: false,
|
||||
crossOrigin: 'anonymous',
|
||||
m3u8: {
|
||||
quality: { control: true, setting: true, title: 'Quality', auto: 'Auto', getName: level => `${level.id}/${level.index}/${level.name}/${level.height}/${level.bitrate}` },
|
||||
audio: { control: true, setting: true, title: 'Audio', auto: 'Auto', getName: track => `${track.id}/${track.index}/${track.name}/${track.lang}/${track.language}/${track.bitrate}` },
|
||||
},
|
||||
}
|
||||
factory()
|
||||
factory(undefined)
|
||||
factory(options)(art).getContext('2d')
|
||||
factory({ source: new Blob([]) })
|
||||
factory({ source: 'movie.mp4' })
|
||||
void [assignOld, assignNew, initializer, canvas]
|
||||
@@ -80,3 +80,14 @@ main/legacy各159通过。最终78浏览器含16新增原生音频场景及8新
|
||||
11通过/32失败,候选专项226及main/legacy各202通过。最终108浏览器含20新增原生HLS
|
||||
新旧核心/UI/SDK场景与10新增能力对照。旧main同30项14通过/16失败,未把能力缺失计作播放。
|
||||
全部报告/results已归档;完整CI1387项及44重复契约通过,320生产TS;完成后立即专用提交,MB-LIFE-01剩余组合/长播范围仍open。
|
||||
|
||||
## PKG-MB-08 入口与声明检查点(doing)
|
||||
|
||||
index实际迁移,入口/Canvas桥/生命周期/清理拆为四模块,全包34生产TS。
|
||||
精确默认Canvas和整工厂保留;可选shim/HLS/RAF视图、ESM声明及旧legacy解析已验证。
|
||||
15条新断言旧main6通过/9失败,候选专项241及main/legacy各217通过。新增12真实Canvas/
|
||||
新旧核心场景通过,旧main同12项失败;最终8文件120浏览器通过,其中29能力对照不计作播放。
|
||||
实际打包安装17类型矩阵通过,候选7组各拒绝15非法用法;旧NodeNext失败保留负例。
|
||||
见[变更](changes/2026-09-12-PKG-MB-08-entry-checkpoint.md)和[证据](baselines/mb-entry-checkpoint.json)。
|
||||
完整CI1402项及44重复契约通过,324生产TS。受控canDecode=false已复现旧/候选
|
||||
video-only输入及替换无sink仍readyState=4;任务保持doing,下一步修复该能力边界。
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 111 / doing 7 / blocked 0 / done 104 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 110 / doing 8 / blocked 0 / done 104 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -336,7 +336,7 @@
|
||||
| PKG-MB-05 | artplayer-proxy-mediabunny<br>整理视频解码、seek 和帧释放 | PKG-MB-04 | VideoEngine 调度、队列、晚帧处理和资源释放 | 暂停/seek/切源无旧帧覆盖,默认丢帧策略不变 | H | done |
|
||||
| PKG-MB-06 | artplayer-proxy-mediabunny<br>整理音频解码、时钟与同步 | PKG-MB-04 | AudioEngine 时钟、缓冲和 AudioContext 归属 | AV sync、倍速、无音轨、静音/音量和暂停恢复通过 | H | done |
|
||||
| PKG-MB-07 | artplayer-proxy-mediabunny<br>整理 HLS 配对轨道与 selector | PKG-MB-05, PKG-MB-06, CORE-14 | m3u8 配对、质量/音频选择及拓扑清理 | 实际选择高亮、切到无轨道来源清理、无重复 readiness | H | done |
|
||||
| PKG-MB-08 | artplayer-proxy-mediabunny<br>完成 TS 与媒体能力声明 | PKG-MB-07, ENG-04, ENG-06 | 8 个自有 JS 模块迁移与 Result/shim 类型 | 解码器/Stream/DOM 类型清楚,旧 Option 和 art.mediabunny 使用保持 | H | todo |
|
||||
| PKG-MB-08 | artplayer-proxy-mediabunny<br>完成 TS 与媒体能力声明 | PKG-MB-07, ENG-04, ENG-06 | 8 个自有 JS 模块迁移与 Result/shim 类型 | 解码器/Stream/DOM 类型清楚,旧 Option 和 art.mediabunny 使用保持 | H | doing |
|
||||
| PKG-MB-09 | artplayer-proxy-mediabunny<br>完成新旧核心和真实媒体组合 | PKG-MB-08, CORE-22 | 跨浏览器能力、长播放、DPiP、HLS 音轨/质量报告 | 资源释放、事件顺序、AV sync 与支持范围满足基线 | H | todo |
|
||||
| PKG-MB-10 | artplayer-proxy-mediabunny<br>完成分发和文档 | PKG-MB-09, ENG-07 | mediabunny.js、README、依赖版本、三产物和许可 | tarball 可消费、无意外依赖升级、旧调用与回退可用 | H | todo |
|
||||
|
||||
@@ -527,4 +527,5 @@
|
||||
- PKG-MB-05: [记录](baselines/mb-video-source.json) [记录](changes/2026-09-12-PKG-MB-05-video.md) [记录](baselines/mb-video-validation.json)
|
||||
- PKG-MB-06: [记录](baselines/mb-audio-source.json) [记录](changes/2026-09-12-PKG-MB-06-audio.md) [记录](baselines/mb-audio-validation.json)
|
||||
- PKG-MB-07: [记录](baselines/mb-hls-source.json) [记录](changes/2026-09-12-PKG-MB-07-hls.md) [记录](baselines/mb-hls-validation.json)
|
||||
- PKG-MB-08: [记录](baselines/mb-entry-source.json) [记录](changes/2026-09-12-PKG-MB-08-entry-checkpoint.md) [记录](baselines/mb-entry-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,18 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-MB-08检查点:入口TS与兼容媒体声明(doing)
|
||||
|
||||
全包34个自有生产模块已转TS,入口/Canvas桥/生命周期/清理分离。默认工厂仍可选Option、
|
||||
同步初始化与精确Canvas结果;新增可选媒体/HLS/RAF类型,修复旧legacy及NodeNext ESM解析。
|
||||
15条入口断言旧main6通过/9失败,候选专项241及main/legacy各217通过。最终120浏览器
|
||||
全部通过,含12原生Canvas/新旧核心场景及29能力对照;旧main相同12入口场景全部失败。
|
||||
实际安装17组类型/导出矩阵通过,候选7组各拒绝15非法用法;整工厂按冻结声明双向赋值。
|
||||
完整CI1402项及44重复契约通过,324生产TS。源码/产物/浏览器/安装包哈希已核验。
|
||||
见[变更](changes/2026-09-12-PKG-MB-08-entry-checkpoint.md)与[证据](baselines/mb-entry-checkpoint.json)。
|
||||
222项:104 done、8 doing、110 todo。本地检查点提交,不计任务完成。
|
||||
下一步:修复受控canDecode=false时video-only输入及替换无可用sink却发布就绪;
|
||||
该问题旧/候选均已复现。MB-08、能力风险及后续分发/许可/设备门槛仍未关闭。
|
||||
|
||||
## PKG-MB-07完成:HLS配对、选择意图与UI拓扑
|
||||
|
||||
m3u8实际迁移为四个TS模块,包内30生产TS,仅剩index.js待MB-08。
|
||||
|
||||
+7
-4
@@ -3679,13 +3679,15 @@
|
||||
"PKG-MB-08",
|
||||
"PKG-MB-10"
|
||||
],
|
||||
"compatibleResolution": "Preserve optional whole-factory and exact Canvas assignment, model explicit shim capabilities separately and verify historical default namespace and callable consumers.",
|
||||
"compatibleResolution": "Preserve optional whole-factory and exact Canvas assignment, model explicit shim capabilities separately and verify historical default namespace and callable consumers. MB-08 entry checkpoint preserves exact optional factory/Canvas assignment, adds opt-in checked media types without SDK imports, and fixes classic legacy/NodeNext ESM resolution. Seventeen installed compiler cases plus runtime exports passed against two frozen releases and candidate; seven candidate cases reject fifteen invalid uses each. Type facade/runtime export part is verified; final MB-08 capability and MB-10 distribution gates remain open.",
|
||||
"closureCriteria": "Actual installed old/candidate compiler and runtime export matrices pass with negative controls; any incompatible declaration generation requires an explicit decision.",
|
||||
"status": "open",
|
||||
"evidence": [
|
||||
"refactor/baselines/mb-release.json",
|
||||
"refactor/baselines/mb-contract.md",
|
||||
"refactor/changes/2026-09-12-PKG-MB-01-contract.md"
|
||||
"refactor/changes/2026-09-12-PKG-MB-01-contract.md",
|
||||
"refactor/changes/2026-09-12-PKG-MB-08-entry-checkpoint.md",
|
||||
"refactor/baselines/mb-entry-checkpoint.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3715,13 +3717,14 @@
|
||||
"PKG-MB-04",
|
||||
"PKG-MB-09"
|
||||
],
|
||||
"compatibleResolution": "Use explicit coordinated readiness and failure state, preserving valid event order while preventing duplicate or stale callbacks; evaluate trackless behavior separately from normal media. MB-04 coordinates metadata once, suppresses stale readiness and rejects trackless input before decoder setup. Existing-track unsupported-codec behavior and full native combinations remain MB-05/06/09; risk stays open.",
|
||||
"compatibleResolution": "Use explicit coordinated readiness and failure state, preserving valid event order while preventing duplicate or stale callbacks; evaluate trackless behavior separately from normal media. MB-04 coordinates metadata once, suppresses stale readiness and rejects trackless input before decoder setup. Existing-track unsupported-codec behavior and full native combinations remain MB-05/06/09; risk stays open. MB-08 entry checkpoint reproduces remaining false readiness in both previous and candidate builds: actual SDK-parsed video-only MP4 with controlled canDecode=false and AudioContext yields null audio/video sinks but readyState 4 and all readiness events, including replaceTracks. Keep open for the next MB-08 capability implementation; this controlled case is not native decoding evidence.",
|
||||
"closureCriteria": "Candidate metadata/data/canplay ordering and counts pass normal, absent-track and failure interleavings in native browsers and controlled race tests.",
|
||||
"evidence": [
|
||||
"refactor/changes/2026-09-12-PKG-MB-02-baseline.md",
|
||||
"refactor/baselines/mb-behavior-validation.json",
|
||||
"refactor/changes/2026-09-12-PKG-MB-04-coordination.md",
|
||||
"refactor/baselines/mb-coordination-validation.json"
|
||||
"refactor/baselines/mb-coordination-validation.json",
|
||||
"refactor/baselines/mb-entry-checkpoint.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
|
||||
import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs'
|
||||
import { verifyMbContract } from './mb-contract.mjs'
|
||||
import { hash, readMember } from './releases.mjs'
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:mediabunny-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/mb-package-types-'))
|
||||
console.log(`MediaBunny isolated package evidence: ${output}`)
|
||||
const contract = await verifyMbContract()
|
||||
const packages = []
|
||||
for (const name of ['artplayer', 'artplayer-proxy-mediabunny']) {
|
||||
const archive = path.join(output, `${name}.tgz`)
|
||||
fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name)))
|
||||
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
|
||||
const files = packedFiles(archive)
|
||||
checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : [])
|
||||
packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
|
||||
}
|
||||
const matrix = []
|
||||
const core = packages[0]
|
||||
const candidates = [
|
||||
...contract.baseline.previous.concat(contract.baseline.release).map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })),
|
||||
{ ...packages[1], label: 'candidate' },
|
||||
]
|
||||
for (const plugin of candidates) {
|
||||
const consumer = consumerDirectory()
|
||||
try {
|
||||
writeJson(path.join(consumer, 'package.json'), { name: 'mediabunny-isolated-consumer', private: true, dependencies: Object.fromEntries([core, plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) })
|
||||
fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock'))
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
const lock = fs.readFileSync(path.join(consumer, 'yarn.lock'))
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock)
|
||||
for (const pkg of [core, plugin]) {
|
||||
const root = path.join(consumer, 'node_modules', pkg.name)
|
||||
assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link')
|
||||
for (const [member, expected] of Object.entries(pkg.files))
|
||||
assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`)
|
||||
}
|
||||
const runtimeFile = path.join(consumer, 'exports.cjs')
|
||||
fs.writeFileSync(runtimeFile, `const assert = require('node:assert/strict');
|
||||
for (const name of ['artplayer-proxy-mediabunny', 'artplayer-proxy-mediabunny/legacy']) {
|
||||
const value = require(name);
|
||||
assert.equal(typeof value, '${plugin.version === '1.0.0' ? 'object' : 'function'}');
|
||||
assert.equal(typeof (value.default || value)(), 'function');
|
||||
${plugin.label === 'candidate' ? 'assert.equal(value.default, value); assert.equal(typeof value.default(), "function"); assert.deepEqual(Object.keys(value), []);' : plugin.version === '1.2.0' ? 'assert.equal(value.default, undefined);' : 'assert.equal(typeof value.default, "function");'}
|
||||
}
|
||||
Promise.all(['artplayer-proxy-mediabunny', 'artplayer-proxy-mediabunny/legacy'].map(async name => {
|
||||
const module = await import(name);
|
||||
const value = module.default;
|
||||
assert.equal(typeof (value.default || value)(), 'function');
|
||||
if (name === 'artplayer-proxy-mediabunny') assert.deepEqual(Object.keys(module), ['default']);
|
||||
})).catch(error => { console.error(error); process.exitCode = 1; });`)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
if (plugin.label === 'candidate')
|
||||
modes.push([ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop'])
|
||||
for (const [compiler, mode] of modes) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const fixture = plugin.label === 'candidate' ? 'test/types/mediabunny.ts' : plugin.version === '1.0.0' ? 'refactor/fixtures/consumers/mediabunny-1.0.ts' : 'refactor/fixtures/consumers/mediabunny-published.ts'
|
||||
const source = fs.readFileSync(path.join(workspace, fixture), 'utf8')
|
||||
const filename = path.join(consumer, `consumer.${extension}`)
|
||||
const files = [filename]
|
||||
if (plugin.label === 'candidate') {
|
||||
for (const generation of ['1.0', 'published']) {
|
||||
const historical = path.join(consumer, `historical-${generation}.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, `refactor/fixtures/consumers/mediabunny-${generation}.ts`), historical)
|
||||
files.push(historical)
|
||||
}
|
||||
// Compare complete factories against the actual frozen declaration bytes.
|
||||
for (const release of contract.baseline.previous.concat(contract.baseline.release)) {
|
||||
const name = `frozen-${release.version}`
|
||||
fs.writeFileSync(path.join(consumer, `${name}.d.ts`), readMember(contract.archives.get(release.version), `package/${release.manifest.types.replace(/^\.\//, '')}`))
|
||||
const assignment = path.join(consumer, `${name}-assignment.cts`)
|
||||
fs.writeFileSync(assignment, `import current from 'artplayer-proxy-mediabunny'; import frozen from './${name}'; const forward: typeof frozen = current; const backward: typeof current = frozen; void [forward, backward];`)
|
||||
// TS 4.3 predates .cts. Use an ordinary .ts for classic resolution.
|
||||
const actual = next ? assignment : assignment.replace(/\.cts$/, '.ts')
|
||||
if (actual !== assignment)
|
||||
fs.renameSync(assignment, actual)
|
||||
files.push(actual)
|
||||
}
|
||||
if (mode === 'nodenext-cjs') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import factory = require('artplayer-proxy-mediabunny'); import legacy = require('artplayer-proxy-mediabunny/legacy'); factory.default(); legacy.default({ volume: 0.5 });`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
}
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
|
||||
function compile(code) {
|
||||
fs.writeFileSync(filename, code)
|
||||
const program = compiler.createProgram(files, options)
|
||||
for (const file of program.getSourceFiles()) {
|
||||
const actual = fs.realpathSync(file.fileName)
|
||||
assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`)
|
||||
assert(!/[\\/]node_modules[\\/]mediabunny[\\/]/.test(actual), 'Public declarations must not pull modern SDK types into older compilers')
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
const historicalFailure = plugin.label !== 'candidate' && mode === 'nodenext-esm'
|
||||
writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}-diagnostics.json`), { diagnostics, historicalFailure })
|
||||
if (historicalFailure) {
|
||||
assert.deepEqual(diagnostics.map(item => item.code), plugin.version === '1.0.0'
|
||||
? [2344, 2349, 2322, 2344, 2349, 2349, 2349]
|
||||
: [2349, 2344, 2322, 2322, 2344, 2344, 2322, 7006, 7006, 2349, 2349, 2349, 2349, 2349], 'Unexpected historical NodeNext failure')
|
||||
}
|
||||
else {
|
||||
assert.deepEqual(diagnostics, [], `${plugin.label} ${compiler.version} ${mode}`)
|
||||
}
|
||||
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (plugin.label === 'candidate')
|
||||
assert.equal(invalid.length, 15, 'Installed declarations must reject all invalid uses')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
|
||||
}
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'mediabunny-isolated-package-types', introducedBy: 'PKG-MB-08', scope: 'Two frozen published packages and packed candidate with packed candidate core, installed outside workspace; offline/frozen reinstall, exact bytes, default/legacy CJS/ESM runtime and compiler matrices. Exact optional whole-factory assignments checked against actual historical declaration bytes. New shim types are opt-in, without SDK declaration leakage. Historical NodeNext ESM failures remain explicit negative controls. Not native decoding, old-core runtime combinations or full distribution/license acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: hash(fs.readFileSync(pkg.archive)) })), matrix })
|
||||
console.log(`MediaBunny installed types passed: ${matrix.length} cases; ${output}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
+6
-2
@@ -3546,11 +3546,15 @@
|
||||
"ENG-04",
|
||||
"ENG-06"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "doing",
|
||||
"risk": "H",
|
||||
"deliverable": "8 个自有 JS 模块迁移与 Result/shim 类型",
|
||||
"acceptance": "解码器/Stream/DOM 类型清楚,旧 Option 和 art.mediabunny 使用保持",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"baselines/mb-entry-source.json",
|
||||
"changes/2026-09-12-PKG-MB-08-entry-checkpoint.md",
|
||||
"baselines/mb-entry-checkpoint.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-MB-09",
|
||||
|
||||
@@ -76,7 +76,7 @@ export function runTypechecks() {
|
||||
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'test/types/ads.ts', 'refactor/fixtures/consumers/ads-published.ts', 'refactor/fixtures/consumers/ads-workspace.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
}
|
||||
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts', 'test/types/canvas.ts', 'refactor/fixtures/consumers/canvas-published.ts', 'test/types/dpip.ts', 'refactor/fixtures/consumers/dpip-published.ts'])
|
||||
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts', 'test/types/canvas.ts', 'refactor/fixtures/consumers/canvas-published.ts', 'test/types/mediabunny.ts', 'refactor/fixtures/consumers/mediabunny-1.0.ts', 'refactor/fixtures/consumers/mediabunny-published.ts', 'test/types/dpip.ts', 'refactor/fixtures/consumers/dpip-published.ts'])
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
if (mode === 'nodenext-cjs')
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/commonjs.cts'), 'utf8')), [], 'CommonJS export assignment consumer failed')
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
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 core of ['published', 'candidate']) {
|
||||
for (const scenario of ['native-cleanup', 'replacement']) {
|
||||
test(`MediaBunny ${implementation.name}: ${core} core native Canvas entry ${scenario}`, async ({ page }, testInfo) => {
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
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((scenario) => {
|
||||
const registered = []
|
||||
const removed = []
|
||||
const art = new window.Artplayer({
|
||||
container: '.player',
|
||||
url: '',
|
||||
proxy(player) {
|
||||
const on = player.on
|
||||
const off = player.off
|
||||
player.on = function (name, callback) {
|
||||
registered.push({ name, callback })
|
||||
return on.call(this, name, callback)
|
||||
}
|
||||
player.off = function (name, callback) {
|
||||
removed.push({ name, callback })
|
||||
return off.call(this, name, callback)
|
||||
}
|
||||
return window.mbFactory()(player)
|
||||
},
|
||||
})
|
||||
const canvas = art.video
|
||||
const shim = art.mediabunny
|
||||
const resize = registered.find(item => item.name === 'resize')
|
||||
const metadata = registered.find(item => item.name === 'video:loadedmetadata')
|
||||
const destroy = registered.find(item => item.name === 'destroy')
|
||||
const receiver = canvas.getContext
|
||||
const bounds = canvas.getBoundingClientRect
|
||||
const attributes = canvas.setAttribute
|
||||
const identity = canvas instanceof HTMLCanvasElement && shim.canvas === canvas
|
||||
const context = receiver('2d')?.canvas === canvas
|
||||
const width = bounds().width
|
||||
let clickCount = 0
|
||||
canvas.addEventListener('click', () => clickCount++)
|
||||
canvas.dispatchEvent(new MouseEvent('click'))
|
||||
attributes('src', 'native-only.mp4')
|
||||
canvas.volume = 0.35
|
||||
art.emit('resize')
|
||||
// Core sizing listeners may reset inline styles after proxy resize; isolate its callback.
|
||||
resize.callback()
|
||||
const before = { identity, context, width, clickCount, source: shim.src, attribute: canvas.getAttribute('src'), volume: shim.volume, style: canvas.style.width, maybe: canvas.canPlayType('video/unknown') }
|
||||
const marker = { owner: 'replacement' }
|
||||
if (scenario === 'replacement')
|
||||
art.mediabunny = marker
|
||||
art.destroy(false)
|
||||
canvas.style.width = '43px'
|
||||
resize.callback()
|
||||
metadata.callback()
|
||||
const cleanup = {
|
||||
released: [resize, metadata, destroy].map(item => removed.some(entry => entry.name === item.name && entry.callback === item.callback)),
|
||||
destroyed: shim.engine.destroyed,
|
||||
styleAfter: canvas.style.width,
|
||||
alias: scenario === 'replacement' ? art.mediabunny === marker : !Object.hasOwn(art, 'mediabunny'),
|
||||
}
|
||||
return { before, cleanup, coreVersion: window.Artplayer.version }
|
||||
}, scenario)
|
||||
expect(result.before).toEqual({ identity: true, context: true, width: 640, clickCount: 1, source: null, attribute: 'native-only.mp4', volume: 0.35, style: '100%', maybe: 'maybe' })
|
||||
expect(result.cleanup).toEqual({ released: [true, true, true], destroyed: true, styleAfter: '43px', alias: true })
|
||||
await testInfo.attach('mediabunny-entry', { contentType: 'application/json', body: JSON.stringify({ implementation: implementation.name, sha256: hash(implementation.code), core, scenario, result, scope: 'Actual browser Canvas descriptors and published/candidate core lifecycle. Empty source deliberately excludes decoding; existing media suites cover playback separately.' }) })
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Actual entry bridge and lifecycle with controlled native-like Canvas descriptors.
|
||||
import test from 'node:test'
|
||||
import { mbCandidate, mbEnvironment } from './helpers/mediabunny.js'
|
||||
|
||||
const implementation = await mbCandidate()
|
||||
function environment() {
|
||||
const env = mbEnvironment(implementation)
|
||||
env.art.off = (name, callback) => {
|
||||
env.handlers.set(name, (env.handlers.get(name) || []).filter(item => item !== callback))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
test('MediaBunny entry keeps a callable factory and a writable historical default alias', () => {
|
||||
const env = environment()
|
||||
assert.equal(typeof env.exported, 'function')
|
||||
assert.equal(env.exported.default, env.exported)
|
||||
assert.equal(typeof env.exported.default(), 'function')
|
||||
assert.deepEqual(Object.keys(env.exported), [])
|
||||
const replacement = () => {}
|
||||
env.exported.default = replacement
|
||||
assert.equal(env.exported.default, replacement)
|
||||
assert.equal(typeof env.exported(), 'function')
|
||||
})
|
||||
|
||||
test('MediaBunny entry preserves canvas identity, native receiver and shim binding', async () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
assert.equal(canvas, env.canvas)
|
||||
assert.equal(canvas.canvas, canvas)
|
||||
assert.equal(canvas.art, env.art)
|
||||
assert.equal(env.art.mediabunny.engine, canvas.engine)
|
||||
assert.notEqual(canvas.play, canvas.play)
|
||||
const pause = canvas.pause
|
||||
assert.equal(pause(), undefined)
|
||||
canvas.getContext('2d')
|
||||
assert.equal(env.nativeCalls.at(-1)[2], true)
|
||||
const pending = Promise.resolve()
|
||||
canvas.engine.play = () => pending
|
||||
assert.equal(canvas.play(), pending)
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny entry preserves enumerable forwarding and readonly setter failure', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
const descriptor = Object.getOwnPropertyDescriptor(canvas, 'duration')
|
||||
assert.equal(descriptor.enumerable, true)
|
||||
assert.equal(descriptor.configurable, true)
|
||||
assert.equal(typeof descriptor.get, 'function')
|
||||
assert.equal(typeof descriptor.set, 'function')
|
||||
assert.throws(() => {
|
||||
canvas.duration = 4
|
||||
}, { name: 'TypeError' })
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny entry preserves native collisions and direct-prototype forwarding boundary', () => {
|
||||
const env = environment()
|
||||
env.canvas.currentTime = 13
|
||||
const native = env.canvas.setAttribute
|
||||
const canvas = env.factory()(env.art)
|
||||
assert.equal(canvas.currentTime, 13)
|
||||
assert.notEqual(canvas.setAttribute, native)
|
||||
canvas.setAttribute('src', 'native-only.mp4')
|
||||
assert.equal(env.art.mediabunny.src, null)
|
||||
assert.equal(Object.hasOwn(canvas, 'toString'), false)
|
||||
assert.equal(Object.hasOwn(canvas, 'constructor'), false)
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny resize preserves default styling and autoSize behavior', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
assert.deepEqual(canvas.style, {})
|
||||
env.art.emit('resize')
|
||||
assert.deepEqual(canvas.style, { width: '100%', height: '100%', objectFit: 'contain' })
|
||||
canvas.style.width = '42px'
|
||||
env.art.option.autoSize = true
|
||||
env.art.emit('video:loadedmetadata')
|
||||
assert.equal(canvas.style.width, '42px')
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny entry destruction unregisters its resize and metadata callbacks', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
env.art.emit('destroy')
|
||||
assert.equal(env.handlers.get('resize')?.length || 0, 0)
|
||||
assert.equal(env.handlers.get('video:loadedmetadata')?.length || 0, 0)
|
||||
assert.equal(env.handlers.get('destroy')?.length || 0, 0)
|
||||
assert.equal(canvas.engine.destroyed, true)
|
||||
})
|
||||
|
||||
test('MediaBunny saved resize callback cannot write after destruction', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
const callback = env.handlers.get('resize')[0]
|
||||
env.art.emit('destroy')
|
||||
canvas.style.width = '42px'
|
||||
callback()
|
||||
assert.equal(canvas.style.width, '42px')
|
||||
})
|
||||
|
||||
test('MediaBunny destruction preserves a replacement art.mediabunny alias', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
const replacement = {}
|
||||
env.art.mediabunny = replacement
|
||||
env.art.emit('destroy')
|
||||
assert.equal(env.art.mediabunny, replacement)
|
||||
assert.equal(canvas.engine.destroyed, true)
|
||||
})
|
||||
|
||||
test('MediaBunny entry attempts listener cleanup even when engine destroy throws', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
const failure = new Error('engine cleanup failed')
|
||||
canvas.engine.destroy = () => {
|
||||
throw failure
|
||||
}
|
||||
assert.throws(() => env.art.emit('destroy'), error => error === failure)
|
||||
assert.equal(env.art.mediabunny, undefined)
|
||||
assert.equal(env.handlers.get('resize')?.length || 0, 0)
|
||||
assert.equal(env.handlers.get('destroy')?.length || 0, 0)
|
||||
assert.doesNotThrow(() => env.art.emit('destroy'))
|
||||
})
|
||||
|
||||
test('MediaBunny entry attempts all cleanup after one listener removal fails', () => {
|
||||
const env = environment()
|
||||
const canvas = env.factory()(env.art)
|
||||
const off = env.art.off
|
||||
const removed = []
|
||||
const failure = new Error('off failed')
|
||||
env.art.off = (name, callback) => {
|
||||
removed.push(name)
|
||||
off(name, callback)
|
||||
if (name === 'resize')
|
||||
throw failure
|
||||
}
|
||||
assert.throws(() => env.art.emit('destroy'), error => error === failure)
|
||||
assert.deepEqual(removed, ['resize', 'video:loadedmetadata', 'destroy'])
|
||||
assert.equal(canvas.engine.destroyed, true)
|
||||
assert.equal(env.art.mediabunny, undefined)
|
||||
})
|
||||
|
||||
test('MediaBunny entry keeps legacy host adapters without off usable and callbacks inert after destroy', () => {
|
||||
const env = environment()
|
||||
delete env.art.off
|
||||
const canvas = env.factory()(env.art)
|
||||
env.art.emit('destroy')
|
||||
canvas.style.width = '42px'
|
||||
env.art.emit('resize')
|
||||
assert.equal(canvas.style.width, '42px')
|
||||
})
|
||||
|
||||
test('MediaBunny bridge setup failure releases the assigned shim and preserves the original failure', () => {
|
||||
const env = environment()
|
||||
Object.preventExtensions(env.canvas)
|
||||
let captured
|
||||
Object.defineProperty(env.art, 'mediabunny', {
|
||||
configurable: true,
|
||||
get() { return captured },
|
||||
set(value) { captured = value },
|
||||
})
|
||||
assert.throws(() => env.factory()(env.art), { name: 'TypeError' })
|
||||
assert.equal(captured.engine.destroyed, true)
|
||||
assert.equal(env.art.mediabunny, undefined)
|
||||
assert.equal(env.handlers.get('resize')?.length || 0, 0)
|
||||
})
|
||||
|
||||
test('MediaBunny entry calls createElement with the historical detached receiver', () => {
|
||||
const env = environment()
|
||||
let receiver = 'unset'
|
||||
env.art.constructor.utils.createElement = function () {
|
||||
receiver = this
|
||||
return env.canvas
|
||||
}
|
||||
env.factory()(env.art)
|
||||
assert.equal(receiver, undefined)
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny bridge preserves the two native method getter reads and bound invocation', () => {
|
||||
const env = environment()
|
||||
let reads = 0
|
||||
const receivers = []
|
||||
Object.defineProperty(env.canvas, 'probe', {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
reads++
|
||||
return function () {
|
||||
receivers.push(this)
|
||||
}
|
||||
},
|
||||
set(value) {
|
||||
Object.defineProperty(env.canvas, 'probe', { value, configurable: true })
|
||||
},
|
||||
})
|
||||
env.factory()(env.art)
|
||||
assert.equal(reads, 2)
|
||||
const probe = env.canvas.probe
|
||||
probe()
|
||||
assert.equal(receivers[0], env.canvas)
|
||||
env.art.emit('destroy')
|
||||
})
|
||||
|
||||
test('MediaBunny partial HLS registration failure cleans the entry, shim and installed callbacks', () => {
|
||||
const env = environment()
|
||||
const failure = new Error('registration failed')
|
||||
const on = env.art.on
|
||||
env.art.on = (name, callback) => {
|
||||
on(name, callback)
|
||||
if (name === 'restart')
|
||||
throw failure
|
||||
}
|
||||
let shim
|
||||
Object.defineProperty(env.art, 'mediabunny', {
|
||||
configurable: true,
|
||||
get() { return shim },
|
||||
set(value) { shim = value },
|
||||
})
|
||||
assert.throws(() => env.factory({ m3u8: {} })(env.art), error => error === failure)
|
||||
assert.equal(shim.engine.destroyed, true)
|
||||
assert.equal(env.art.mediabunny, undefined)
|
||||
assert.equal([...env.handlers.values()].flat().length, 0)
|
||||
})
|
||||
@@ -257,11 +257,11 @@ test('MediaBunny HLS control update reentry cannot write later settings or audio
|
||||
test('MediaBunny HLS destruction removes its refresh listeners and preserves proxy teardown', async () => {
|
||||
const env = environment()
|
||||
await env.refresh()
|
||||
const metadataBefore = env.handlers.get('video:loadedmetadata').length
|
||||
const metadataListener = env.handlers.get('video:loadedmetadata').at(-1)
|
||||
env.art.emit('destroy')
|
||||
assert.equal(env.canvas.engine.destroyed, true)
|
||||
assert.equal(env.art.mediabunny, undefined)
|
||||
assert.equal(env.handlers.get('video:loadedmetadata').length, metadataBefore - 1)
|
||||
assert.equal(env.handlers.get('video:loadedmetadata').includes(metadataListener), false)
|
||||
assert.equal(env.handlers.get('restart')?.length || 0, 0)
|
||||
})
|
||||
|
||||
@@ -402,7 +402,7 @@ test('MediaBunny HLS failing listener removal still attempts remaining removals
|
||||
throw new Error('off failed')
|
||||
}
|
||||
env.art.emit('destroy')
|
||||
assert.equal(removed.length, 5)
|
||||
assert.deepEqual(removed.slice(0, 5), ['video:loadedmetadata', 'restart', 'video:loadstart', 'video:error', 'destroy'])
|
||||
assert.equal(env.canvas.engine.destroyed, true)
|
||||
assert.equal(env.warnings.length, 1)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { HlsState, MediaBunnyCanvas, MediaBunnyPlayer, MediaBunnyShim, Option, Result, SyntheticFrameCallback } from 'artplayer-proxy-mediabunny'
|
||||
import Artplayer from 'artplayer'
|
||||
import factory from 'artplayer-proxy-mediabunny'
|
||||
import legacy from 'artplayer-proxy-mediabunny/legacy'
|
||||
|
||||
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
|
||||
type Assert<T extends true> = T
|
||||
type ExactFactory = Assert<Equal<typeof factory, (option?: Option) => (art: Artplayer) => HTMLCanvasElement>>
|
||||
type ExactParameters = Assert<Equal<Parameters<typeof factory>, [option?: Option]>>
|
||||
type ExactResult = Assert<Equal<ReturnType<ReturnType<typeof factory>>, HTMLCanvasElement>>
|
||||
type ExactResultAlias = Assert<Equal<Result, HTMLCanvasElement>>
|
||||
type NativeAttributes = Assert<Equal<MediaBunnyCanvas['setAttribute'], HTMLCanvasElement['setAttribute']>>
|
||||
type NativeEvents = Assert<Equal<MediaBunnyCanvas['addEventListener'], HTMLCanvasElement['addEventListener']>>
|
||||
|
||||
const art: MediaBunnyPlayer = new Artplayer({ container: '#player', url: 'movie.mp4', proxy: factory() })
|
||||
const replacement: typeof factory = (_option?: Option) => (_art: Artplayer) => document.createElement('canvas')
|
||||
const canvas: Result = factory(undefined)(art)
|
||||
const fromLegacy: typeof factory = legacy
|
||||
const toLegacy: typeof legacy = factory
|
||||
declare const shim: MediaBunnyShim
|
||||
const playing: Promise<void> = shim.play()
|
||||
const paused: void = shim.pause()
|
||||
const loading: void = shim.load()
|
||||
const state: Promise<HlsState | null> = shim.getM3u8State()
|
||||
const source: unknown = shim.currentSrc
|
||||
shim.src = new Blob([])
|
||||
shim.currentTime = 2
|
||||
shim.volume = 0.5
|
||||
shim.muted = false
|
||||
const callback: SyntheticFrameCallback = (now, metadata) => {
|
||||
const values: number[] = [now, metadata.presentationTime, metadata.expectedDisplayTime, metadata.width, metadata.height, metadata.mediaTime, metadata.presentedFrames, metadata.processingDuration, metadata.captureTime, metadata.receiveTime, metadata.rtpTimestamp]
|
||||
void values
|
||||
}
|
||||
shim.cancelVideoFrameCallback(shim.requestVideoFrameCallback(callback))
|
||||
const maybe: 'maybe' = shim.canPlayType('video/mp4')
|
||||
art.mediabunny?.pause()
|
||||
delete art.mediabunny
|
||||
declare const extended: MediaBunnyCanvas
|
||||
const native: HTMLCanvasElement = extended
|
||||
extended.play()
|
||||
extended.addEventListener('click', event => event.clientX)
|
||||
extended.setAttribute('src', 'native-attribute-only')
|
||||
state.then((value) => {
|
||||
if (!value)
|
||||
return
|
||||
const height: number | undefined = value.currentLevel?.height
|
||||
const language: string | undefined = value.currentAudio?.language
|
||||
const track: unknown = value.levels[0]?.track
|
||||
// @ts-expect-error SDK internals require explicit narrowing, not ambient SDK declarations.
|
||||
track.decode()
|
||||
void [height, language]
|
||||
})
|
||||
// @ts-expect-error The default factory does not promise shim members on a replacement canvas.
|
||||
canvas.play()
|
||||
// @ts-expect-error The default result is the historical exact DOM Canvas.
|
||||
const requiredShim: MediaBunnyCanvas = canvas
|
||||
// @ts-expect-error Alias does not exist before initialization or after destroy.
|
||||
art.mediabunny.play()
|
||||
// @ts-expect-error Factory options are optional but not null.
|
||||
factory(null)
|
||||
// @ts-expect-error Volume remains numeric.
|
||||
factory({ volume: '0.7' })
|
||||
// @ts-expect-error Historical Option does not widen arbitrary SDK objects.
|
||||
factory({ source: {} })
|
||||
// @ts-expect-error Option stream remains byte-oriented.
|
||||
factory({ source: new ReadableStream<string>() })
|
||||
// @ts-expect-error Unknown options remain errors.
|
||||
factory({ arbitrary: true })
|
||||
// @ts-expect-error The initializer still requires its host.
|
||||
factory()()
|
||||
// @ts-expect-error Synchronous registration remains synchronous.
|
||||
const promise: Promise<Result> = factory()(art)
|
||||
// @ts-expect-error Computed duration has no writable setter.
|
||||
shim.duration = 10
|
||||
// @ts-expect-error Synchronous load does not falsely promise awaitable completion.
|
||||
const loaded: Promise<void> = shim.load()
|
||||
// @ts-expect-error Canvas retains its native string attribute API.
|
||||
extended.setAttribute('src', new Blob([]))
|
||||
// @ts-expect-error Manual/auto are the only track modes.
|
||||
const mode: NonNullable<HlsState>['videoMode'] = 'adaptive'
|
||||
void [replacement, fromLegacy, toLegacy, playing, paused, loading, state, source, maybe, native, requiredShim, promise, loaded, mode]
|
||||
export type { ExactFactory, ExactParameters, ExactResult, ExactResultAlias, NativeAttributes, NativeEvents }
|
||||
Reference in new issue
Block a user