From 5d7710979df81497d24d4b80578a12902c551966 Mon Sep 17 00:00:00 2001 From: Harvey Zack Date: Wed, 21 May 2025 23:55:32 +0800 Subject: [PATCH] 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. --- docs/assets/example/asr.js | 57 +++ docs/assets/js/common.js | 1 + docs/assets/ts/artplayer-plugin-asr.d.ts | 20 + docs/assets/ts/artplayer.d.ts | 1 - docs/compiled/artplayer-plugin-asr.esm.js | 9 + docs/compiled/artplayer-plugin-asr.esm.js.map | 1 + docs/compiled/artplayer-plugin-asr.js | 8 + docs/compiled/artplayer-plugin-asr.legacy.js | 8 + docs/index.html | 4 + docs/uncompiled/artplayer-plugin-asr/index.js | 406 ++++++++++++++++++ .../artplayer-plugin-asr/index.js.map | 1 + packages/artplayer-plugin-asr/.npmignore | 10 + packages/artplayer-plugin-asr/.npmrc | 1 + packages/artplayer-plugin-asr/README.md | 11 + .../dist/artplayer-plugin-asr.esm.js | 9 + .../dist/artplayer-plugin-asr.esm.js.map | 1 + .../dist/artplayer-plugin-asr.js | 8 + .../dist/artplayer-plugin-asr.legacy.js | 8 + packages/artplayer-plugin-asr/package.json | 30 ++ packages/artplayer-plugin-asr/src/index.js | 249 +++++++++++ packages/artplayer-plugin-asr/src/style.less | 31 ++ .../types/artplayer-plugin-asr.d.ts | 20 + 22 files changed, 893 insertions(+), 1 deletion(-) create mode 100644 docs/assets/example/asr.js create mode 100644 docs/assets/ts/artplayer-plugin-asr.d.ts create mode 100644 docs/compiled/artplayer-plugin-asr.esm.js create mode 100644 docs/compiled/artplayer-plugin-asr.esm.js.map create mode 100644 docs/compiled/artplayer-plugin-asr.js create mode 100644 docs/compiled/artplayer-plugin-asr.legacy.js create mode 100644 docs/uncompiled/artplayer-plugin-asr/index.js create mode 100644 docs/uncompiled/artplayer-plugin-asr/index.js.map create mode 100644 packages/artplayer-plugin-asr/.npmignore create mode 100644 packages/artplayer-plugin-asr/.npmrc create mode 100644 packages/artplayer-plugin-asr/README.md create mode 100644 packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js create mode 100644 packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js.map create mode 100644 packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js create mode 100644 packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js create mode 100644 packages/artplayer-plugin-asr/package.json create mode 100644 packages/artplayer-plugin-asr/src/index.js create mode 100644 packages/artplayer-plugin-asr/src/style.less create mode 100644 packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts diff --git a/docs/assets/example/asr.js b/docs/assets/example/asr.js new file mode 100644 index 000000000..5e93e3006 --- /dev/null +++ b/docs/assets/example/asr.js @@ -0,0 +1,57 @@ +var art = new Artplayer({ + container: '.artplayer-app', + url: '/assets/sample/video.mp4', + autoSize: true, + fullscreen: true, + fullscreenWeb: true, + plugins: [ + artplayerPluginAsr({ + length: 3, + interval: 40, + sampleRate: 16000, + hideTimeout: 10000, + // Use your AI tool to convert buffer into subtitles + onAudioChunk: (buffer) => tencentASR(buffer), + }), + ] +}); + +var ws = null; +var loading = false; +async function tencentASR(buffer) { + if (loading) return; + + if (!ws) { + loading = true; + const api = 'https://api.aimu.app/asr/tencent?engine_model_type=16k_ja'; + const { url } = await (await fetch(api)).json(); + ws = new WebSocket(url); + ws.binaryType = 'arraybuffer'; + ws.onmessage = (event) => { + const { code, result } = JSON.parse(event.data); + if (code === 0) { + const subtitle = result?.voice_text_str; + art.plugins.artplayerPluginAsr.append(subtitle); + } else { + ws.close(); + ws = null; + } + + }; + loading = false; + } + + if (ws.readyState === WebSocket.OPEN) { + ws.send(buffer); + } else { + console.warn('WebSocket is not open'); + } +} + +art.on('destroy', () => { + try { + ws.send(JSON.stringify({ type: 'end' })); + ws.close(); + } catch {} + ws = null; +}); diff --git a/docs/assets/js/common.js b/docs/assets/js/common.js index 517d15a3f..f175583f3 100644 --- a/docs/assets/js/common.js +++ b/docs/assets/js/common.js @@ -48,6 +48,7 @@ var libUris = [ './assets/ts/artplayer-plugin-ads.d.ts', + './assets/ts/artplayer-plugin-asr.d.ts', './assets/ts/artplayer-plugin-ambilight.d.ts', './assets/ts/artplayer-plugin-auto-thumbnail.d.ts', './assets/ts/artplayer-plugin-chapter.d.ts', diff --git a/docs/assets/ts/artplayer-plugin-asr.d.ts b/docs/assets/ts/artplayer-plugin-asr.d.ts new file mode 100644 index 000000000..a744cf07f --- /dev/null +++ b/docs/assets/ts/artplayer-plugin-asr.d.ts @@ -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; +} + +interface AsrPluginInstance { + name: 'artplayerPluginAsr'; + hide: () => void; + append: () => void; +} + +declare function artplayerPluginAsr(option?: AsrPluginOption): (art: Artplayer) => AsrPluginInstance; diff --git a/docs/assets/ts/artplayer.d.ts b/docs/assets/ts/artplayer.d.ts index d59beb74e..d85edad21 100644 --- a/docs/assets/ts/artplayer.d.ts +++ b/docs/assets/ts/artplayer.d.ts @@ -58,7 +58,6 @@ export type Utils = { export type Template = { readonly $container: HTMLDivElement; - readonly $original: HTMLDivElement; readonly $player: HTMLDivElement; readonly $video: HTMLVideoElement; readonly $track: HTMLTrackElement; diff --git a/docs/compiled/artplayer-plugin-asr.esm.js b/docs/compiled/artplayer-plugin-asr.esm.js new file mode 100644 index 000000000..bcd4d40d7 --- /dev/null +++ b/docs/compiled/artplayer-plugin-asr.esm.js @@ -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;ynull}=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;ni),"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 \ No newline at end of file diff --git a/docs/compiled/artplayer-plugin-asr.esm.js.map b/docs/compiled/artplayer-plugin-asr.esm.js.map new file mode 100644 index 000000000..9a88cf004 --- /dev/null +++ b/docs/compiled/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":["","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 no-undef */\n\n // Save the require from previous bundle to this closure if any\n var previousRequire =\n typeof globalObject[parcelRequireName] === 'function' &&\n globalObject[parcelRequireName];\n\n var importMap = previousRequire.i || {};\n var cache = previousRequire.cache || {};\n // Do not use `require` to prevent Webpack from trying to bundle this call\n var nodeRequire =\n typeof module !== 'undefined' &&\n typeof module.require === 'function' &&\n module.require.bind(module);\n\n function newRequire(name, jumped) {\n if (!cache[name]) {\n if (!modules[name]) {\n if (externals[name]) {\n return externals[name];\n }\n // if we cannot find the module within our internal map or\n // cache jump to the current global require ie. the last bundle\n // that was added to the page.\n var currentRequire =\n typeof globalObject[parcelRequireName] === 'function' &&\n globalObject[parcelRequireName];\n if (!jumped && currentRequire) {\n return currentRequire(name, true);\n }\n\n // If there are other bundles on this page the require from the\n // previous one is saved to 'previousRequire'. Repeat this as\n // many times as there are bundles until the module is found or\n // we exhaust the require chain.\n if (previousRequire) {\n return previousRequire(name, true);\n }\n\n // Try the node require function if it exists.\n if (nodeRequire && typeof name === 'string') {\n return nodeRequire(name);\n }\n\n var err = new Error(\"Cannot find module '\" + name + \"'\");\n err.code = 'MODULE_NOT_FOUND';\n throw err;\n }\n\n localRequire.resolve = resolve;\n localRequire.cache = {};\n\n var module = (cache[name] = new newRequire.Module(name));\n\n modules[name][0].call(\n module.exports,\n localRequire,\n module,\n module.exports,\n globalObject\n );\n }\n\n return cache[name].exports;\n\n function localRequire(x) {\n var res = localRequire.resolve(x);\n return res === false ? {} : newRequire(res);\n }\n\n function resolve(x) {\n var id = modules[name][1][x];\n return id != null ? id : x;\n }\n }\n\n function Module(moduleName) {\n this.id = moduleName;\n this.bundle = newRequire;\n this.require = nodeRequire;\n this.exports = {};\n }\n\n newRequire.isParcelRequire = true;\n newRequire.Module = Module;\n newRequire.modules = modules;\n newRequire.cache = cache;\n newRequire.parent = previousRequire;\n newRequire.distDir = distDir;\n newRequire.publicUrl = publicUrl;\n newRequire.devServer = devServer;\n newRequire.i = importMap;\n newRequire.register = function (id, exports) {\n modules[id] = [\n function (require, module) {\n module.exports = exports;\n },\n {},\n ];\n };\n\n // Only insert newRequire.load when it is actually used.\n // The code in this file is linted against ES5, so dynamic import is not allowed.\n // INSERT_LOAD_HERE\n\n Object.defineProperty(newRequire, 'root', {\n get: function () {\n return globalObject[parcelRequireName];\n },\n });\n\n globalObject[parcelRequireName] = newRequire;\n\n for (var i = 0; i < entry.length; i++) {\n newRequire(entry[i]);\n }\n\n if (mainEntry) {\n // Expose entry point to Node, AMD or browser globals\n // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js\n var mainExports = newRequire(mainEntry);\n\n // CommonJS\n if (typeof exports === 'object' && typeof module !== 'undefined') {\n module.exports = mainExports;\n\n // RequireJS\n } else if (typeof define === 'function' && define.amd) {\n define(function () {\n return mainExports;\n });\n }\n }\n})({\"40eeC\":[function(require,module,exports,__globalThis) {\nvar parcelHelpers = require(\"@parcel/transformer-js/src/esmodule-helpers.js\");\nparcelHelpers.defineInteropFlag(exports);\nparcelHelpers.export(exports, \"default\", ()=>artplayerPluginAsr);\nfunction artplayerPluginAsr(option = {}) {\n const { interval = 5000, sampleRate = 16000, callback = ()=>null } = option;\n return (art)=>{\n let started = false;\n let audioCtx = null;\n let sourceNode = null;\n let recorderNode = null;\n let bufferChunks = [];\n let timer = null;\n const recorderProcessorCode = `\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 `;\n const createWorkletBlobUrl = ()=>{\n const blob = new Blob([\n recorderProcessorCode\n ], {\n type: 'application/javascript'\n });\n return URL.createObjectURL(blob);\n };\n function floatTo16BitPCM(float32Array) {\n const len = float32Array.length;\n const buffer = new ArrayBuffer(len * 2);\n const view = new DataView(buffer);\n for(let i = 0; i < len; i++){\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n return buffer;\n }\n function mergeFloat32Array(chunks) {\n const length = chunks.reduce((acc, cur)=>acc + cur.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n chunks.forEach((chunk)=>{\n result.set(chunk, offset);\n offset += chunk.length;\n });\n return result;\n }\n function encodeWavPCM(buffers, sampleRate) {\n const samples = mergeFloat32Array(buffers);\n const dataLength = samples.length * 2;\n const buffer = new ArrayBuffer(44 + dataLength);\n const view = new DataView(buffer);\n function writeStr(offset, str) {\n for(let i = 0; i < str.length; i++)view.setUint8(offset + i, str.charCodeAt(i));\n }\n writeStr(0, 'RIFF');\n view.setUint32(4, 36 + dataLength, true);\n writeStr(8, 'WAVE');\n writeStr(12, 'fmt ');\n view.setUint32(16, 16, true); // fmt chunk size\n view.setUint16(20, 1, true); // audio format = PCM\n view.setUint16(22, 1, true); // mono\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true); // byte rate\n view.setUint16(32, 2, true); // block align\n view.setUint16(34, 16, true); // bits per sample\n writeStr(36, 'data');\n view.setUint32(40, dataLength, true);\n const pcm = floatTo16BitPCM(samples);\n new Uint8Array(buffer).set(new Uint8Array(pcm), 44);\n return new Blob([\n view\n ], {\n type: 'audio/wav'\n });\n }\n async function startCapture() {\n if (started) return;\n started = true;\n audioCtx = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate\n });\n if (audioCtx.state === 'suspended') await audioCtx.resume();\n if (!sourceNode) {\n sourceNode = audioCtx.createMediaElementSource(art.video);\n sourceNode.connect(audioCtx.destination);\n }\n const blobUrl = createWorkletBlobUrl();\n await audioCtx.audioWorklet.addModule(blobUrl);\n URL.revokeObjectURL(blobUrl);\n recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');\n recorderNode.port.onmessage = (event)=>{\n bufferChunks.push(new Float32Array(event.data));\n };\n sourceNode.connect(recorderNode).connect(audioCtx.destination);\n timer = setInterval(async ()=>{\n if (bufferChunks.length === 0) return;\n const wav = encodeWavPCM(bufferChunks, audioCtx.sampleRate);\n const buffer = await wav.arrayBuffer();\n await callback(buffer);\n bufferChunks = [];\n }, interval);\n }\n function stopCapture() {\n if (!started) return;\n started = false;\n if (timer) clearInterval(timer);\n if (recorderNode) recorderNode.disconnect();\n recorderNode = null;\n bufferChunks = [];\n }\n art.on('play', startCapture);\n art.on('pause', stopCapture);\n art.on('destroy', stopCapture);\n return {\n name: 'artplayerPluginAsr',\n destroy: stopCapture\n };\n };\n}\nif (typeof window !== 'undefined') window['artplayerPluginAsr'] = artplayerPluginAsr;\n\n},{\"@parcel/transformer-js/src/esmodule-helpers.js\":\"5lQoW\"}],\"5lQoW\":[function(require,module,exports,__globalThis) {\nexports.interopDefault = function(a) {\n return a && a.__esModule ? a : {\n default: a\n };\n};\nexports.defineInteropFlag = function(a) {\n Object.defineProperty(a, '__esModule', {\n value: true\n });\n};\nexports.exportAll = function(source, dest) {\n Object.keys(source).forEach(function(key) {\n if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) return;\n Object.defineProperty(dest, key, {\n enumerable: true,\n get: function() {\n return source[key];\n }\n });\n });\n return dest;\n};\nexports.export = function(dest, destName, get) {\n Object.defineProperty(dest, destName, {\n enumerable: true,\n get: get\n });\n};\n\n},{}]},[\"40eeC\"], \"40eeC\", \"parcelRequire4dc0\", {})\nlet {default: _default, } = parcelRequire4dc0(\"40eeC\");\nexport {_default as default, };\n\n//# sourceMappingURL=index.js.map\n","export default function artplayerPluginAsr(option = {}) {\n const { interval = 5000, sampleRate = 16000, callback = () => null } = option;\n\n return (art) => {\n let started = false;\n let audioCtx = null;\n let sourceNode = null;\n let recorderNode = null;\n let bufferChunks = [];\n let timer = null;\n\n const recorderProcessorCode = `\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 `;\n\n const createWorkletBlobUrl = () => {\n const blob = new Blob([recorderProcessorCode], { type: 'application/javascript' });\n return URL.createObjectURL(blob);\n };\n\n function floatTo16BitPCM(float32Array) {\n const len = float32Array.length;\n const buffer = new ArrayBuffer(len * 2);\n const view = new DataView(buffer);\n for (let i = 0; i < len; i++) {\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n return buffer;\n }\n\n function mergeFloat32Array(chunks) {\n const length = chunks.reduce((acc, cur) => acc + cur.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n chunks.forEach((chunk) => {\n result.set(chunk, offset);\n offset += chunk.length;\n });\n return result;\n }\n\n function encodeWavPCM(buffers, sampleRate) {\n const samples = mergeFloat32Array(buffers);\n const dataLength = samples.length * 2;\n const buffer = new ArrayBuffer(44 + dataLength);\n const view = new DataView(buffer);\n\n function writeStr(offset, str) {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n }\n\n writeStr(0, 'RIFF');\n view.setUint32(4, 36 + dataLength, true);\n writeStr(8, 'WAVE');\n writeStr(12, 'fmt ');\n view.setUint32(16, 16, true); // fmt chunk size\n view.setUint16(20, 1, true); // audio format = PCM\n view.setUint16(22, 1, true); // mono\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true); // byte rate\n view.setUint16(32, 2, true); // block align\n view.setUint16(34, 16, true); // bits per sample\n writeStr(36, 'data');\n view.setUint32(40, dataLength, true);\n\n const pcm = floatTo16BitPCM(samples);\n new Uint8Array(buffer).set(new Uint8Array(pcm), 44);\n\n return new Blob([view], { type: 'audio/wav' });\n }\n\n async function startCapture() {\n if (started) return;\n started = true;\n\n audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate });\n\n if (audioCtx.state === 'suspended') {\n await audioCtx.resume();\n }\n\n if (!sourceNode) {\n sourceNode = audioCtx.createMediaElementSource(art.video);\n sourceNode.connect(audioCtx.destination);\n }\n\n const blobUrl = createWorkletBlobUrl();\n await audioCtx.audioWorklet.addModule(blobUrl);\n URL.revokeObjectURL(blobUrl);\n\n recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');\n recorderNode.port.onmessage = (event) => {\n bufferChunks.push(new Float32Array(event.data));\n };\n\n sourceNode.connect(recorderNode).connect(audioCtx.destination);\n\n timer = setInterval(async () => {\n if (bufferChunks.length === 0) return;\n const wav = encodeWavPCM(bufferChunks, audioCtx.sampleRate);\n const buffer = await wav.arrayBuffer();\n await callback(buffer);\n bufferChunks = [];\n }, interval);\n }\n\n function stopCapture() {\n if (!started) return;\n started = false;\n if (timer) clearInterval(timer);\n if (recorderNode) recorderNode.disconnect();\n recorderNode = null;\n bufferChunks = [];\n }\n\n art.on('play', startCapture);\n art.on('pause', stopCapture);\n art.on('destroy', stopCapture);\n\n return {\n name: 'artplayerPluginAsr',\n destroy: stopCapture,\n };\n };\n}\n\nif (typeof window !== 'undefined') {\n window['artplayerPluginAsr'] = artplayerPluginAsr;\n}\n","exports.interopDefault = function (a) {\n return a && a.__esModule ? a : {default: a};\n};\n\nexports.defineInteropFlag = function (a) {\n Object.defineProperty(a, '__esModule', {value: true});\n};\n\nexports.exportAll = function (source, dest) {\n Object.keys(source).forEach(function (key) {\n if (\n key === 'default' ||\n key === '__esModule' ||\n Object.prototype.hasOwnProperty.call(dest, key)\n ) {\n return;\n }\n\n Object.defineProperty(dest, key, {\n enumerable: true,\n get: function () {\n return source[key];\n },\n });\n });\n\n return dest;\n};\n\nexports.export = function (dest, destName, get) {\n Object.defineProperty(dest, destName, {\n enumerable: true,\n get: get,\n });\n};\n"],"names":["modules","entry","mainEntry","parcelRequireName","externals","distDir","publicUrl","devServer","globalObject","globalThis","self","window","global","previousRequire","importMap","i","cache","nodeRequire","module","require","bind","newRequire","name","jumped","currentRequire","err","Error","code","localRequire","resolve","x","id","Module","call","exports","res","isParcelRequire","moduleName","bundle","parent","register","Object","defineProperty","get","length","mainExports","define","amd","__globalThis","parcelHelpers","artplayerPluginAsr","option","interval","sampleRate","callback","art","started","audioCtx","sourceNode","recorderNode","bufferChunks","timer","recorderProcessorCode","createWorkletBlobUrl","blob","Blob","type","URL","createObjectURL","startCapture","AudioContext","webkitAudioContext","state","resume","createMediaElementSource","video","connect","destination","blobUrl","audioWorklet","addModule","revokeObjectURL","AudioWorkletNode","port","onmessage","event","push","Float32Array","data","setInterval","wav","encodeWavPCM","buffers","samples","mergeFloat32Array","chunks","result","reduce","acc","cur","offset","forEach","chunk","set","dataLength","buffer","ArrayBuffer","view","DataView","writeStr","str","setUint8","charCodeAt","setUint32","setUint16","pcm","floatTo16BitPCM","float32Array","len","s","Math","max","min","setInt16","Uint8Array","arrayBuffer","stopCapture","clearInterval","disconnect","on","destroy","defineInteropFlag","export","interopDefault","a","__esModule","default","value","exportAll","source","dest","keys","key","prototype","hasOwnProperty","enumerable","destName","_default","parcelRequire4dc0"],"version":3,"file":"index.js.map"} \ No newline at end of file diff --git a/docs/compiled/artplayer-plugin-asr.js b/docs/compiled/artplayer-plugin-asr.js new file mode 100644 index 000000000..220ec95b9 --- /dev/null +++ b/docs/compiled/artplayer-plugin-asr.js @@ -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;ynull}=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;ni),"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",{}); \ No newline at end of file diff --git a/docs/compiled/artplayer-plugin-asr.legacy.js b/docs/compiled/artplayer-plugin-asr.legacy.js new file mode 100644 index 000000000..9adf9a226 --- /dev/null +++ b/docs/compiled/artplayer-plugin-asr.legacy.js @@ -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;y0&&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;rt.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r}function f(e,t,r,n){var o,u=arguments.length,a=u<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if(("undefined"==typeof Reflect?"undefined":(0,i._)(Reflect))==="object"&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,r,n);else for(var c=e.length-1;c>=0;c--)(o=e[c])&&(a=(u<3?o(a):u>3?o(t,r,a):o(t,r))||a);return u>3&&a&&Object.defineProperty(t,r,a),a}function l(e,t){return function(r,n){t(r,n,e)}}function p(e,t,r,n,o,u){function a(e){if(void 0!==e&&"function"!=typeof e)throw TypeError("Function expected");return e}for(var c,s=n.kind,f="getter"===s?"get":"setter"===s?"set":"value",l=!t&&e?n.static?e:e.prototype:null,p=t||(l?Object.getOwnPropertyDescriptor(l,n.name):{}),d=!1,y=r.length-1;y>=0;y--){var _={};for(var h in n)_[h]="access"===h?{}:n[h];for(var h in n.access)_.access[h]=n.access[h];_.addInitializer=function(e){if(d)throw TypeError("Cannot add initializers after decoration has completed");u.push(a(e||null))};var v=(0,r[y])("accessor"===s?{get:p.get,set:p.set}:p[f],_);if("accessor"===s){if(void 0===v)continue;if(null===v||(void 0===v?"undefined":(0,i._)(v))!=="object")throw TypeError("Object expected");(c=a(v.get))&&(p.get=c),(c=a(v.set))&&(p.set=c),(c=a(v.init))&&o.unshift(c)}else(c=a(v))&&("field"===s?o.unshift(c):p[f]=c)}l&&Object.defineProperty(l,n.name,p),d=!0}function d(e,t,r){for(var n=arguments.length>2,o=0;o0&&o[o.length-1])&&(6===s[0]||2===s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function j(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,i=r.call(e),u=[];try{for(;(void 0===t||t-- >0)&&!(n=i.next()).done;)u.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=i.return)&&r.call(i)}finally{if(o)throw o.error}}return u}function x(){for(var e=[],t=0;t1||a(e,t)})},t&&(n[e]=t(n[e])))}function a(e,t){try{var r;(r=o[e](t)).value instanceof S?Promise.resolve(r.value.v).then(c,s):f(i[0][2],r)}catch(e){f(i[0][3],e)}}function c(e){a("next",e)}function s(e){a("throw",e)}function f(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}}function A(e){var t,r;return t={},n("next"),n("throw",function(e){throw e}),n("return"),t[Symbol.iterator]=function(){return this},t;function n(n,o){t[n]=e[n]?function(t){return(r=!r)?{value:S(e[n](t)),done:!1}:o?o(t):t}:o}}function R(e){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var t,r=e[Symbol.asyncIterator];return r?r.call(e):(e=g(e),t={},n("next"),n("throw"),n("return"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=e[r]&&function(t){return new Promise(function(n,o){var i,u,a;i=n,u=o,a=(t=e[r](t)).done,Promise.resolve(t.value).then(function(e){i({value:e,done:a})},u)})}}}function I(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}var T=Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t},D=function(e){return(D=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r);return t})(e)};function M(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r=D(e),n=0;nartplayer-plugin-ambilight +
  • + artplayer-plugin-asr +
    • Proxys
    • diff --git a/docs/uncompiled/artplayer-plugin-asr/index.js b/docs/uncompiled/artplayer-plugin-asr/index.js new file mode 100644 index 000000000..a9fd3cbdc --- /dev/null +++ b/docs/uncompiled/artplayer-plugin-asr/index.js @@ -0,0 +1,406 @@ +// modules are defined as an array +// [ module function, map of requires ] +// +// map of requires is short require name -> numeric require +// +// anything defined in a previous bundle is accessed via the +// orig method which is the require for previous bundles + +(function ( + modules, + entry, + mainEntry, + parcelRequireName, + externals, + distDir, + publicUrl, + devServer +) { + /* eslint-disable no-undef */ + var globalObject = + typeof globalThis !== 'undefined' + ? globalThis + : typeof self !== 'undefined' + ? self + : typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}; + /* eslint-enable no-undef */ + + // Save the require from previous bundle to this closure if any + var previousRequire = + typeof globalObject[parcelRequireName] === 'function' && + globalObject[parcelRequireName]; + + var importMap = previousRequire.i || {}; + var cache = previousRequire.cache || {}; + // Do not use `require` to prevent Webpack from trying to bundle this call + var nodeRequire = + typeof module !== 'undefined' && + typeof module.require === 'function' && + module.require.bind(module); + + function newRequire(name, jumped) { + if (!cache[name]) { + if (!modules[name]) { + if (externals[name]) { + return externals[name]; + } + // if we cannot find the module within our internal map or + // cache jump to the current global require ie. the last bundle + // that was added to the page. + var currentRequire = + typeof globalObject[parcelRequireName] === 'function' && + globalObject[parcelRequireName]; + if (!jumped && currentRequire) { + return currentRequire(name, true); + } + + // If there are other bundles on this page the require from the + // previous one is saved to 'previousRequire'. Repeat this as + // many times as there are bundles until the module is found or + // we exhaust the require chain. + if (previousRequire) { + return previousRequire(name, true); + } + + // Try the node require function if it exists. + if (nodeRequire && typeof name === 'string') { + return nodeRequire(name); + } + + var err = new Error("Cannot find module '" + name + "'"); + err.code = 'MODULE_NOT_FOUND'; + throw err; + } + + localRequire.resolve = resolve; + localRequire.cache = {}; + + var module = (cache[name] = new newRequire.Module(name)); + + modules[name][0].call( + module.exports, + localRequire, + module, + module.exports, + globalObject + ); + } + + return cache[name].exports; + + function localRequire(x) { + var res = localRequire.resolve(x); + return res === false ? {} : newRequire(res); + } + + function resolve(x) { + var id = modules[name][1][x]; + return id != null ? id : x; + } + } + + function Module(moduleName) { + this.id = moduleName; + this.bundle = newRequire; + this.require = nodeRequire; + this.exports = {}; + } + + newRequire.isParcelRequire = true; + newRequire.Module = Module; + newRequire.modules = modules; + newRequire.cache = cache; + newRequire.parent = previousRequire; + newRequire.distDir = distDir; + newRequire.publicUrl = publicUrl; + newRequire.devServer = devServer; + newRequire.i = importMap; + newRequire.register = function (id, exports) { + modules[id] = [ + function (require, module) { + module.exports = exports; + }, + {}, + ]; + }; + + // Only insert newRequire.load when it is actually used. + // The code in this file is linted against ES5, so dynamic import is not allowed. + // INSERT_LOAD_HERE + + Object.defineProperty(newRequire, 'root', { + get: function () { + return globalObject[parcelRequireName]; + }, + }); + + globalObject[parcelRequireName] = newRequire; + + for (var i = 0; i < entry.length; i++) { + newRequire(entry[i]); + } + + if (mainEntry) { + // Expose entry point to Node, AMD or browser globals + // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js + var mainExports = newRequire(mainEntry); + + // CommonJS + if (typeof exports === 'object' && typeof module !== 'undefined') { + module.exports = mainExports; + + // RequireJS + } else if (typeof define === 'function' && define.amd) { + define(function () { + return mainExports; + }); + } + } +})({"eiaCd":[function(require,module,exports,__globalThis) { +var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); +parcelHelpers.defineInteropFlag(exports); +parcelHelpers.export(exports, "default", ()=>artplayerPluginAsr); +var _styleLess = require("bundle-text:./style.less"); +var _styleLessDefault = parcelHelpers.interopDefault(_styleLess); +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)=>`
      ${line}
      `).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 = (0, _styleLessDefault.default); + document.head.appendChild($style); + } +} +if (typeof window !== 'undefined') window['artplayerPluginAsr'] = artplayerPluginAsr; + +},{"bundle-text:./style.less":"enAJa","@parcel/transformer-js/src/esmodule-helpers.js":"8oCsH"}],"enAJa":[function(require,module,exports,__globalThis) { +module.exports = ".art-video-player .art-layer-asr {\n z-index: 150;\n justify-content: end;\n gap: var(--art-subtitle-gap);\n padding: 0 2%;\n padding-bottom: var(--art-subtitle-bottom);\n transition: padding-bottom var(--art-transition-duration) ease;\n 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;\n flex-direction: column;\n font-size: 1rem;\n display: flex;\n position: absolute;\n inset: 0;\n pointer-events: none !important;\n}\n\n.art-video-player.art-control-show .art-layer-asr {\n padding-bottom: calc(var(--art-control-height) + var(--art-subtitle-bottom));\n}\n"; + +},{}],"8oCsH":[function(require,module,exports,__globalThis) { +exports.interopDefault = function(a) { + return a && a.__esModule ? a : { + default: a + }; +}; +exports.defineInteropFlag = function(a) { + Object.defineProperty(a, '__esModule', { + value: true + }); +}; +exports.exportAll = function(source, dest) { + Object.keys(source).forEach(function(key) { + if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) return; + Object.defineProperty(dest, key, { + enumerable: true, + get: function() { + return source[key]; + } + }); + }); + return dest; +}; +exports.export = function(dest, destName, get) { + Object.defineProperty(dest, destName, { + enumerable: true, + get: get + }); +}; + +},{}]},["eiaCd"], "eiaCd", "parcelRequire4dc0", {}) + +//# sourceMappingURL=index.js.map diff --git a/docs/uncompiled/artplayer-plugin-asr/index.js.map b/docs/uncompiled/artplayer-plugin-asr/index.js.map new file mode 100644 index 000000000..62a31ce72 --- /dev/null +++ b/docs/uncompiled/artplayer-plugin-asr/index.js.map @@ -0,0 +1 @@ +{"mappings":"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","sources":["packages/artplayer-plugin-asr/src/index.js","node_modules/@parcel/runtime-js/lib/bundles/runtime-54f3e2ccbee34ab4.js","node_modules/@parcel/transformer-js/src/esmodule-helpers.js"],"sourcesContent":["import style from 'bundle-text:./style.less';\n\nexport default function artplayerPluginAsr(option = {}) {\n const { hideTimeout = 10000, length = 3, interval = 100, sampleRate = 16000, onAudioChunk = () => null } = option;\n\n return (art) => {\n let started = false;\n let audioCtx = null;\n let sourceNode = null;\n let recorderNode = null;\n let gainNode = null;\n let bufferChunks = [];\n let timer = null;\n let workletLoaded = false;\n let hideTimer = null;\n let mediaStream = null;\n let mediaStreamSource = null;\n\n const $asr = art.layers.add({\n name: 'asr',\n html: '',\n });\n\n function splitByPunctuation(text) {\n return text\n .split(/(?<=[、。!?!?\\.])\\s*/u)\n .map((s) => s.trim())\n .filter(Boolean)\n .slice(-length);\n }\n\n function hide() {\n $asr.style.display = 'none';\n }\n\n function append(subtitle) {\n if (typeof subtitle !== 'string') return;\n clearTimeout(hideTimer);\n hideTimer = setTimeout(hide, hideTimeout);\n $asr.style.display = '';\n $asr.innerHTML = splitByPunctuation(subtitle)\n .map((line) => `
      ${line}
      `)\n .join('');\n }\n\n const recorderProcessorCode = `\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 `;\n\n const createWorkletBlobUrl = () => {\n const blob = new Blob([recorderProcessorCode], { type: 'application/javascript' });\n return URL.createObjectURL(blob);\n };\n\n function floatTo16BitPCM(float32Array) {\n const len = float32Array.length;\n const buffer = new ArrayBuffer(len * 2);\n const view = new DataView(buffer);\n for (let i = 0; i < len; i++) {\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n return buffer;\n }\n\n async function setupAudioContext() {\n if (audioCtx) return audioCtx;\n audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate });\n if (audioCtx.state === 'suspended') await audioCtx.resume();\n return audioCtx;\n }\n\n async function setupAudioSource() {\n if (!sourceNode && art.video) {\n try {\n sourceNode = audioCtx.createMediaElementSource(art.video);\n return sourceNode;\n } catch (err) {\n console.warn('[artplayerPluginAsr] Direct connection failed, trying fallback...', err);\n }\n }\n\n try {\n if (!mediaStream) {\n mediaStream = await captureAudioFromVideo(art.video);\n }\n if (mediaStream) {\n mediaStreamSource = audioCtx.createMediaStreamSource(mediaStream);\n return mediaStreamSource;\n }\n } catch (err) {\n console.warn('[artplayerPluginAsr] MediaStream fallback failed:', err);\n }\n\n return null;\n }\n\n async function captureAudioFromVideo(videoElement) {\n try {\n if (videoElement.captureStream) return videoElement.captureStream();\n if (videoElement.mozCaptureStream) return videoElement.mozCaptureStream();\n console.warn('[artplayerPluginAsr] captureStream not supported');\n return null;\n } catch (err) {\n console.warn('[artplayerPluginAsr] Error capturing stream:', err);\n return null;\n }\n }\n\n async function startCapture() {\n if (started) return;\n\n try {\n await setupAudioContext();\n const audioSource = await setupAudioSource();\n if (!audioSource) throw new Error('Could not establish audio source');\n\n if (!workletLoaded) {\n const blobUrl = createWorkletBlobUrl();\n await audioCtx.audioWorklet.addModule(blobUrl);\n URL.revokeObjectURL(blobUrl);\n workletLoaded = true;\n }\n\n // 创建输出音频的 GainNode(正常播放)\n gainNode = audioCtx.createGain();\n gainNode.gain.value = 1;\n\n recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');\n recorderNode.port.onmessage = (event) => {\n bufferChunks.push(new Float32Array(event.data));\n };\n\n // 分流:识别和播放\n audioSource.connect(recorderNode);\n audioSource.connect(gainNode);\n\n gainNode.connect(audioCtx.destination); // 正常声音输出\n // recorderNode 不连接 destination,避免输出干扰\n\n const CHUNK_SAMPLES = sampleRate * (interval / 1000);\n timer = setInterval(async () => {\n if (bufferChunks.length === 0) return;\n\n let accumulated = new Float32Array(0);\n while (accumulated.length < CHUNK_SAMPLES && bufferChunks.length) {\n const next = bufferChunks.shift();\n const tmp = new Float32Array(accumulated.length + next.length);\n tmp.set(accumulated, 0);\n tmp.set(next, accumulated.length);\n accumulated = tmp;\n }\n\n if (accumulated.length < CHUNK_SAMPLES) return;\n\n const chunkToSend = accumulated.slice(0, CHUNK_SAMPLES);\n const pcm = floatTo16BitPCM(chunkToSend);\n const subtitle = await onAudioChunk(pcm);\n append(subtitle);\n }, interval);\n\n started = true;\n } catch (err) {\n console.error('[artplayerPluginAsr] Initialization failed:', err);\n await stopCapture();\n }\n }\n\n async function stopCapture() {\n if (!started) return;\n started = false;\n\n clearInterval(timer);\n timer = null;\n\n if (recorderNode) {\n recorderNode.disconnect();\n recorderNode.port.onmessage = null;\n recorderNode = null;\n }\n\n if (gainNode) {\n gainNode.disconnect();\n gainNode = null;\n }\n\n bufferChunks = [];\n }\n\n async function destroy() {\n await stopCapture();\n\n if (mediaStreamSource) {\n mediaStreamSource.disconnect();\n mediaStreamSource = null;\n }\n\n if (sourceNode) {\n sourceNode.disconnect();\n sourceNode = null;\n }\n\n if (mediaStream) {\n mediaStream.getTracks().forEach((track) => track.stop());\n mediaStream = null;\n }\n\n if (audioCtx) {\n await audioCtx.close();\n audioCtx = null;\n }\n\n workletLoaded = false;\n }\n\n art.on('play', startCapture);\n art.on('pause', stopCapture);\n art.on('destroy', destroy);\n\n return {\n name: 'artplayerPluginAsr',\n stop: destroy,\n hide,\n append,\n };\n };\n}\n\nif (typeof document !== 'undefined') {\n if (!document.getElementById('artplayer-plugin-asr')) {\n const $style = document.createElement('style');\n $style.id = 'artplayer-plugin-asr';\n $style.textContent = style;\n document.head.appendChild($style);\n }\n}\n\nif (typeof window !== 'undefined') {\n window['artplayerPluginAsr'] = artplayerPluginAsr;\n}\n","module.exports = \"c5e0457df51aebc9\";","exports.interopDefault = function (a) {\n return a && a.__esModule ? a : {default: a};\n};\n\nexports.defineInteropFlag = function (a) {\n Object.defineProperty(a, '__esModule', {value: true});\n};\n\nexports.exportAll = function (source, dest) {\n Object.keys(source).forEach(function (key) {\n if (\n key === 'default' ||\n key === '__esModule' ||\n Object.prototype.hasOwnProperty.call(dest, key)\n ) {\n return;\n }\n\n Object.defineProperty(dest, key, {\n enumerable: true,\n get: function () {\n return source[key];\n },\n });\n });\n\n return dest;\n};\n\nexports.export = function (dest, destName, get) {\n Object.defineProperty(dest, destName, {\n enumerable: true,\n get: get,\n });\n};\n"],"names":[],"version":3,"file":"index.js.map","sourceRoot":"../../../"} \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/.npmignore b/packages/artplayer-plugin-asr/.npmignore new file mode 100644 index 000000000..ec696c3f1 --- /dev/null +++ b/packages/artplayer-plugin-asr/.npmignore @@ -0,0 +1,10 @@ +node_modules +.DS_Store +yarn.lock +package-lock.json +test +coverage +.nyc_output +.parcel-cache +.cache +docs \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/.npmrc b/packages/artplayer-plugin-asr/.npmrc new file mode 100644 index 000000000..9cf949503 --- /dev/null +++ b/packages/artplayer-plugin-asr/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/README.md b/packages/artplayer-plugin-asr/README.md new file mode 100644 index 000000000..d10339ce8 --- /dev/null +++ b/packages/artplayer-plugin-asr/README.md @@ -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 diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js new file mode 100644 index 000000000..bcd4d40d7 --- /dev/null +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js @@ -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;ynull}=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;ni),"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 \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js.map b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.esm.js.map new file mode 100644 index 000000000..9a88cf004 --- /dev/null +++ b/packages/artplayer-plugin-asr/dist/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":["","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 no-undef */\n\n // Save the require from previous bundle to this closure if any\n var previousRequire =\n typeof globalObject[parcelRequireName] === 'function' &&\n globalObject[parcelRequireName];\n\n var importMap = previousRequire.i || {};\n var cache = previousRequire.cache || {};\n // Do not use `require` to prevent Webpack from trying to bundle this call\n var nodeRequire =\n typeof module !== 'undefined' &&\n typeof module.require === 'function' &&\n module.require.bind(module);\n\n function newRequire(name, jumped) {\n if (!cache[name]) {\n if (!modules[name]) {\n if (externals[name]) {\n return externals[name];\n }\n // if we cannot find the module within our internal map or\n // cache jump to the current global require ie. the last bundle\n // that was added to the page.\n var currentRequire =\n typeof globalObject[parcelRequireName] === 'function' &&\n globalObject[parcelRequireName];\n if (!jumped && currentRequire) {\n return currentRequire(name, true);\n }\n\n // If there are other bundles on this page the require from the\n // previous one is saved to 'previousRequire'. Repeat this as\n // many times as there are bundles until the module is found or\n // we exhaust the require chain.\n if (previousRequire) {\n return previousRequire(name, true);\n }\n\n // Try the node require function if it exists.\n if (nodeRequire && typeof name === 'string') {\n return nodeRequire(name);\n }\n\n var err = new Error(\"Cannot find module '\" + name + \"'\");\n err.code = 'MODULE_NOT_FOUND';\n throw err;\n }\n\n localRequire.resolve = resolve;\n localRequire.cache = {};\n\n var module = (cache[name] = new newRequire.Module(name));\n\n modules[name][0].call(\n module.exports,\n localRequire,\n module,\n module.exports,\n globalObject\n );\n }\n\n return cache[name].exports;\n\n function localRequire(x) {\n var res = localRequire.resolve(x);\n return res === false ? {} : newRequire(res);\n }\n\n function resolve(x) {\n var id = modules[name][1][x];\n return id != null ? id : x;\n }\n }\n\n function Module(moduleName) {\n this.id = moduleName;\n this.bundle = newRequire;\n this.require = nodeRequire;\n this.exports = {};\n }\n\n newRequire.isParcelRequire = true;\n newRequire.Module = Module;\n newRequire.modules = modules;\n newRequire.cache = cache;\n newRequire.parent = previousRequire;\n newRequire.distDir = distDir;\n newRequire.publicUrl = publicUrl;\n newRequire.devServer = devServer;\n newRequire.i = importMap;\n newRequire.register = function (id, exports) {\n modules[id] = [\n function (require, module) {\n module.exports = exports;\n },\n {},\n ];\n };\n\n // Only insert newRequire.load when it is actually used.\n // The code in this file is linted against ES5, so dynamic import is not allowed.\n // INSERT_LOAD_HERE\n\n Object.defineProperty(newRequire, 'root', {\n get: function () {\n return globalObject[parcelRequireName];\n },\n });\n\n globalObject[parcelRequireName] = newRequire;\n\n for (var i = 0; i < entry.length; i++) {\n newRequire(entry[i]);\n }\n\n if (mainEntry) {\n // Expose entry point to Node, AMD or browser globals\n // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js\n var mainExports = newRequire(mainEntry);\n\n // CommonJS\n if (typeof exports === 'object' && typeof module !== 'undefined') {\n module.exports = mainExports;\n\n // RequireJS\n } else if (typeof define === 'function' && define.amd) {\n define(function () {\n return mainExports;\n });\n }\n }\n})({\"40eeC\":[function(require,module,exports,__globalThis) {\nvar parcelHelpers = require(\"@parcel/transformer-js/src/esmodule-helpers.js\");\nparcelHelpers.defineInteropFlag(exports);\nparcelHelpers.export(exports, \"default\", ()=>artplayerPluginAsr);\nfunction artplayerPluginAsr(option = {}) {\n const { interval = 5000, sampleRate = 16000, callback = ()=>null } = option;\n return (art)=>{\n let started = false;\n let audioCtx = null;\n let sourceNode = null;\n let recorderNode = null;\n let bufferChunks = [];\n let timer = null;\n const recorderProcessorCode = `\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 `;\n const createWorkletBlobUrl = ()=>{\n const blob = new Blob([\n recorderProcessorCode\n ], {\n type: 'application/javascript'\n });\n return URL.createObjectURL(blob);\n };\n function floatTo16BitPCM(float32Array) {\n const len = float32Array.length;\n const buffer = new ArrayBuffer(len * 2);\n const view = new DataView(buffer);\n for(let i = 0; i < len; i++){\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n return buffer;\n }\n function mergeFloat32Array(chunks) {\n const length = chunks.reduce((acc, cur)=>acc + cur.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n chunks.forEach((chunk)=>{\n result.set(chunk, offset);\n offset += chunk.length;\n });\n return result;\n }\n function encodeWavPCM(buffers, sampleRate) {\n const samples = mergeFloat32Array(buffers);\n const dataLength = samples.length * 2;\n const buffer = new ArrayBuffer(44 + dataLength);\n const view = new DataView(buffer);\n function writeStr(offset, str) {\n for(let i = 0; i < str.length; i++)view.setUint8(offset + i, str.charCodeAt(i));\n }\n writeStr(0, 'RIFF');\n view.setUint32(4, 36 + dataLength, true);\n writeStr(8, 'WAVE');\n writeStr(12, 'fmt ');\n view.setUint32(16, 16, true); // fmt chunk size\n view.setUint16(20, 1, true); // audio format = PCM\n view.setUint16(22, 1, true); // mono\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true); // byte rate\n view.setUint16(32, 2, true); // block align\n view.setUint16(34, 16, true); // bits per sample\n writeStr(36, 'data');\n view.setUint32(40, dataLength, true);\n const pcm = floatTo16BitPCM(samples);\n new Uint8Array(buffer).set(new Uint8Array(pcm), 44);\n return new Blob([\n view\n ], {\n type: 'audio/wav'\n });\n }\n async function startCapture() {\n if (started) return;\n started = true;\n audioCtx = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate\n });\n if (audioCtx.state === 'suspended') await audioCtx.resume();\n if (!sourceNode) {\n sourceNode = audioCtx.createMediaElementSource(art.video);\n sourceNode.connect(audioCtx.destination);\n }\n const blobUrl = createWorkletBlobUrl();\n await audioCtx.audioWorklet.addModule(blobUrl);\n URL.revokeObjectURL(blobUrl);\n recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');\n recorderNode.port.onmessage = (event)=>{\n bufferChunks.push(new Float32Array(event.data));\n };\n sourceNode.connect(recorderNode).connect(audioCtx.destination);\n timer = setInterval(async ()=>{\n if (bufferChunks.length === 0) return;\n const wav = encodeWavPCM(bufferChunks, audioCtx.sampleRate);\n const buffer = await wav.arrayBuffer();\n await callback(buffer);\n bufferChunks = [];\n }, interval);\n }\n function stopCapture() {\n if (!started) return;\n started = false;\n if (timer) clearInterval(timer);\n if (recorderNode) recorderNode.disconnect();\n recorderNode = null;\n bufferChunks = [];\n }\n art.on('play', startCapture);\n art.on('pause', stopCapture);\n art.on('destroy', stopCapture);\n return {\n name: 'artplayerPluginAsr',\n destroy: stopCapture\n };\n };\n}\nif (typeof window !== 'undefined') window['artplayerPluginAsr'] = artplayerPluginAsr;\n\n},{\"@parcel/transformer-js/src/esmodule-helpers.js\":\"5lQoW\"}],\"5lQoW\":[function(require,module,exports,__globalThis) {\nexports.interopDefault = function(a) {\n return a && a.__esModule ? a : {\n default: a\n };\n};\nexports.defineInteropFlag = function(a) {\n Object.defineProperty(a, '__esModule', {\n value: true\n });\n};\nexports.exportAll = function(source, dest) {\n Object.keys(source).forEach(function(key) {\n if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) return;\n Object.defineProperty(dest, key, {\n enumerable: true,\n get: function() {\n return source[key];\n }\n });\n });\n return dest;\n};\nexports.export = function(dest, destName, get) {\n Object.defineProperty(dest, destName, {\n enumerable: true,\n get: get\n });\n};\n\n},{}]},[\"40eeC\"], \"40eeC\", \"parcelRequire4dc0\", {})\nlet {default: _default, } = parcelRequire4dc0(\"40eeC\");\nexport {_default as default, };\n\n//# sourceMappingURL=index.js.map\n","export default function artplayerPluginAsr(option = {}) {\n const { interval = 5000, sampleRate = 16000, callback = () => null } = option;\n\n return (art) => {\n let started = false;\n let audioCtx = null;\n let sourceNode = null;\n let recorderNode = null;\n let bufferChunks = [];\n let timer = null;\n\n const recorderProcessorCode = `\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 `;\n\n const createWorkletBlobUrl = () => {\n const blob = new Blob([recorderProcessorCode], { type: 'application/javascript' });\n return URL.createObjectURL(blob);\n };\n\n function floatTo16BitPCM(float32Array) {\n const len = float32Array.length;\n const buffer = new ArrayBuffer(len * 2);\n const view = new DataView(buffer);\n for (let i = 0; i < len; i++) {\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n return buffer;\n }\n\n function mergeFloat32Array(chunks) {\n const length = chunks.reduce((acc, cur) => acc + cur.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n chunks.forEach((chunk) => {\n result.set(chunk, offset);\n offset += chunk.length;\n });\n return result;\n }\n\n function encodeWavPCM(buffers, sampleRate) {\n const samples = mergeFloat32Array(buffers);\n const dataLength = samples.length * 2;\n const buffer = new ArrayBuffer(44 + dataLength);\n const view = new DataView(buffer);\n\n function writeStr(offset, str) {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n }\n\n writeStr(0, 'RIFF');\n view.setUint32(4, 36 + dataLength, true);\n writeStr(8, 'WAVE');\n writeStr(12, 'fmt ');\n view.setUint32(16, 16, true); // fmt chunk size\n view.setUint16(20, 1, true); // audio format = PCM\n view.setUint16(22, 1, true); // mono\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true); // byte rate\n view.setUint16(32, 2, true); // block align\n view.setUint16(34, 16, true); // bits per sample\n writeStr(36, 'data');\n view.setUint32(40, dataLength, true);\n\n const pcm = floatTo16BitPCM(samples);\n new Uint8Array(buffer).set(new Uint8Array(pcm), 44);\n\n return new Blob([view], { type: 'audio/wav' });\n }\n\n async function startCapture() {\n if (started) return;\n started = true;\n\n audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate });\n\n if (audioCtx.state === 'suspended') {\n await audioCtx.resume();\n }\n\n if (!sourceNode) {\n sourceNode = audioCtx.createMediaElementSource(art.video);\n sourceNode.connect(audioCtx.destination);\n }\n\n const blobUrl = createWorkletBlobUrl();\n await audioCtx.audioWorklet.addModule(blobUrl);\n URL.revokeObjectURL(blobUrl);\n\n recorderNode = new AudioWorkletNode(audioCtx, 'recorder-processor');\n recorderNode.port.onmessage = (event) => {\n bufferChunks.push(new Float32Array(event.data));\n };\n\n sourceNode.connect(recorderNode).connect(audioCtx.destination);\n\n timer = setInterval(async () => {\n if (bufferChunks.length === 0) return;\n const wav = encodeWavPCM(bufferChunks, audioCtx.sampleRate);\n const buffer = await wav.arrayBuffer();\n await callback(buffer);\n bufferChunks = [];\n }, interval);\n }\n\n function stopCapture() {\n if (!started) return;\n started = false;\n if (timer) clearInterval(timer);\n if (recorderNode) recorderNode.disconnect();\n recorderNode = null;\n bufferChunks = [];\n }\n\n art.on('play', startCapture);\n art.on('pause', stopCapture);\n art.on('destroy', stopCapture);\n\n return {\n name: 'artplayerPluginAsr',\n destroy: stopCapture,\n };\n };\n}\n\nif (typeof window !== 'undefined') {\n window['artplayerPluginAsr'] = artplayerPluginAsr;\n}\n","exports.interopDefault = function (a) {\n return a && a.__esModule ? a : {default: a};\n};\n\nexports.defineInteropFlag = function (a) {\n Object.defineProperty(a, '__esModule', {value: true});\n};\n\nexports.exportAll = function (source, dest) {\n Object.keys(source).forEach(function (key) {\n if (\n key === 'default' ||\n key === '__esModule' ||\n Object.prototype.hasOwnProperty.call(dest, key)\n ) {\n return;\n }\n\n Object.defineProperty(dest, key, {\n enumerable: true,\n get: function () {\n return source[key];\n },\n });\n });\n\n return dest;\n};\n\nexports.export = function (dest, destName, get) {\n Object.defineProperty(dest, destName, {\n enumerable: true,\n get: get,\n });\n};\n"],"names":["modules","entry","mainEntry","parcelRequireName","externals","distDir","publicUrl","devServer","globalObject","globalThis","self","window","global","previousRequire","importMap","i","cache","nodeRequire","module","require","bind","newRequire","name","jumped","currentRequire","err","Error","code","localRequire","resolve","x","id","Module","call","exports","res","isParcelRequire","moduleName","bundle","parent","register","Object","defineProperty","get","length","mainExports","define","amd","__globalThis","parcelHelpers","artplayerPluginAsr","option","interval","sampleRate","callback","art","started","audioCtx","sourceNode","recorderNode","bufferChunks","timer","recorderProcessorCode","createWorkletBlobUrl","blob","Blob","type","URL","createObjectURL","startCapture","AudioContext","webkitAudioContext","state","resume","createMediaElementSource","video","connect","destination","blobUrl","audioWorklet","addModule","revokeObjectURL","AudioWorkletNode","port","onmessage","event","push","Float32Array","data","setInterval","wav","encodeWavPCM","buffers","samples","mergeFloat32Array","chunks","result","reduce","acc","cur","offset","forEach","chunk","set","dataLength","buffer","ArrayBuffer","view","DataView","writeStr","str","setUint8","charCodeAt","setUint32","setUint16","pcm","floatTo16BitPCM","float32Array","len","s","Math","max","min","setInt16","Uint8Array","arrayBuffer","stopCapture","clearInterval","disconnect","on","destroy","defineInteropFlag","export","interopDefault","a","__esModule","default","value","exportAll","source","dest","keys","key","prototype","hasOwnProperty","enumerable","destName","_default","parcelRequire4dc0"],"version":3,"file":"index.js.map"} \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js new file mode 100644 index 000000000..220ec95b9 --- /dev/null +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js @@ -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;ynull}=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;ni),"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",{}); \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js new file mode 100644 index 000000000..9adf9a226 --- /dev/null +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js @@ -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;y0&&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;rt.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r}function f(e,t,r,n){var o,u=arguments.length,a=u<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if(("undefined"==typeof Reflect?"undefined":(0,i._)(Reflect))==="object"&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,r,n);else for(var c=e.length-1;c>=0;c--)(o=e[c])&&(a=(u<3?o(a):u>3?o(t,r,a):o(t,r))||a);return u>3&&a&&Object.defineProperty(t,r,a),a}function l(e,t){return function(r,n){t(r,n,e)}}function p(e,t,r,n,o,u){function a(e){if(void 0!==e&&"function"!=typeof e)throw TypeError("Function expected");return e}for(var c,s=n.kind,f="getter"===s?"get":"setter"===s?"set":"value",l=!t&&e?n.static?e:e.prototype:null,p=t||(l?Object.getOwnPropertyDescriptor(l,n.name):{}),d=!1,y=r.length-1;y>=0;y--){var _={};for(var h in n)_[h]="access"===h?{}:n[h];for(var h in n.access)_.access[h]=n.access[h];_.addInitializer=function(e){if(d)throw TypeError("Cannot add initializers after decoration has completed");u.push(a(e||null))};var v=(0,r[y])("accessor"===s?{get:p.get,set:p.set}:p[f],_);if("accessor"===s){if(void 0===v)continue;if(null===v||(void 0===v?"undefined":(0,i._)(v))!=="object")throw TypeError("Object expected");(c=a(v.get))&&(p.get=c),(c=a(v.set))&&(p.set=c),(c=a(v.init))&&o.unshift(c)}else(c=a(v))&&("field"===s?o.unshift(c):p[f]=c)}l&&Object.defineProperty(l,n.name,p),d=!0}function d(e,t,r){for(var n=arguments.length>2,o=0;o0&&o[o.length-1])&&(6===s[0]||2===s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function j(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,i=r.call(e),u=[];try{for(;(void 0===t||t-- >0)&&!(n=i.next()).done;)u.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=i.return)&&r.call(i)}finally{if(o)throw o.error}}return u}function x(){for(var e=[],t=0;t1||a(e,t)})},t&&(n[e]=t(n[e])))}function a(e,t){try{var r;(r=o[e](t)).value instanceof S?Promise.resolve(r.value.v).then(c,s):f(i[0][2],r)}catch(e){f(i[0][3],e)}}function c(e){a("next",e)}function s(e){a("throw",e)}function f(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}}function A(e){var t,r;return t={},n("next"),n("throw",function(e){throw e}),n("return"),t[Symbol.iterator]=function(){return this},t;function n(n,o){t[n]=e[n]?function(t){return(r=!r)?{value:S(e[n](t)),done:!1}:o?o(t):t}:o}}function R(e){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var t,r=e[Symbol.asyncIterator];return r?r.call(e):(e=g(e),t={},n("next"),n("throw"),n("return"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=e[r]&&function(t){return new Promise(function(n,o){var i,u,a;i=n,u=o,a=(t=e[r](t)).done,Promise.resolve(t.value).then(function(e){i({value:e,done:a})},u)})}}}function I(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}var T=Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t},D=function(e){return(D=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r);return t})(e)};function M(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r=D(e),n=0;n", + "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" + ] +} \ No newline at end of file diff --git a/packages/artplayer-plugin-asr/src/index.js b/packages/artplayer-plugin-asr/src/index.js new file mode 100644 index 000000000..64e026eb6 --- /dev/null +++ b/packages/artplayer-plugin-asr/src/index.js @@ -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) => `
      ${line}
      `) + .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; +} diff --git a/packages/artplayer-plugin-asr/src/style.less b/packages/artplayer-plugin-asr/src/style.less new file mode 100644 index 000000000..49e90b41d --- /dev/null +++ b/packages/artplayer-plugin-asr/src/style.less @@ -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)); + } + } +} diff --git a/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts b/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts new file mode 100644 index 000000000..a744cf07f --- /dev/null +++ b/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts @@ -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; +} + +interface AsrPluginInstance { + name: 'artplayerPluginAsr'; + hide: () => void; + append: () => void; +} + +declare function artplayerPluginAsr(option?: AsrPluginOption): (art: Artplayer) => AsrPluginInstance;