feat: 优化 AudioWorklet 加载逻辑,增加 Blob URL 兼容性和多基址回退方案

This commit is contained in:
Pine
2026-08-18 08:06:12 +08:00
parent 6f8ccf1176
commit 49c0975fdc
+34 -6
View File
@@ -478,11 +478,11 @@ export class S2sWsRealtimeClient extends EventTarget {
}
}
// The worklets live at the repo root, one level up from this module.
// ?v=2 busts the browser cache so the fixed (unmuted) playback worklet
// always loads instead of a stale muted copy.
// 多基址回退:打包环境优先后端 httptauri.localhost 无法加载 worklet 模块),
// 浏览器开发回退页面源 / 相对路径
// AudioWorklet 加载(WebView2/打包兼容):
// 1) 先直接 addModule(http 地址) —— 浏览器/常规环境快路径
// 2) 失败则 fetch 源码 → Blob URL → addModule —— 打包 WebView2 下
// tauri.localhost 自定义协议与跨域均受限,Blob 同源最稳
// 3) 多个候选基址(后端 http / 页面源 / 相对路径
const candidates = [
window.VOICE_WORKLETS_BASE,
`${window.location.origin}/voice-worklets/`,
@@ -490,6 +490,27 @@ export class S2sWsRealtimeClient extends EventTarget {
].filter(Boolean);
let workletErr = null;
let workletOk = false;
const addViaBlob = async (name) => {
for (const base of candidates) {
try {
const res = await fetch(base + name, { cache: "no-store" });
if (!res.ok) continue;
const src = await res.text();
const url = URL.createObjectURL(new Blob([src], { type: "application/javascript" }));
try {
await ctx.audioWorklet.addModule(url);
return true;
} finally {
URL.revokeObjectURL(url);
}
} catch (err) {
workletErr = err;
}
}
return false;
};
for (const base of candidates) {
try {
await ctx.audioWorklet.addModule(base + "mic-capture.js?v=3");
@@ -498,9 +519,16 @@ export class S2sWsRealtimeClient extends EventTarget {
break;
} catch (err) {
workletErr = err;
console.warn("[ws] worklet 加载失败,尝试备用地址:", base, err);
console.warn("[ws] worklet 直接加载失败,改走 Blob 方案:", base, err);
}
}
if (!workletOk) {
// 回退:fetch → Blob → addModuleWebView2 打包环境最稳)
const micOk = await addViaBlob("mic-capture.js?v=3");
const playOk = await addViaBlob("audio-playback.js?v=3");
workletOk = micOk && playOk;
if (workletOk) console.info("[ws] AudioWorklet 已通过 Blob URL 加载(WebView2 兼容模式)");
}
if (!workletOk) throw workletErr || new Error("AudioWorklet 模块加载失败");
const captureNode = new AudioWorkletNode(ctx, "mic-capture", {