diff --git a/src/server/services/obs/index.ts b/src/server/services/obs/index.ts index 45aefb8..34d8133 100644 --- a/src/server/services/obs/index.ts +++ b/src/server/services/obs/index.ts @@ -4,13 +4,13 @@ import { TextEvent, TextEventType, } from "@/types"; +import OBSWebSocket, { OBSWebSocketError } from "obs-websocket-js"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; import { serviceSubscibeToInput, serviceSubscibeToSource, } from "../../../utils"; -import OBSWebSocket from "obs-websocket-js"; -import { proxy } from "valtio"; -import { toast } from "react-toastify"; async function sleep(time: number) { return new Promise((res) => setTimeout(res, time)); @@ -18,6 +18,8 @@ async function sleep(time: number) { class Service_OBS implements IServiceInterface { private wsInstance!: OBSWebSocket; + private wsRequestCancelToken = false; + wsState = proxy({ status: ServiceNetworkState.disconnected, }); @@ -28,23 +30,12 @@ class Service_OBS implements IServiceInterface { async init() { this.wsInstance = new OBSWebSocket(); - this.wsInstance.on("ConnectionOpened", () => { - this.wsState.status = ServiceNetworkState.connected; - }); - this.wsInstance.on("ConnectionClosed", () => { - this.wsState.status = ServiceNetworkState.disconnected; - }); - + serviceSubscibeToSource(this.#state, "source", e => this.processTextEvent(e)); serviceSubscibeToInput(this.#state, "inputField", e => this.processTextEvent(e)); - if (this.#state.wsAutoStart) this.wsConnect(); - - } - sendTest(): void { - this.wsInstance.call("SendStreamCaption", { captionText: "Test" }); } private processTextEvent(data?: TextEvent) { @@ -57,7 +48,39 @@ class Service_OBS implements IServiceInterface { (data?.type === TextEventType.interim && this.#state.interim) ) ) { - this.wsInstance.call("SendStreamCaption", { captionText: data.value }); + this.wsInstance.call("SendStreamCaption", { captionText: data.value }).catch((e: OBSWebSocketError) => { + if (e.code !== 501) + this.toastError(e); + }); + } + } + + private toastError(e: OBSWebSocketError) { + const err = e.message ? "[OBS] " + e.message : "[OBS] Connection error"; + toast(err, { type: "error", autoClose: 2000 }); + } + + private wsHandleDisconnect(e: OBSWebSocketError) { + // auto reconnect + if (e.code === 1006) { + if (this.wsRequestCancelToken) { + this.wsRequestCancelToken = false; + this.wsState.status = ServiceNetworkState.disconnected; + } + else { + if (this.#state.wsAutoStart) + this.wsConnect(); + else { + this.wsState.status = ServiceNetworkState.disconnected; + this.toastError(e); + } + } + } + else { + this.wsState.status = ServiceNetworkState.disconnected; + if(e.code !== 1000) { // 1000 - graceful stop + this.toastError(e); + } } } @@ -66,17 +89,17 @@ class Service_OBS implements IServiceInterface { toast("[OBS] Invalid connection port", { type: "error", autoClose: false }); return; } + this.wsState.status = ServiceNetworkState.connecting; try { - await this.wsInstance.connect( - `ws://127.0.0.1:${this.#state.wsPort}`, - this.#state.wsPassword - ); - } catch (error: any) { - this.wsState.status = ServiceNetworkState.error; - const err = error.message ? "[OBS] " + error.message : "[OBS] Connection error"; - toast(err, { type: "error", autoClose: false }); - } finally { + this.wsInstance.disconnect(); + this.wsInstance.removeAllListeners("ConnectionClosed"); + await this.wsInstance.connect(`ws://127.0.0.1:${this.#state.wsPort}`,this.#state.wsPassword); + this.wsState.status = ServiceNetworkState.connected; + this.wsInstance.addListener("ConnectionClosed", e => this.wsHandleDisconnect(e)); + } catch (e: any) { + if (e instanceof OBSWebSocketError) + this.wsHandleDisconnect(e); } } @@ -84,6 +107,13 @@ class Service_OBS implements IServiceInterface { this.wsInstance.disconnect(); } + wsCancel() { + if (this.wsState.status === ServiceNetworkState.connecting) { + this.wsInstance.disconnect(); + this.wsRequestCancelToken = true; + } + } + async setupObsScene({ name, port, diff --git a/src/server/ui/inspector/inspector_obs.tsx b/src/server/ui/inspector/inspector_obs.tsx index 0b2b738..d5e41fb 100644 --- a/src/server/ui/inspector/inspector_obs.tsx +++ b/src/server/ui/inspector/inspector_obs.tsx @@ -56,6 +56,7 @@ const Inspector_OBS: FC = () => { } const handleStopWs = () => window.ApiServer.obs.wsDisconnect(); + const handleCancelWs = () => window.ApiServer.obs.wsCancel(); return OBS Studio @@ -75,7 +76,7 @@ const Inspector_OBS: FC = () => { {/* Websocket Plugin */} Native stream captions "obs-websocket" plugin required.
OBS 28.x should have it by default, just enable it!
- up("wsAutoStart", e)} label="Connect on start" /> + up("wsAutoStart", e)} label="Auto Connect" /> up("wsPort", e.target.value)} label="Websocket Port" /> up("wsPassword", e.target.value)} label="Websocket Password" /> @@ -85,12 +86,13 @@ const Inspector_OBS: FC = () => { up("interim", e)} label="Show interim" />