feat: 优化 AudioWorklet 加载策略,增加多种 URL 支持和详细错误日志

This commit is contained in:
Pine
2026-08-18 08:41:30 +08:00
parent 94faf14f07
commit 71aa2bc3b6
+44 -9
View File
@@ -484,25 +484,60 @@ export class S2sWsRealtimeClient extends EventTarget {
}
}
// AudioWorklet 加载(WebView2/打包兼容):
// 源码已由 Vite `?raw` 内联进 bundle,运行时直接从内存字符串建 Blob → addModule。
// 彻底摆脱运行时 fetch/网络/CORS/后端可达性依赖,任何 WebView2 都能加载。
const addWorkletFromSrc = async (name, src) => {
const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
// AudioWorklet 加载(WebView2/打包兼容)——多策略 + 详细日志
// 源码已由 Vite `?raw` 内联进 bundle。不同 WebView2 版本对
// blob: / data: / http: 三种 URL 的 addModule 支持不一致,逐一尝试,
// 并打印每次失败的真实 name/message,便于定位真正原因。
// 策略1 内联源码 → Blob URL(无网络,多数环境最稳)
// 策略2 内联源码 → data: URL(部分 WebView2 拒绝 blob: 但接受 data:
// 策略3 后端真实 HTTPIP 可达 + CORS *,直连 addModule
const attempts = [];
const tryModule = async (label, url) => {
try {
await ctx.audioWorklet.addModule(url);
return true;
} finally {
URL.revokeObjectURL(url);
} catch (err) {
const detail = `${err?.name ?? ""}: ${err?.message ?? String(err)}`;
attempts.push(`${label} -> ${detail}`);
console.warn(`[ws] worklet addModule 失败(${label}:`, err);
return false;
}
};
const addWorkletFromSrc = async (name, src) => {
// 策略1Blob URL
const blobUrl = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
let ok = await tryModule(`${name}@blob`, blobUrl);
URL.revokeObjectURL(blobUrl);
if (ok) return true;
// 策略2data: URLbase64UTF-8 安全)
try {
const b64 = btoa(unescape(encodeURIComponent(src)));
ok = await tryModule(`${name}@data`, `data:text/javascript;base64,${b64}`);
if (ok) return true;
} catch (err) {
attempts.push(`${name}@data-encode -> ${err?.message ?? String(err)}`);
}
// 策略3:后端 HTTPIP 可达 + CORS *
const httpBase = window.VOICE_WORKLETS_BASE;
if (httpBase) {
ok = await tryModule(`${name}@http`, `${httpBase}${name}.js`);
if (ok) return true;
}
return false;
};
const workletOk =
(await addWorkletFromSrc("mic-capture", micWorkletSrc)) &&
(await addWorkletFromSrc("audio-playback", playbackWorkletSrc));
if (workletOk) {
console.info("[ws] AudioWorklet 已通过内联源码 Blob URL 加载(WebView2 兼容模式)");
console.info("[ws] AudioWorklet 加载成功WebView2 兼容模式)");
} else {
throw new Error("AudioWorklet 模块加载失败(内联源码 Blob addModule 失败)");
throw new Error(
`AudioWorklet 模块加载失败。尝试详情:${attempts.join(" | ") || "无"}`,
);
}
const captureNode = new AudioWorkletNode(ctx, "mic-capture", {