Skip to content

Commit

Permalink
feat: add auto reconnect
Browse files Browse the repository at this point in the history
  • Loading branch information
mmpneo committed Apr 18, 2023
1 parent e0d0cc7 commit cc3c6ce
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 27 deletions.
80 changes: 55 additions & 25 deletions src/server/services/obs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ 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));
}

class Service_OBS implements IServiceInterface {
private wsInstance!: OBSWebSocket;
private wsRequestCancelToken = false;

wsState = proxy({
status: ServiceNetworkState.disconnected,
});
Expand All @@ -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) {
Expand All @@ -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);
}
}
}

Expand All @@ -66,24 +89,31 @@ 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);
}
}

wsDisconnect() {
this.wsInstance.disconnect();
}

wsCancel() {
if (this.wsState.status === ServiceNetworkState.connecting) {
this.wsInstance.disconnect();
this.wsRequestCancelToken = true;
}
}

async setupObsScene({
name,
port,
Expand Down
6 changes: 4 additions & 2 deletions src/server/ui/inspector/inspector_obs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const Inspector_OBS: FC = () => {
}

const handleStopWs = () => window.ApiServer.obs.wsDisconnect();
const handleCancelWs = () => window.ApiServer.obs.wsCancel();

return <Inspector.Body>
<Inspector.Header><SiObsstudio/> OBS Studio</Inspector.Header>
Expand All @@ -75,7 +76,7 @@ const Inspector_OBS: FC = () => {
{/* <Inspector.SubHeader>Websocket Plugin</Inspector.SubHeader> */}
<Inspector.SubHeader>Native stream captions</Inspector.SubHeader>
<Inspector.Description><span><span className="font-medium">"obs-websocket"</span> plugin required. <br /> OBS 28.x should have it by default, just enable it!</span></Inspector.Description>
<InputCheckbox value={data.wsAutoStart} onChange={e => up("wsAutoStart", e)} label="Connect on start" />
<InputCheckbox value={data.wsAutoStart} onChange={e => up("wsAutoStart", e)} label="Auto Connect" />
<InputNetworkStatus label="OBS connection" value={wsState.status} />
<InputText type="number" value={data.wsPort} onChange={e => up("wsPort", e.target.value)} label="Websocket Port" />
<InputText type="password" value={data.wsPassword} onChange={e => up("wsPassword", e.target.value)} label="Websocket Password" />
Expand All @@ -85,12 +86,13 @@ const Inspector_OBS: FC = () => {
<InputCheckbox value={data.interim} onChange={e => up("interim", e)} label="Show interim" />
<ServiceButton
showError
errorLabel="Error. Try recconect"
errorLabel="Error - Try Again"
stopLabel="Disconnect"
startLabel="Connect"
onError={handleStartWs}
status={wsState.status}
onStart={handleStartWs}
onPending={handleCancelWs}
onStop={handleStopWs}/>
</Inspector.Content>
</Inspector.Body>
Expand Down

0 comments on commit cc3c6ce

Please sign in to comment.