From e8a04fee2009a0870a296c7a9d6517b70e4446ef Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Wed, 28 Oct 2026 20:19:02 -0400 Subject: [PATCH] fix(denoise): ship the DeepFilterNet 3 worklet as a file so it starts in the desktop app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported: DeepFilterNet 3 "did not start" in a desktop call. Console: "Loading the script blob:http://127.0.0.1:44548/… violates … script-src", then "[lotus] denoise setProcessor failed … Failed to load worklet module". deepfilternet3-noise-filter inlines its AudioWorklet as a string and loads it from a blob: URL; audio worklets fall under script-src, and the desktop CSP has no blob: there (the web call page has no script-src, so web was fine). Instead of loosening the CSP, the build now writes the inline worklet to denoise/deepfilternet/dfn-worklet.js and patches the copied module to load it by URL (script-src self). The build fails if the library changes shape. Verified in Chromium under the exact desktop CSP with the built assets: before, DeepFilterNet fails as reported; after, it creates its node and, fed 4 s of white noise, cuts it by 60 dB (its configured level). RNNoise, Speex and DTLN load as before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5 --- vite.config.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/vite.config.js b/vite.config.js index 8e0638c3e..8184d5929 100644 --- a/vite.config.js +++ b/vite.config.js @@ -136,6 +136,36 @@ function denoiseSources() { }; } +// deepfilternet3-noise-filter inlines its AudioWorklet as a string and loads it +// from a blob: URL. Audio worklets fall under script-src, and the desktop app's +// CSP (rightly) has no blob: there, so DeepFilterNet 3 failed to start ("denoise +// setProcessor failed … Failed to load worklet module script: blob:…"). Write +// the worklet out as a real file next to the module and load it by URL instead, +// which script-src 'self' allows everywhere. Fails the build if the library's +// shape changes, rather than shipping a model that cannot start. +const DFN_WORKLET_FILE = 'dfn-worklet.js'; +const DFN_BLOB_LOAD = ` const blob = new Blob([workletCode], { type: 'application/javascript' }); + const blobUrl = URL.createObjectURL(blob); + await audioContext.audioWorklet.addModule(blobUrl);`; +function dfnWorkletToFile(esmPath) { + const src = fs.readFileSync(esmPath, 'utf8'); + const literal = src.match(/^var workletCode = (".*");\s*$/m)?.[1]; + const code = literal ? JSON.parse(literal) : undefined; + if (!code?.includes('registerProcessor(') || !src.includes(DFN_BLOB_LOAD)) { + throw new Error( + `[lotus-denoise] deepfilternet3-noise-filter changed shape (inline worklet / blob loader not found in ${esmPath}) — build aborted; update dfnWorkletToFile in vite.config.js.`, + ); + } + fs.writeFileSync(path.join(path.dirname(esmPath), DFN_WORKLET_FILE), code); + fs.writeFileSync( + esmPath, + src.replace( + DFN_BLOB_LOAD, + ` // [lotus] Loaded from a file, not a blob: URL (CSP script-src 'self').\n await audioContext.audioWorklet.addModule(new URL('./${DFN_WORKLET_FILE}', import.meta.url).href);`, + ), + ); +} + // Lotus ML noise suppression: ship the denoise worklets/wasm/models alongside // the vendored Element Call bundle. Runs after viteStaticCopy has populated // dist/public/element-call (closeBundle order). [Gitea #293] The dev server @@ -212,6 +242,7 @@ function lotusDenoise() { fs.mkdirSync(path.dirname(d), { recursive: true }); fs.copyFileSync(s, d); }); + dfnWorkletToFile(path.join(denoiseDir, 'deepfilternet/index.esm.js')); // [lotus] DENOISE CUTOVER: the getUserMedia shim is no longer injected. // Our forked Element Call now runs ML denoise in-source as a real LiveKit -- 2.47.3