fix: rename cleanupTimeout to hideTimeout for consistency in ASR plugin

Refactor artplayerPluginAsr: Enhance audio capture and processing

- Introduced gainNode for audio output management.
- Added mediaStream and mediaStreamSource for fallback audio capture.
- Refactored startCapture and stopCapture functions for better clarity and error handling.
- Improved audio source setup with a dedicated function for capturing audio from video.
- Ensured proper disconnection of audio nodes and cleanup of resources on destroy.

feat: enhance ASR plugin with improved subtitle handling and styling

fix: rename autoClearTimer to hideTimer for consistency in ASR plugin

Update asr.js

feat: refactor ASR plugin to improve audio chunk handling and WebSocket management

Update asr.js

fix: rename autoClearTimeout to cleanupTimeout for consistency

Update asr.js

feat: refactor ASR handling and improve WebSocket management

Update index.js

feat: add append and clear functions to enhance audio chunk handling

Update asr.js

Update asr.js

feat: release version 2.0.0 of artplayer-plugin-asr

- Implemented core functionality for audio recording using AudioWorklet.
- Added support for configurable audio capture interval and sample rate.
- Introduced WAV encoding for recorded audio chunks.
- Created a new plugin structure with proper TypeScript definitions.
- Added legacy support for older environments.
- Updated package.json with necessary metadata and export configurations.
- Included comprehensive inline documentation for better maintainability.
This commit is contained in:
Harvey Zack committed 2025-05-23 00:09:13 +08:00
1 parent 6c4e9f7e78
commit 5d7710979d
22 files changed
+893 -1

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
node_modules
.DS_Store
yarn.lock
package-lock.json
test
coverage
.nyc_output
.parcel-cache
.cache
docs
+1
View File
@@ -0,0 +1 @@
package-lock=false
+11
View File
@@ -0,0 +1,11 @@
# artplayer-plugin-asr
Ads plugin for ArtPlayer
## Demo
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-asr/index.js&example=asr)
## License
MIT © Harvey Zack
@@ -0,0 +1,9 @@
/*!
* artplayer-plugin-asr.js v2.0.0
* Github: https://github.com/zhw2590582/ArtPlayer
* (c) 2017-2025 Harvey Zack
* Released under the MIT License.
*/
!function(e,t,n,r,o,i,u,l){var a="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},s="function"==typeof a[r]&&a[r],d=s.i||{},f=s.cache||{},c="undefined"!=typeof module&&"function"==typeof module.require&&module.require.bind(module);function p(t,n){if(!f[t]){if(!e[t]){if(o[t])return o[t];var i="function"==typeof a[r]&&a[r];if(!n&&i)return i(t,!0);if(s)return s(t,!0);if(c&&"string"==typeof t)return c(t);var u=Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}d.resolve=function(n){var r=e[t][1][n];return null!=r?r:n},d.cache={};var l=f[t]=new p.Module(t);e[t][0].call(l.exports,d,l,l.exports,a)}return f[t].exports;function d(e){var t=d.resolve(e);return!1===t?{}:p(t)}}p.isParcelRequire=!0,p.Module=function(e){this.id=e,this.bundle=p,this.require=c,this.exports={}},p.modules=e,p.cache=f,p.parent=s,p.distDir=void 0,p.publicUrl=void 0,p.devServer=void 0,p.i=d,p.register=function(t,n){e[t]=[function(e,t){t.exports=n},{}]},Object.defineProperty(p,"root",{get:function(){return a[r]}}),a[r]=p;for(var y=0;y<t.length;y++)p(t[y]);if(n){var w=p(n);"object"==typeof exports&&"undefined"!=typeof module?module.exports=w:"function"==typeof define&&define.amd&&define(function(){return w})}}({"40eeC":[function(e,t,n,r){var o=e("@parcel/transformer-js/src/esmodule-helpers.js");function i(e={}){let{interval:t=5e3,sampleRate:n=16e3,callback:r=()=>null}=e;return e=>{let o=!1,i=null,u=null,l=null,a=[],s=null,d=` class RecorderProcessor extends AudioWorkletProcessor { process(inputs) { const input = inputs[0]; if (input && input[0]) { this.port.postMessage(input[0]); } return true; } } registerProcessor('recorder-processor', RecorderProcessor); `,f=()=>{let e=new Blob([d],{type:"application/javascript"});return URL.createObjectURL(e)};async function c(){if(o)return;o=!0,"suspended"===(i=new(window.AudioContext||window.webkitAudioContext)({sampleRate:n})).state&&await i.resume(),u||(u=i.createMediaElementSource(e.video)).connect(i.destination);let d=f();await i.audioWorklet.addModule(d),URL.revokeObjectURL(d),(l=new AudioWorkletNode(i,"recorder-processor")).port.onmessage=e=>{a.push(new Float32Array(e.data))},u.connect(l).connect(i.destination),s=setInterval(async()=>{if(0===a.length)return;let e=function(e,t){let n=function(e){let t=new Float32Array(e.reduce((e,t)=>e+t.length,0)),n=0;return e.forEach(e=>{t.set(e,n),n+=e.length}),t}(e),r=2*n.length,o=new ArrayBuffer(44+r),i=new DataView(o);function u(e,t){for(let n=0;n<t.length;n++)i.setUint8(e+n,t.charCodeAt(n))}u(0,"RIFF"),i.setUint32(4,36+r,!0),u(8,"WAVE"),u(12,"fmt "),i.setUint32(16,16,!0),i.setUint16(20,1,!0),i.setUint16(22,1,!0),i.setUint32(24,t,!0),i.setUint32(28,2*t,!0),i.setUint16(32,2,!0),i.setUint16(34,16,!0),u(36,"data"),i.setUint32(40,r,!0);let l=function(e){let t=e.length,n=new ArrayBuffer(2*t),r=new DataView(n);for(let n=0;n<t;n++){let t=Math.max(-1,Math.min(1,e[n]));r.setInt16(2*n,t<0?32768*t:32767*t,!0)}return n}(n);return new Uint8Array(o).set(new Uint8Array(l),44),new Blob([i],{type:"audio/wav"})}(a,i.sampleRate),t=await e.arrayBuffer();await r(t),a=[]},t)}function p(){o&&(o=!1,s&&clearInterval(s),l&&l.disconnect(),l=null,a=[])}return e.on("play",c),e.on("pause",p),e.on("destroy",p),{name:"artplayerPluginAsr",destroy:p}}}o.defineInteropFlag(n),o.export(n,"default",()=>i),"undefined"!=typeof window&&(window.artplayerPluginAsr=i)},{"@parcel/transformer-js/src/esmodule-helpers.js":"5lQoW"}],"5lQoW":[function(e,t,n,r){n.interopDefault=function(e){return e&&e.__esModule?e:{default:e}},n.defineInteropFlag=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.exportAll=function(e,t){return Object.keys(e).forEach(function(n){"default"===n||"__esModule"===n||Object.prototype.hasOwnProperty.call(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:function(){return e[n]}})}),t},n.export=function(e,t,n){Object.defineProperty(e,t,{enumerable:!0,get:n})}},{}]},["40eeC"],"40eeC","parcelRequire4dc0",{});let{default:e}=parcelRequire4dc0("40eeC");export{e as default};
//# sourceMappingURL=artplayer-plugin-asr.esm.js.map
@@ -0,0 +1 @@
{"mappings":"C,A,S,C,C,C,C,C,C,C,C,C,C,C,C,C,C,C,E,I,E,A,a,O,W,W,A,a,O,K,K,A,a,O,O,O,A,a,O,O,O,C,E,E,A,Y,O,C,C,E,E,C,C,E,C,E,E,C,E,C,E,E,E,K,E,C,E,E,A,a,O,Q,A,Y,O,O,O,E,O,O,C,I,C,Q,S,E,C,C,C,E,G,C,C,C,E,C,C,G,C,C,C,E,C,C,G,C,C,E,C,O,C,C,E,C,I,E,A,Y,O,C,C,E,E,C,C,E,C,G,C,G,E,O,E,E,C,G,G,E,O,E,E,C,G,G,G,A,U,O,E,O,E,G,I,E,A,M,uB,E,I,O,E,I,C,mB,C,C,E,O,C,S,C,E,I,E,C,C,E,C,E,C,E,C,O,A,M,E,E,C,E,E,K,C,C,E,I,E,C,C,E,C,I,E,M,C,G,C,C,E,C,E,C,I,C,E,O,C,E,E,E,O,C,E,C,O,C,C,E,C,O,C,S,E,C,E,I,E,E,O,C,G,M,A,C,I,E,C,E,E,E,C,C,E,e,C,C,E,E,M,C,S,C,E,I,C,E,C,E,I,C,M,C,E,I,C,O,C,E,I,C,O,C,C,C,E,E,O,C,E,E,K,C,E,E,M,C,E,E,O,C,K,E,E,S,C,K,E,E,S,C,K,E,E,C,C,E,E,Q,C,S,C,C,C,E,C,C,E,C,C,S,C,C,C,E,E,O,C,C,E,C,E,A,E,O,c,C,E,O,C,I,W,O,C,C,E,A,C,G,C,C,E,C,E,I,I,E,E,E,E,M,C,I,E,C,C,E,E,G,E,C,I,E,E,E,A,C,U,O,S,A,a,O,O,O,O,C,E,A,Y,O,Q,O,G,E,O,W,O,C,E,C,E,C,Q,C,S,C,C,C,C,C,C,C,E,I,E,E,kDCAe,SAAS,EAAmB,EAAS,CAAC,CAAC,EAClD,GAAM,CAAE,SAAA,EAAW,GAAI,CAAE,WAAA,EAAa,IAAK,CAAE,SAAA,EAAW,IAAM,IAAI,CAAE,CAAG,EAEvE,OAAO,AAAC,IACJ,IAAI,EAAU,CAAA,EACV,EAAW,KACX,EAAa,KACb,EAAe,KACf,EAAe,EAAE,CACjB,EAAQ,KAEN,EAAwB;A;A;A;A;A;A;A;A;A;A;AAW9B,QAAA,CAAC,CAEK,EAAuB,KACzB,IAAM,EAAO,IAAI,KAAK,CAAC,EAAsB,CAAE,CAAE,KAAM,wBAAyB,GAChF,OAAO,IAAI,eAAe,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","sources":["<anon>","packages/artplayer-plugin-asr/src/index.js","node_modules/@parcel/transformer-js/src/esmodule-helpers.js"],"sourcesContent":["// modules are defined as an array\n// [ module function, map of requires ]\n//\n// map of requires is short require name -> numeric require\n//\n// anything defined in a previous bundle is accessed via the\n// orig method which is the require for previous bundles\n\n(function (\n modules,\n entry,\n mainEntry,\n parcelRequireName,\n externals,\n distDir,\n publicUrl,\n devServer\n) {\n /* eslint-disable no-undef */\n var globalObject =\n typeof globalThis !== 'undefined'\n ? globalThis\n : typeof self !== 'undefined'\n ? self\n : typeof window !== 'undefined'\n ? window\n : typeof global !== 'undefined'\n ? global\n : {};\n /* eslint-enable Line truncated
@@ -0,0 +1,8 @@
/*!
* artplayer-plugin-asr.js v2.0.0
* Github: https://github.com/zhw2590582/ArtPlayer
* (c) 2017-2025 Harvey Zack
* Released under the MIT License.
*/
!function(e,t,n,r,o,i,u,l){var a="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},s="function"==typeof a[r]&&a[r],f=s.i||{},c=s.cache||{},d="undefined"!=typeof module&&"function"==typeof module.require&&module.require.bind(module);function p(t,n){if(!c[t]){if(!e[t]){if(o[t])return o[t];var i="function"==typeof a[r]&&a[r];if(!n&&i)return i(t,!0);if(s)return s(t,!0);if(d&&"string"==typeof t)return d(t);var u=Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}f.resolve=function(n){var r=e[t][1][n];return null!=r?r:n},f.cache={};var l=c[t]=new p.Module(t);e[t][0].call(l.exports,f,l,l.exports,a)}return c[t].exports;function f(e){var t=f.resolve(e);return!1===t?{}:p(t)}}p.isParcelRequire=!0,p.Module=function(e){this.id=e,this.bundle=p,this.require=d,this.exports={}},p.modules=e,p.cache=c,p.parent=s,p.distDir=void 0,p.publicUrl=void 0,p.devServer=void 0,p.i=f,p.register=function(t,n){e[t]=[function(e,t){t.exports=n},{}]},Object.defineProperty(p,"root",{get:function(){return a[r]}}),a[r]=p;for(var y=0;y<t.length;y++)p(t[y]);if(n){var w=p(n);"object"==typeof exports&&"undefined"!=typeof module?module.exports=w:"function"==typeof define&&define.amd&&define(function(){return w})}}({"1gnQM":[function(e,t,n,r){var o=e("@parcel/transformer-js/src/esmodule-helpers.js");function i(e={}){let{interval:t=5e3,sampleRate:n=16e3,callback:r=()=>null}=e;return e=>{let o=!1,i=null,u=null,l=null,a=[],s=null,f=` class RecorderProcessor extends AudioWorkletProcessor { process(inputs) { const input = inputs[0]; if (input && input[0]) { this.port.postMessage(input[0]); } return true; } } registerProcessor('recorder-processor', RecorderProcessor); `,c=()=>{let e=new Blob([f],{type:"application/javascript"});return URL.createObjectURL(e)};async function d(){if(o)return;o=!0,"suspended"===(i=new(window.AudioContext||window.webkitAudioContext)({sampleRate:n})).state&&await i.resume(),u||(u=i.createMediaElementSource(e.video)).connect(i.destination);let f=c();await i.audioWorklet.addModule(f),URL.revokeObjectURL(f),(l=new AudioWorkletNode(i,"recorder-processor")).port.onmessage=e=>{a.push(new Float32Array(e.data))},u.connect(l).connect(i.destination),s=setInterval(async()=>{if(0===a.length)return;let e=function(e,t){let n=function(e){let t=new Float32Array(e.reduce((e,t)=>e+t.length,0)),n=0;return e.forEach(e=>{t.set(e,n),n+=e.length}),t}(e),r=2*n.length,o=new ArrayBuffer(44+r),i=new DataView(o);function u(e,t){for(let n=0;n<t.length;n++)i.setUint8(e+n,t.charCodeAt(n))}u(0,"RIFF"),i.setUint32(4,36+r,!0),u(8,"WAVE"),u(12,"fmt "),i.setUint32(16,16,!0),i.setUint16(20,1,!0),i.setUint16(22,1,!0),i.setUint32(24,t,!0),i.setUint32(28,2*t,!0),i.setUint16(32,2,!0),i.setUint16(34,16,!0),u(36,"data"),i.setUint32(40,r,!0);let l=function(e){let t=e.length,n=new ArrayBuffer(2*t),r=new DataView(n);for(let n=0;n<t;n++){let t=Math.max(-1,Math.min(1,e[n]));r.setInt16(2*n,t<0?32768*t:32767*t,!0)}return n}(n);return new Uint8Array(o).set(new Uint8Array(l),44),new Blob([i],{type:"audio/wav"})}(a,i.sampleRate),t=await e.arrayBuffer();await r(t),a=[]},t)}function p(){o&&(o=!1,s&&clearInterval(s),l&&l.disconnect(),l=null,a=[])}return e.on("play",d),e.on("pause",p),e.on("destroy",p),{name:"artplayerPluginAsr",destroy:p}}}o.defineInteropFlag(n),o.export(n,"default",()=>i),"undefined"!=typeof window&&(window.artplayerPluginAsr=i)},{"@parcel/transformer-js/src/esmodule-helpers.js":"6ykb1"}],"6ykb1":[function(e,t,n,r){n.interopDefault=function(e){return e&&e.__esModule?e:{default:e}},n.defineInteropFlag=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.exportAll=function(e,t){return Object.keys(e).forEach(function(n){"default"===n||"__esModule"===n||Object.prototype.hasOwnProperty.call(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:function(){return e[n]}})}),t},n.export=function(e,t,n){Object.defineProperty(e,t,{enumerable:!0,get:n})}},{}]},["1gnQM"],"1gnQM","parcelRequire4dc0",{});
@@ -0,0 +1,8 @@
/*!
* artplayer-plugin-asr.js v2.0.0
* Github: https://github.com/zhw2590582/ArtPlayer
* (c) 2017-2025 Harvey Zack
* Released under the MIT License.
*/
!function(e,t,r,n,o,i,u,a){var c="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},s="function"==typeof c[n]&&c[n],f=s.i||{},l=s.cache||{},p="undefined"!=typeof module&&"function"==typeof module.require&&module.require.bind(module);function d(t,r){if(!l[t]){if(!e[t]){if(o[t])return o[t];var i="function"==typeof c[n]&&c[n];if(!r&&i)return i(t,!0);if(s)return s(t,!0);if(p&&"string"==typeof t)return p(t);var u=Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}f.resolve=function(r){var n=e[t][1][r];return null!=n?n:r},f.cache={};var a=l[t]=new d.Module(t);e[t][0].call(a.exports,f,a,a.exports,c)}return l[t].exports;function f(e){var t=f.resolve(e);return!1===t?{}:d(t)}}d.isParcelRequire=!0,d.Module=function(e){this.id=e,this.bundle=d,this.require=p,this.exports={}},d.modules=e,d.cache=l,d.parent=s,d.distDir=void 0,d.publicUrl=void 0,d.devServer=void 0,d.i=f,d.register=function(t,r){e[t]=[function(e,t){t.exports=r},{}]},Object.defineProperty(d,"root",{get:function(){return c[n]}}),c[n]=d;for(var y=0;y<t.length;y++)d(t[y]);if(r){var _=d(r);"object"==typeof exports&&"undefined"!=typeof module?module.exports=_:"function"==typeof define&&define.amd&&define(function(){return _})}}({"98QZN":[function(e,t,r,n){var o=e("@parcel/transformer-js/src/esmodule-helpers.js");o.defineInteropFlag(r),o.export(r,"default",function(){return a});var i=e("@swc/helpers/cjs/_async_to_generator.cjs"),u=e("@swc/helpers/cjs/_ts_generator.cjs");function a(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.interval,r=void 0===t?5e3:t,n=e.sampleRate,o=void 0===n?16e3:n,a=e.callback,c=void 0===a?function(){return null}:a;return function(e){var t=!1,n=null,a=null,s=null,f=[],l=null,p=function(){var e=new Blob(["\n class RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n }\n registerProcessor('recorder-processor', RecorderProcessor);\n "],{type:"application/javascript"});return URL.createObjectURL(e)};function d(){return(d=(0,i._)(function(){var d;return(0,u._)(this,function(y){switch(y.label){case 0:if(t)return[2];if(t=!0,"suspended"!==(n=new(window.AudioContext||window.webkitAudioContext)({sampleRate:o})).state)return[3,2];return[4,n.resume()];case 1:y.sent(),y.label=2;case 2:return a||(a=n.createMediaElementSource(e.video)).connect(n.destination),d=p(),[4,n.audioWorklet.addModule(d)];case 3:return y.sent(),URL.revokeObjectURL(d),(s=new AudioWorkletNode(n,"recorder-processor")).port.onmessage=function(e){f.push(new Float32Array(e.data))},a.connect(s).connect(n.destination),l=setInterval((0,i._)(function(){return(0,u._)(this,function(e){switch(e.label){case 0:if(0===f.length)return[2];return[4,(function(e,t){var r,n,o=(r=new Float32Array(e.reduce(function(e,t){return e+t.length},0)),n=0,e.forEach(function(e){r.set(e,n),n+=e.length}),r),i=2*o.length,u=new ArrayBuffer(44+i),a=new DataView(u);function c(e,t){for(var r=0;r<t.length;r++)a.setUint8(e+r,t.charCodeAt(r))}c(0,"RIFF"),a.setUint32(4,36+i,!0),c(8,"WAVE"),c(12,"fmt "),a.setUint32(16,16,!0),a.setUint16(20,1,!0),a.setUint16(22,1,!0),a.setUint32(24,t,!0),a.setUint32(28,2*t,!0),a.setUint16(32,2,!0),a.setUint16(34,16,!0),c(36,"data"),a.setUint32(40,i,!0);var s=function(e){for(var t=e.length,r=new ArrayBuffer(2*t),n=new DataView(r),o=0;o<t;o++){var i=Math.max(-1,Math.min(1,e[o]));n.setInt16(2*o,i<0?32768*i:32767*i,!0)}return r}(o);return new Uint8Array(u).set(new Uint8Array(s),44),new Blob([a],{type:"audio/wav"})})(f,n.sampleRate).arrayBuffer()];case 1:return[4,c(e.sent())];case 2:return e.sent(),f=[],[2]}})}),r),[2]}})})).apply(this,arguments)}function y(){t&&(t=!1,l&&clearInterval(l),s&&s.disconnect(),s=null,f=[])}return e.on("play",function(){return d.apply(this,arguments)}),e.on("pause",y),e.on("destroy",y),{name:"artplayerPluginAsr",destroy:y}}}"undefined"!=typeof window&&(window.artplayerPluginAsr=a)},{"@swc/helpers/cjs/_async_to_generator.cjs":"cZn0e","@swc/helpers/cjs/_ts_generator.cjs":"eltGh","@parcel/transformer-js/src/esmodule-helpers.js":"qpMMf"}],cZn0e:[function(e,t,r,n){"use strict";function o(e,t,r,n,o,i,u){try{var a=e[i](u),c=a.value}catch(e){r(e);return}a.done?t(c):Promise.resolve(c).then(n,o)}r._=function(e){return function(){var t=this,r=arguments;return new Promise(function(n,i){var u=e.apply(t,r);function a(e){o(u,n,i,a,c,"next",e)}function c(e){o(u,n,i,a,c,"throw",e)}a(void 0)})}}},{}],eltGh:[function(e,t,r,n){"use strict";r._=e("eb260df52bffddd").__generator},{eb260df52bffddd:"6yrAu"}],"6yrAu":[function(e,t,r,n){var o=e("@parcel/transformer-js/src/esmodule-helpers.js");o.defineInteropFlag(r),o.export(r,"__extends",function(){return a}),o.export(r,"__assign",function(){return c}),o.export(r,"__rest",function(){return s}),o.export(r,"__decorate",function(){return f}),o.export(r,"__param",function(){return l}),o.exLine truncated
@@ -0,0 +1,30 @@
{
"license": "MIT",
"version": "2.0.0",
"name": "artplayer-plugin-asr",
"description": "Asr plugin for ArtPlayer",
"main": "dist/artplayer-plugin-asr.js",
"legacy": "dist/artplayer-plugin-asr.legacy.js",
"module": "dist/artplayer-plugin-asr.esm.js",
"types": "types/artplayer-plugin-asr.d.ts",
"exports": {
"import": "./dist/artplayer-plugin-asr.esm.js",
"require": "./dist/artplayer-plugin-asr.js",
"default": "./dist/artplayer-plugin-asr.js"
},
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
]
}
+249
View File
@@ -0,0 +1,249 @@
import style from 'bundle-text:./style.less';
export default function artplayerPluginAsr(option = {}) {
const { hideTimeout = 10000, length = 3, interval = 100, sampleRate = 16000, onAudioChunk = () => null } = option;
return (art) => {
let started = false;
let audioCtx = null;
let sourceNode = null;
let recorderNode = null;
let gainNode = null;
let bufferChunks = [];
let timer = null;
let workletLoaded = false;
let hideTimer = null;
let mediaStream = null;
let mediaStreamSource = null;
const $asr = art.layers.add({
name: 'asr',
html: '',
});
function splitByPunctuation(text) {
return text
.split(/(?<=[、。!?!?\.])\s*/u)
.map((s) => s.trim())
.filter(Boolean)
.slice(-length);
}
function hide() {
$asr.style.display = 'none';
}
function append(subtitle) {
if (typeof subtitle !== 'string') return;
clearTimeout(hideTimer);
hideTimer = setTimeout(hide, hideTimeout);
$asr.style.display = '';
$asr.innerHTML = splitByPunctuation(subtitle)
.map((line) => `<div class="art-asr-line">${line}</div>`)
.join('');
}
const recorderProcessorCode = `
class RecorderProcessor extends AudioWorkletProcessor {
process(inputs) {
const input = inputs[0];
if (input && input[0]) {
this.port.postMessage(input[0]);
}
return true;
}
}
registerProcessor('recorder-processor', RecorderProcessor);
`;
const createWorkletBlobUrl = () => {
const blob = new Blob([recorderProcessorCode], { type: 'application/javascript' });
return URL.createObjectURL(blob);
};
function floatTo16BitPCM(float32Array) {
const len = float32Array.length;
const buffer = new ArrayBuffer(len * 2);
const view = new DataView(buffer);
for (let i = 0; i < len; i++) {
let s = Math.max(-1, Math.min(1, float32Array[i]));
view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
return buffer;
}
async function setupAudioContext() {
if (audioCtx) return audioCtx;
audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate });
if (audioCtx.state === 'suspended') await audioCtx.resume();
return audioCtx;
}
async function setupAudioSource() {
if (!sourceNode && art.video) {
try {
sourceNode = audioCtx.createMediaElementSource(art.video);
return sourceNode;
} catch (err) {
console.warn('[artplayerPluginAsr] Direct connection failed, trying fallback...', err);
}
}
try {
if (!mediaStream) {
mediaStream = await captureAudioFromVideo(art.video);
}
if (mediaStream) {
mediaStreamSource = audioCtx.createMediaStreamSource(mediaStream);
return mediaStreamSource;
}
} catch (err) {
console.warn('[artplayerPluginAsr] MediaStream fallback failed:', err);
}
return null;
}
async function captureAudioFromVideo(videoElement) {
try {
if (videoElement.captureStream) return videoElement.captureStream();
if (videoElement.mozCaptureStream) return videoElement.mozCaptureStream();
console.warn('[artplayerPluginAsr] captureStream not supported');
return null;
} catch (err) {
console.warn('[artplayerPluginAsr] Error capturing stream:', err);
return null;
}
}
async function startCapture() {
if (started) return;
try {
await setupAudioContext();
const audioSource = await setupAudioSource();
if (!audioSource) throw new Error('Could not establish audio source');
if (!workletLoaded) {
const blobUrl = createWorkletBlobUrl();
await audioCtx.audioWorklet.addModule(blobUrl);
URL.revokeObjectURL(blobUrl);
workletLoaded = true;
}
// 创建输出音频的 GainNode(正常播放)
gainNode = audioCtx.createGain();
gainNode.gain.value = 1;
recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');
recorderNode.port.onmessage = (event) => {
bufferChunks.push(new Float32Array(event.data));
};
// 分流:识别和播放
audioSource.connect(recorderNode);
audioSource.connect(gainNode);
gainNode.connect(audioCtx.destination); // 正常声音输出
// recorderNode 不连接 destination,避免输出干扰
const CHUNK_SAMPLES = sampleRate * (interval / 1000);
timer = setInterval(async () => {
if (bufferChunks.length === 0) return;
let accumulated = new Float32Array(0);
while (accumulated.length < CHUNK_SAMPLES && bufferChunks.length) {
const next = bufferChunks.shift();
const tmp = new Float32Array(accumulated.length + next.length);
tmp.set(accumulated, 0);
tmp.set(next, accumulated.length);
accumulated = tmp;
}
if (accumulated.length < CHUNK_SAMPLES) return;
const chunkToSend = accumulated.slice(0, CHUNK_SAMPLES);
const pcm = floatTo16BitPCM(chunkToSend);
const subtitle = await onAudioChunk(pcm);
append(subtitle);
}, interval);
started = true;
} catch (err) {
console.error('[artplayerPluginAsr] Initialization failed:', err);
await stopCapture();
}
}
async function stopCapture() {
if (!started) return;
started = false;
clearInterval(timer);
timer = null;
if (recorderNode) {
recorderNode.disconnect();
recorderNode.port.onmessage = null;
recorderNode = null;
}
if (gainNode) {
gainNode.disconnect();
gainNode = null;
}
bufferChunks = [];
}
async function destroy() {
await stopCapture();
if (mediaStreamSource) {
mediaStreamSource.disconnect();
mediaStreamSource = null;
}
if (sourceNode) {
sourceNode.disconnect();
sourceNode = null;
}
if (mediaStream) {
mediaStream.getTracks().forEach((track) => track.stop());
mediaStream = null;
}
if (audioCtx) {
await audioCtx.close();
audioCtx = null;
}
workletLoaded = false;
}
art.on('play', startCapture);
art.on('pause', stopCapture);
art.on('destroy', destroy);
return {
name: 'artplayerPluginAsr',
stop: destroy,
hide,
append,
};
};
}
if (typeof document !== 'undefined') {
if (!document.getElementById('artplayer-plugin-asr')) {
const $style = document.createElement('style');
$style.id = 'artplayer-plugin-asr';
$style.textContent = style;
document.head.appendChild($style);
}
}
if (typeof window !== 'undefined') {
window['artplayerPluginAsr'] = artplayerPluginAsr;
}
@@ -0,0 +1,31 @@
.art-video-player {
.art-layer-asr {
pointer-events: none !important;
position: absolute;
z-index: 150;
inset: 0px;
display: flex;
flex-direction: column;
justify-content: end;
padding: 0 2%;
font-size: 1rem;
gap: var(--art-subtitle-gap);
padding-bottom: var(--art-subtitle-bottom);
transition: padding-bottom var(--art-transition-duration) ease;
text-shadow:
var(--art-subtitle-border) 1px 0 1px,
var(--art-subtitle-border) 0 1px 1px,
var(--art-subtitle-border) -1px 0 1px,
var(--art-subtitle-border) 0 -1px 1px,
var(--art-subtitle-border) 1px 1px 1px,
var(--art-subtitle-border) -1px -1px 1px,
var(--art-subtitle-border) 1px -1px 1px,
var(--art-subtitle-border) -1px 1px 1px;
}
&.art-control-show {
.art-layer-asr {
padding-bottom: calc(var(--art-control-height) + var(--art-subtitle-bottom));
}
}
}
@@ -0,0 +1,20 @@
import type Artplayer from 'artplayer';
export = artplayerPluginAsr;
export as namespace artplayerPluginAsr;
interface AsrPluginOption {
length?: number;
interval?: number;
sampleRate?: number;
hideTimeout?: number;
onAudioChunk?: (buffer: ArrayBuffer) => void | Promise<void>;
}
interface AsrPluginInstance {
name: 'artplayerPluginAsr';
hide: () => void;
append: () => void;
}
declare function artplayerPluginAsr(option?: AsrPluginOption): (art: Artplayer) => AsrPluginInstance;