feat: 优化 AudioWorklet 加载策略,增加多种 URL 支持和详细错误日志
This commit is contained in:
@@ -484,25 +484,60 @@ export class S2sWsRealtimeClient extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioWorklet 加载(WebView2/打包兼容):
|
// AudioWorklet 加载(WebView2/打包兼容)——多策略 + 详细日志:
|
||||||
// 源码已由 Vite `?raw` 内联进 bundle,运行时直接从内存字符串建 Blob → addModule。
|
// 源码已由 Vite `?raw` 内联进 bundle。不同 WebView2 版本对
|
||||||
// 彻底摆脱运行时 fetch/网络/CORS/后端可达性依赖,任何 WebView2 都能加载。
|
// blob: / data: / http: 三种 URL 的 addModule 支持不一致,逐一尝试,
|
||||||
const addWorkletFromSrc = async (name, src) => {
|
// 并打印每次失败的真实 name/message,便于定位真正原因。
|
||||||
const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
|
// 策略1 内联源码 → Blob URL(无网络,多数环境最稳)
|
||||||
|
// 策略2 内联源码 → data: URL(部分 WebView2 拒绝 blob: 但接受 data:)
|
||||||
|
// 策略3 后端真实 HTTP(IP 可达 + CORS *,直连 addModule)
|
||||||
|
const attempts = [];
|
||||||
|
const tryModule = async (label, url) => {
|
||||||
try {
|
try {
|
||||||
await ctx.audioWorklet.addModule(url);
|
await ctx.audioWorklet.addModule(url);
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} catch (err) {
|
||||||
URL.revokeObjectURL(url);
|
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) => {
|
||||||
|
// 策略1:Blob 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;
|
||||||
|
|
||||||
|
// 策略2:data: URL(base64,UTF-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:后端 HTTP(IP 可达 + 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 =
|
const workletOk =
|
||||||
(await addWorkletFromSrc("mic-capture", micWorkletSrc)) &&
|
(await addWorkletFromSrc("mic-capture", micWorkletSrc)) &&
|
||||||
(await addWorkletFromSrc("audio-playback", playbackWorkletSrc));
|
(await addWorkletFromSrc("audio-playback", playbackWorkletSrc));
|
||||||
if (workletOk) {
|
if (workletOk) {
|
||||||
console.info("[ws] AudioWorklet 已通过内联源码 Blob URL 加载(WebView2 兼容模式)");
|
console.info("[ws] AudioWorklet 加载成功(WebView2 兼容模式)");
|
||||||
} else {
|
} else {
|
||||||
throw new Error("AudioWorklet 模块加载失败(内联源码 Blob addModule 失败)");
|
throw new Error(
|
||||||
|
`AudioWorklet 模块加载失败。尝试详情:${attempts.join(" | ") || "无"}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const captureNode = new AudioWorkletNode(ctx, "mic-capture", {
|
const captureNode = new AudioWorkletNode(ctx, "mic-capture", {
|
||||||
|
|||||||
Reference in New Issue
Block a user