Compare commits

..

11 Commits

Author SHA1 Message Date
Janis
f296bba7d6 [fix] fixed a bug when connected twice to the same server 2023-06-28 23:32:18 +02:00
Janis
268252bf56 Update README.md 2023-06-28 19:19:16 +02:00
Janis
b7beddb32e [fix] bug while reconnecting server 2023-06-28 19:16:03 +02:00
Janis
92906d25c3 [feature] add key to div 2023-06-28 18:57:32 +02:00
Janis
1bd6e3400b [refactor] add setTimeout before reconnect 2023-06-28 18:48:19 +02:00
Janis
7d2409f6f4 Update README.md 2023-06-28 18:46:17 +02:00
Janis
b702b82cce [refactor] update readme 2023-06-28 18:43:49 +02:00
Janis
00b8db20c3 [feature] add remoteAppPort as query param 2023-06-28 18:33:57 +02:00
Janis
16573394b1 Update README.md 2023-06-28 18:23:09 +02:00
Janis
08fa223207 [feature] add content table 2023-06-28 18:01:52 +02:00
Janis
e2423a54cf [feature] add README.md 2023-06-28 18:01:24 +02:00
7 changed files with 129 additions and 14 deletions

58
README.md Normal file
View 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.
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/d0ab06f2-1a36-479d-826f-bd4bd3d405b7)
- [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)
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/04dc3a66-c493-429b-b4ae-44bade473ad6)
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"
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/b31bc553-fde2-46ab-b07c-d3c81339cc7d)
3. Add a new Browser Source to your OBS Scene
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/0198b468-bb96-4b65-bdd4-3d6bb3ef7d25)
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/58ad399f-5344-456f-b243-6e267b489fd5)
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)
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/5ad8ce69-645b-45e7-acc3-ce7ba8d7f8ab)
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)
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/40faa435-e128-415f-98eb-a9e8809e8f65)
### 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
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/87985b4c-4397-4681-9635-239d1e382c24)
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&parameter2=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
View File

@@ -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",

View File

@@ -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": {

View File

@@ -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[]

View File

@@ -72,7 +72,7 @@ export default function Viewer({
</div> </div>
); );
} else { } else {
return <></>; return <div key={Math.random()}></div>;
} }
})} })}
</div> </div>

View File

@@ -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>
); );

View File

@@ -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");
} }
setTimeout(() => {
this.reconnect(); 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);
} }
} }