mirror of
https://github.com/DerTyp7/teamspeak-obs-overlay.git
synced 2026-07-31 08:39:04 +02:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f296bba7d6 | ||
|
|
268252bf56 | ||
|
|
b7beddb32e | ||
|
|
92906d25c3 | ||
|
|
1bd6e3400b | ||
|
|
7d2409f6f4 | ||
|
|
b702b82cce | ||
|
|
00b8db20c3 | ||
|
|
16573394b1 | ||
|
|
08fa223207 | ||
|
|
e2423a54cf |
58
README.md
Normal file
58
README.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Teamspeak5-OBS-Overlay
|
||||||
|
|
||||||
|
This is a overlay for OBS to show the current talking clients in your Teamspeak 5 Channel.
|
||||||
|
This App uses the new "Remote Apps" feature of Teamspeak 5.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- [Teamspeak5-OBS-Overlay](#teamspeak5-obs-overlay)
|
||||||
|
- [Setup](#setup)
|
||||||
|
- [Customization (Parameters)](#customization-parameters)
|
||||||
|
- [Setup (Developer)](#setup-developer)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Download the `index.html` of the latest release from [here](https://github.com/DerTyp876/ts5-obs-overlay/releases/tag/v1.0.0)
|
||||||
|

|
||||||
|
1.1. (optional) You can rename the file. Just remember using the new file name in the future instead of `index.html`
|
||||||
|
2. Go into the Teamspeak 5 Settings and enable "Remote Apps"
|
||||||
|

|
||||||
|
|
||||||
|
3. Add a new Browser Source to your OBS Scene
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
4. Tick the checkbox "Local File" and select the downloaded `index.html`
|
||||||
|
4.1. Set the width and height to your desired size (e.g. 1920x1080 OR 1280x720)
|
||||||
|

|
||||||
|
|
||||||
|
5. You should now receive a notification in Teamspeak 5 that the app is allowed to connect to your Teamspeak 5 client. Allow it. (If you don't get a notification, restart Teamspeak 5 and OBS -> try again)
|
||||||
|

|
||||||
|
|
||||||
|
### Customization (Parameters)
|
||||||
|
|
||||||
|
You can customize the overlay by adding parameters to the URL of the Browser Source.
|
||||||
|
|
||||||
|
1. Open your Browser Source settings
|
||||||
|
2. **Untick** the checkbox "Local File"
|
||||||
|
3. Add `file://` to the beginning of the URL
|
||||||
|

|
||||||
|
4. Start adding parameters like discribed below
|
||||||
|
|
||||||
|
Start by adding a `?` to the end of the URL and then add the parameters.
|
||||||
|
To add multiple parameters, you have to seperate them with a `&`.
|
||||||
|
|
||||||
|
Like this: `file://C:/Users/.../index.html?parameter1=value1¶meter2=value2`
|
||||||
|
Real example: `file://C:/Users/.../index.html?remoteAppPort=5899`
|
||||||
|
|
||||||
|
This is a list of all available parameters (all parameters are optional):
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
| --------------- | -------------------------------------- | ------- |
|
||||||
|
| `remoteAppPort` | The port of the Teamspeak 5 remote app | `5899` |
|
||||||
|
|
||||||
|
## Setup (Developer)
|
||||||
|
|
||||||
|
1. Clone this repository
|
||||||
|
2. Run `npm install`
|
||||||
|
3. To start the development server run `npm run dev`
|
||||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.14.0",
|
||||||
"sass": "^1.62.1"
|
"sass": "^1.62.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -502,6 +503,14 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@remix-run/router": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.3.62",
|
"version": "1.3.62",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.62.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.62.tgz",
|
||||||
@@ -2171,6 +2180,36 @@
|
|||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "6.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.0.tgz",
|
||||||
|
"integrity": "sha512-OD+vkrcGbvlwkspUFDgMzsu1RXwdjNh83YgG/28lBnDzgslhCgxIqoExLlxsfTpIygp7fc+Hd3esloNwzkm2xA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "6.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.0.tgz",
|
||||||
|
"integrity": "sha512-YEwlApKwzMMMbGbhh+Q7MsloTldcwMgHxUY/1g0uA62+B1hZo2jsybCWIDCL8zvIDB1FA0pBKY9chHbZHt+2dQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.7.0",
|
||||||
|
"react-router": "6.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.14.0",
|
||||||
"sass": "^1.62.1"
|
"sass": "^1.62.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import { TS5Connection } from "./teamspeak5Handler";
|
|||||||
import { IChannel, IClient, IConnection } from "interfaces/teamspeak";
|
import { IChannel, IClient, IConnection } from "interfaces/teamspeak";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Viewer from "./Viewer";
|
import Viewer from "./Viewer";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const [clients, setClients] = useState<IClient[]>([]);
|
const [clients, setClients] = useState<IClient[]>([]);
|
||||||
const [channels, setChannels] = useState<IChannel[]>([]);
|
const [channels, setChannels] = useState<IChannel[]>([]);
|
||||||
const [connections, setConnections] = useState<IConnection[]>([]);
|
const [connections, setConnections] = useState<IConnection[]>([]);
|
||||||
@@ -33,8 +36,10 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const remoteAppPort = searchParams.get("remoteAppPort");
|
||||||
|
|
||||||
const tsConnection: TS5Connection = new TS5Connection(
|
const tsConnection: TS5Connection = new TS5Connection(
|
||||||
5899,
|
parseInt(remoteAppPort ?? "5899"),
|
||||||
setConnections,
|
setConnections,
|
||||||
setChannels,
|
setChannels,
|
||||||
setClients,
|
setClients,
|
||||||
@@ -52,7 +57,7 @@ export default function App() {
|
|||||||
<Viewer
|
<Viewer
|
||||||
clients={
|
clients={
|
||||||
clients.map((client) => {
|
clients.map((client) => {
|
||||||
if (client.channel?.id === currentChannel?.id) {
|
if (client.channel?.id === currentChannel?.id && client.channel.connection.id === activeConnectionId) {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
}) as IClient[]
|
}) as IClient[]
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default function Viewer({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <></>;
|
return <div key={Math.random()}></div>;
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "@styles/index.scss";
|
import "@styles/index.scss";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<BrowserRouter>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class TS5Connection {
|
|||||||
setConnections: React.Dispatch<React.SetStateAction<IConnection[]>>,
|
setConnections: React.Dispatch<React.SetStateAction<IConnection[]>>,
|
||||||
setChannels: React.Dispatch<React.SetStateAction<IChannel[]>>,
|
setChannels: React.Dispatch<React.SetStateAction<IChannel[]>>,
|
||||||
setClients: React.Dispatch<React.SetStateAction<IClient[]>>,
|
setClients: React.Dispatch<React.SetStateAction<IClient[]>>,
|
||||||
setActiveConnectionId: React.Dispatch<React.SetStateAction<number>>,
|
setActiveConnectionStateId: React.Dispatch<React.SetStateAction<number>>,
|
||||||
) {
|
) {
|
||||||
// Create websocket connection to TS5 client
|
// Create websocket connection to TS5 client
|
||||||
this.remoteAppPort = remoteAppPort;
|
this.remoteAppPort = remoteAppPort;
|
||||||
@@ -26,10 +26,11 @@ export class TS5Connection {
|
|||||||
|
|
||||||
// Create dataHandler and messageHandler
|
// Create dataHandler and messageHandler
|
||||||
this.dataHandler = new TS5DataHandler(setConnections, setChannels, setClients);
|
this.dataHandler = new TS5DataHandler(setConnections, setChannels, setClients);
|
||||||
this.messageHandler = new TS5MessageHandler(this.ws, this.dataHandler, setActiveConnectionId);
|
this.messageHandler = new TS5MessageHandler(this.ws, this.dataHandler, setActiveConnectionStateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnect() {
|
reconnect() {
|
||||||
|
console.log("Reconnecting...")
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
|
|
||||||
this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);
|
this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);
|
||||||
@@ -74,7 +75,9 @@ export class TS5Connection {
|
|||||||
localStorage.removeItem("apiKey");
|
localStorage.removeItem("apiKey");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reconnect();
|
setTimeout(() => {
|
||||||
|
this.reconnect();
|
||||||
|
}, 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle messages received from TS5 client
|
// Handle messages received from TS5 client
|
||||||
@@ -325,12 +328,18 @@ class TS5MessageHandler {
|
|||||||
ws: WebSocket;
|
ws: WebSocket;
|
||||||
dataHandler: TS5DataHandler;
|
dataHandler: TS5DataHandler;
|
||||||
|
|
||||||
setActiveConnectionId: React.Dispatch<React.SetStateAction<number>>;
|
setActiveConnectionStateId: React.Dispatch<React.SetStateAction<number>>;
|
||||||
|
activeConnectionId = 0;
|
||||||
|
|
||||||
constructor(ws: WebSocket, dataHandler: TS5DataHandler, setActiveConnectionId: React.Dispatch<React.SetStateAction<number>>) {
|
constructor(ws: WebSocket, dataHandler: TS5DataHandler, setActiveConnectionStateId: React.Dispatch<React.SetStateAction<number>>) {
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
this.dataHandler = dataHandler;
|
this.dataHandler = dataHandler;
|
||||||
this.setActiveConnectionId = setActiveConnectionId;
|
this.setActiveConnectionStateId = setActiveConnectionStateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveConnection(connectionId: number) {
|
||||||
|
this.activeConnectionId = connectionId;
|
||||||
|
this.setActiveConnectionStateId(connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseChannelInfos(channelInfos: IChannelInfos, connection: IConnection) {
|
parseChannelInfos(channelInfos: IChannelInfos, connection: IConnection) {
|
||||||
@@ -473,8 +482,10 @@ class TS5MessageHandler {
|
|||||||
handleClientSelfPropertyUpdatedMessage(data: IClientSelfPropertyUpdatedMessage) {
|
handleClientSelfPropertyUpdatedMessage(data: IClientSelfPropertyUpdatedMessage) {
|
||||||
console.log("handleClientSelfPropertyUpdated", data);
|
console.log("handleClientSelfPropertyUpdated", data);
|
||||||
|
|
||||||
if (data.payload.flag == "inputHardware") { // sadly thats the only way to detect if a server is active or not
|
const connection: IConnection | undefined = this.dataHandler.getConnectionById(this.activeConnectionId);
|
||||||
this.setActiveConnectionId(data.payload.connectionId);
|
|
||||||
|
if (data.payload.flag == "inputHardware" || connection == undefined) { // sadly thats the only way to detect if a server is active or not
|
||||||
|
this.setActiveConnection(data.payload.connectionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user