mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-27 17:51: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
@@ -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 }
|
||||
Reference in new issue
Block a user