diff --git a/src/main/resources/static/js/broadcast/renderer.js b/src/main/resources/static/js/broadcast/renderer.js index 27507bd..8500c46 100644 --- a/src/main/resources/static/js/broadcast/renderer.js +++ b/src/main/resources/static/js/broadcast/renderer.js @@ -20,6 +20,7 @@ export class BroadcastRenderer { this.renderIntervalId = null; this.scriptWorker = null; this.scriptWorkerReady = false; + this.scriptErrorKeys = new Set(); this.obsBrowser = !!globalThis.obsstudio; this.supportsAnimatedDecode = @@ -388,6 +389,7 @@ export class BroadcastRenderer { } const offscreen = this.scriptCanvas.transferControlToOffscreen(); this.scriptWorker = new Worker("/js/broadcast/script-worker.js"); + this.scriptWorker.addEventListener("message", (event) => this.handleScriptWorkerMessage(event)); this.scriptWorker.postMessage( { type: "init", @@ -416,6 +418,24 @@ export class BroadcastRenderer { }); } + handleScriptWorkerMessage(event) { + const { type, payload } = event.data || {}; + if (type !== "scriptError" || !payload?.id) { + return; + } + const key = `${payload.id}:${payload.stage || "unknown"}`; + if (this.scriptErrorKeys.has(key)) { + return; + } + this.scriptErrorKeys.add(key); + const details = payload.message || "Unknown error"; + if (this.showToast) { + this.showToast(`Script ${payload.id} ${payload.stage || "error"}: ${details}`, "error"); + } else { + console.error(`Script ${payload.id} ${payload.stage || "error"}`, payload); + } + } + async spawnUserJavaScriptWorker(asset) { if (!asset?.id || !asset?.url) { return; diff --git a/src/main/resources/static/js/broadcast/script-worker.js b/src/main/resources/static/js/broadcast/script-worker.js index e9b83e0..3f4c9c5 100644 --- a/src/main/resources/static/js/broadcast/script-worker.js +++ b/src/main/resources/static/js/broadcast/script-worker.js @@ -5,6 +5,27 @@ let channelName = ""; let tickIntervalId = null; let lastTick = 0; let startTime = 0; +const errorKeys = new Set(); + +function reportScriptError(id, stage, error) { + if (!id) { + return; + } + const key = `${id}:${stage}:${error?.message ?? error}`; + if (errorKeys.has(key)) { + return; + } + errorKeys.add(key); + self.postMessage({ + type: "scriptError", + payload: { + id, + stage, + message: error?.message ?? String(error), + stack: error?.stack || "", + }, + }); +} function updateScriptContexts() { scripts.forEach((script) => { @@ -119,6 +140,7 @@ self.addEventListener("message", (event) => { handlers = createScriptHandlers(payload.source, context, state); } catch (error) { console.error(`Script ${payload.id} failed to initialize`, error); + reportScriptError(payload.id, "initialize", error); return; } const script = { @@ -134,6 +156,7 @@ self.addEventListener("message", (event) => { script.init(script.context, script.state); } catch (error) { console.error(`Script ${payload.id} init failed`, error); + reportScriptError(payload.id, "init", error); } } ensureTickLoop();