fix(canvas): [PKG-CANVAS-SUBTITLE-01] attach native subtitle tracks

This commit is contained in:
Harvey Zhao committed 2026-09-14 18:51:57 +08:00
1 parent d89728c0a6
commit c3967bcbf4
23 files changed
+1459 -11

No files matched your search

@@ -22,6 +22,7 @@ callback remains optional and function-valued.
| --- | --- |
| index.ts | Host event subscriptions, deferred media forwarding, initialization rollback and terminal disposal |
| adapter.ts | Preserve native canvas methods; forward only video properties absent from canvas; guard escaped media actions after disposal |
| subtitles.ts | Supply the initial native metadata track and route the core's actual HTML track insertion into the backing video |
| media.ts | Connect the backing video before playback and release its source, stream reference and DOM node |
| geometry.ts | Finite intrinsic dimensions and aspect-fit canvas/padding calculations |
| renderer.ts | Decoded-frame eligibility, bitmap acquisition/close, callback and draw/error notification order |
@@ -33,7 +34,18 @@ private core implementation modules.
## State and event order
Canvas methods keep their receiver and win over same-named video methods. Media properties
Canvas methods keep their receiver and win over same-named video methods for
ordinary Canvas content. `appendChild` has one intentional
subtitle-specific adaptation: active HTML track elements attach to the backing video,
where native loading and cuechange work. Other nodes still attach to the Canvas.
The initial empty metadata track makes the old core's synchronous textTracks[0]
capability check succeed; it is removed before the first actual track is appended.
The core owns actual track replacement/removal and subtitle URLs. After destruction,
escaped appendChild cannot attach more tracks to the unloaded private video.
This fixes the formerly empty subtitle path; a working track's parent is now VIDEO.
The initialization is inside the same rollback boundary as host registration.
Media properties
remain live enumerable/configurable descriptors. Assigning src/srcObject or invoking load
invalidates pending frames; the returned canvas and callback arguments keep their identities.
The video is connected before play with absolute positioning, transparent opacity, no focus
@@ -89,4 +101,8 @@ through the semantic generator; the editor root stays plain, with an explicit
artplayerProxyCanvas.RuntimeFactory assertion for code needing the runtime self alias.
Broader installed artifact contents/deep paths and 8082 demos remain 06.
Real Safari/mobile/device scope remains 05; Windows Playwright WebKit is not a device claim.
`canvas-subtitles.spec.js` exercises native VTT load, paused seeking, replacement,
single-track ownership, ordinary Canvas child insertion/removal and escaped insertion
after destroy on old/new cores. It also belongs to the installed browser scope;
keep its bytes and native engine provenance with the lifecycle report.
See `refactor/canvas-validation.md` and the task evidence for actual outcomes and limitations.
@@ -53,6 +53,12 @@ Runtime falsy callback arguments retain historical behavior; declarations accept
optional function, matching the published types. Implementation and test guidance is in
[ARCHITECTURE.md](ARCHITECTURE.md).
The standard ArtPlayer `subtitle` option uses native subtitle tracks on the backing
video. HTML track elements passed to the active proxy's `appendChild` are attached
there so captions can load and follow playback; ordinary Canvas content keeps its
normal parent. The initial metadata track is replaced by the first actual subtitle.
This repairs the previously empty subtitle path without changing subtitle options.
### Older TypeScript consumers
The 1.0.0 declaration used `export =` and required a callback; 1.1.0 changed to
@@ -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.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,s=null;function l(){null!==s&&cancelAnimationFrame(s),s=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,s=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(s,s)}else n&&null===s&&(s=requestAnimationFrame(()=>{s=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,l(),c())}function f(){n=!1,r=!1,a++,l()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:f,destroy(){i=!0,f()}}}function n(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let s=i,l=i.isDestroy,c=null;const u=[],f=()=>!l&&!s?.isDestroy,d=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>s?.template?.$player,f),p=function(t,n,i,o,r){const a=t.getContext("2d");let s=!1;return{available:()=>!!a||(s||(s=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(s){const l=()=>s()&&n.readyState>=2&&!n.seeking;if(!a||!l()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{l()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!l())return;i&&i(a,n),l()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;l()&&!e&&r(u)}}}}(r,a,n,(e,t)=>s?.emit("artplayerProxyCanvas:draw",e,t),e=>s?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,s]of r)Reflect.set(e,a,s)}(r,a,f,h.stop,d.mount),l)return s=null,r;function y(){if(l)return;l=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),d.destroy])try{n()}catch(t){e.push(t)}if(s=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),l&&o()}function v(){p.available()&&h.request()}try{m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{d.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const s=n.videoWidth/n.videoHeight,l=r/a>s?a*s:r,c=r/a>s?a:r/s;t.width=l,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-l)/2}px`}(r,a,s?.template?.$player,s?.option.autoSize),v()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{d.mount();for(const e of o.config.events){if(!f())break;const t=e=>{f()&&s?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);f()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}return Object.assign(n,{default:n})});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,l=null;function s(){null!==l&&cancelAnimationFrame(l),l=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,l=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(l,l)}else n&&null===l&&(l=requestAnimationFrame(()=>{l=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,s(),c())}function d(){n=!1,r=!1,a++,s()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:d,destroy(){i=!0,d()}}}function n(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let l=i,s=i.isDestroy,c=null;const u=[],d=()=>!s&&!l?.isDestroy,f=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>l?.template?.$player,d),p=function(t,n,i,o,r){const a=t.getContext("2d");let l=!1;return{available:()=>!!a||(l||(l=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(l){const s=()=>l()&&n.readyState>=2&&!n.seeking;if(!a||!s()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{s()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(a,n),s()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(u)}}}}(r,a,n,(e,t)=>l?.emit("artplayerProxyCanvas:draw",e,t),e=>l?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,d);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,l]of r)Reflect.set(e,a,l)}(r,a,d,h.stop,f.mount),s)return l=null,r;function y(){if(s)return;s=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),f.destroy])try{n()}catch(t){e.push(t)}if(l=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!d())return;const n=()=>{("destroy"===e||d())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),s&&o()}function v(){p.available()&&h.request()}try{!function(e,t,n,i){if(!i())return;const o=e.appendChild;n.kind="metadata",n.default=!0,t.appendChild(n),e.appendChild=r=>i()&&1===r.nodeType&&"TRACK"===r.nodeName&&"namespaceURI"in r&&"http://www.w3.org/1999/xhtml"===r.namespaceURI?(n.remove(),t.appendChild(r)):o.call(e,r)}(r,a,o.utils.createElement("track"),d),m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{f.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const l=n.videoWidth/n.videoHeight,s=r/a>l?a*l:r,c=r/a>l?a:r/l;t.width=s,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-s)/2}px`}(r,a,l?.template?.$player,l?.option.autoSize),v()}),d()&&(c=setTimeout(()=>{if(c=null,d())try{f.mount();for(const e of o.config.events){if(!d())break;const t=e=>{d()&&l?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);d()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}return Object.assign(n,{default:n})});
@@ -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.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const l=t.getContext("2d");let a=!1;return{available:()=>!!l||(a||(a=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(a){return s=this,u=null,c=function*(){const s=()=>a()&&n.readyState>=2&&!n.seeking;if(!l||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&l.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else l.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(l,n),s()&&o(l,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,l=0,a=null;function s(){null!==a&&cancelAnimationFrame(a),a=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=l,a=()=>{o=!1,u()};e(()=>!i&&t()&&l===n).then(a,a)}else n&&null===a&&(a=requestAnimationFrame(()=>{a=null,r=!0,u()}))}function c(){!i&&t()&&(l++,r=!0,s(),u())}function d(){n=!1,r=!1,l++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}function i(i){return o=>{const r=o.constructor,l=r.utils.createElement("canvas"),a=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),h=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),p=t(l,a,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const l in e){const t=Reflect.get(e,l);"function"===typeof t&&r.set(l,(...n)=>Reflect.apply(t,e,n))}for(const l in t)l in e||Object.defineProperty(e,l,{get(){const e=Reflect.get(t,l);return"function"!==typeof e?e:(...r)=>n()?("load"===l&&i(),"play"===l&&o(),Reflect.apply(e,t,r)):"play"===l?Promise.resolve():void 0},set(e){n()&&("src"!==l&&"srcObject"!==l||i(),Reflect.set(t,l,e))},configurable:!0,enumerable:!0});for(const[l,a]of r)Reflect.set(e,l,a)}(l,a,f,y.stop,h.mount),u)return s=null,l;function v(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),h.destroy])try{n()}catch(t){e.push(t)}if(s=null,l.width=0,l.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){p.available()&&y.request()}try{m("destroy",v),m("video:loadedmetadata",()=>{e(a)&&(l.width=a.videoWidth,l.height=a.videoHeight)}),m("video:play",()=>{h.mount(),p.available()&&y.start()}),m("video:pause",y.stop),m("video:emptied",y.stop),m("video:seeked",g),m("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,l=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(l)||r<=0||l<=0)return;const a=n.videoWidth/n.videoHeight,s=r/l>a?l*a:r,u=r/l>a?l:r/a;t.width=s,t.height=u,t.style.padding=`${(l-u)/2}px ${(r-s)/2}px`}(l,a,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{h.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>a.removeEventListener(e,t);d.push(n);const i=o.proxy(a,e,t);f()?d.push(i):i()}}catch(e){try{v()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{v()}catch(w){}throw b}return l}}return Object.assign(i,{default:i})});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const a=t.getContext("2d");let l=!1;return{available:()=>!!a||(l||(l=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(l){return s=this,u=null,c=function*(){const s=()=>l()&&n.readyState>=2&&!n.seeking;if(!a||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(a,n),s()&&o(a,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,l=null;function s(){null!==l&&cancelAnimationFrame(l),l=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,l=()=>{o=!1,u()};e(()=>!i&&t()&&a===n).then(l,l)}else n&&null===l&&(l=requestAnimationFrame(()=>{l=null,r=!0,u()}))}function c(){!i&&t()&&(a++,r=!0,s(),u())}function d(){n=!1,r=!1,a++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}function i(i){return o=>{const r=o.constructor,a=r.utils.createElement("canvas"),l=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),p=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(l,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),h=t(a,l,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(h.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,l]of r)Reflect.set(e,a,l)}(a,l,f,y.stop,p.mount),u)return s=null,a;function m(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),p.destroy])try{n()}catch(t){e.push(t)}if(s=null,a.width=0,a.height=0,e.length)throw e[0]}function v(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){h.available()&&y.request()}try{!function(e,t,n,i){if(!i())return;const o=e.appendChild;n.kind="metadata",n.default=!0,t.appendChild(n),e.appendChild=r=>i()&&1===r.nodeType&&"TRACK"===r.nodeName&&"namespaceURI"in r&&"http://www.w3.org/1999/xhtml"===r.namespaceURI?(n.remove(),t.appendChild(r)):o.call(e,r)}(a,l,r.utils.createElement("track"),f),v("destroy",m),v("video:loadedmetadata",()=>{e(l)&&(a.width=l.videoWidth,a.height=l.videoHeight)}),v("video:play",()=>{p.mount(),h.available()&&y.start()}),v("video:pause",y.stop),v("video:emptied",y.stop),v("video:seeked",g),v("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const l=n.videoWidth/n.videoHeight,s=r/a>l?a*l:r,u=r/a>l?a:r/l;t.width=s,t.height=u,t.style.padding=`${(a-u)/2}px ${(r-s)/2}px`}(a,l,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{p.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>l.removeEventListener(e,t);d.push(n);const i=o.proxy(l,e,t);f()?d.push(i):i()}}catch(e){try{m()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{m()}catch(w){}throw b}return a}}return Object.assign(i,{default:i})});
@@ -204,6 +204,21 @@ function createFrameScheduler(draw, active) {
}
};
}
function forwardSubtitleTrack(canvas, video, initialTrack, active) {
if (!active())
return;
const append = canvas.appendChild;
initialTrack.kind = "metadata";
initialTrack.default = true;
video.appendChild(initialTrack);
canvas.appendChild = (node) => {
if (active() && node.nodeType === 1 && node.nodeName === "TRACK" && "namespaceURI" in node && node.namespaceURI === "http://www.w3.org/1999/xhtml") {
initialTrack.remove();
return video.appendChild(node);
}
return append.call(canvas, node);
};
}
function artplayerProxyCanvas(callback) {
return (art) => {
const constructor = art.constructor;
@@ -262,6 +277,7 @@ function artplayerProxyCanvas(callback) {
scheduler.request();
}
try {
forwardSubtitleTrack(canvas, video, constructor.utils.createElement("track"), active);
listen("destroy", destroy);
listen("video:loadedmetadata", () => {
if (hasDimensions(video)) {
@@ -5,6 +5,7 @@ import { hasDimensions, resizeCanvas } from './geometry'
import { ownMedia } from './media'
import { createRenderer } from './renderer'
import { createFrameScheduler } from './scheduler'
import { forwardSubtitleTrack } from './subtitles'
function artplayerProxyCanvas(callback?: DrawCallback) {
return (art: Artplayer): HTMLCanvasElement => {
@@ -67,6 +68,7 @@ function artplayerProxyCanvas(callback?: DrawCallback) {
}
try {
forwardSubtitleTrack(canvas, video, constructor.utils.createElement('track'), active)
listen('destroy', destroy)
listen('video:loadedmetadata', () => {
if (hasDimensions(video)) {
@@ -0,0 +1,15 @@
export function forwardSubtitleTrack(canvas: HTMLCanvasElement, video: HTMLVideoElement, initialTrack: HTMLTrackElement, active: () => boolean): void {
if (!active())
return
const append = canvas.appendChild
initialTrack.kind = 'metadata'
initialTrack.default = true
video.appendChild(initialTrack)
canvas.appendChild = <T extends Node>(node: T): T => {
if (active() && node.nodeType === 1 && node.nodeName === 'TRACK' && 'namespaceURI' in node && node.namespaceURI === 'http://www.w3.org/1999/xhtml') {
initialTrack.remove()
return video.appendChild(node)
}
return append.call(canvas, node) as T
}
}