Compare commits

...

35 Commits

Author SHA1 Message Date
Janis
43dc2f1160 update build options 2023-07-08 14:17:36 +02:00
Janis
810da5173a Merge pull request #31 from DerTyp7/dependabot/npm_and_yarn/eslint-plugin-react-refresh-0.4.1
Bump eslint-plugin-react-refresh from 0.3.5 to 0.4.1
2023-07-08 13:58:12 +02:00
Janis
541514df9a Merge pull request #28 from DerTyp7/dependabot/npm_and_yarn/sass-1.63.6
Bump sass from 1.62.1 to 1.63.6
2023-07-08 13:57:59 +02:00
dependabot[bot]
a3335cb44d Bump sass from 1.62.1 to 1.63.6
Bumps [sass](https://github.com/sass/dart-sass) from 1.62.1 to 1.63.6.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.62.1...1.63.6)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 11:56:56 +00:00
Janis
9cdba50155 Merge pull request #30 from DerTyp7/dependabot/npm_and_yarn/jest-29.6.1
Bump jest from 29.5.0 to 29.6.1
2023-07-08 13:56:34 +02:00
dependabot[bot]
ad94353c64 Bump jest from 29.5.0 to 29.6.1
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.5.0 to 29.6.1.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.6.1/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 11:56:23 +00:00
Janis
e18420540f Merge pull request #29 from DerTyp7/dependabot/npm_and_yarn/react-router-dom-6.14.1
Bump react-router-dom from 6.14.0 to 6.14.1
2023-07-08 13:56:17 +02:00
Janis
87d8f628c2 Merge pull request #27 from DerTyp7:dependabot/npm_and_yarn/types/react-18.2.14
Bump @types/react from 18.2.8 to 18.2.14
2023-07-08 13:55:31 +02:00
dependabot[bot]
c962aae1a7 Bump eslint-plugin-react-refresh from 0.3.5 to 0.4.1
Bumps [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) from 0.3.5 to 0.4.1.
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.3.5...v0.4.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-react-refresh
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 11:52:10 +00:00
dependabot[bot]
9ec6d0bc1b Bump react-router-dom from 6.14.0 to 6.14.1
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.14.0 to 6.14.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.14.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 11:51:43 +00:00
dependabot[bot]
db1b7153f9 Bump @types/react from 18.2.8 to 18.2.14
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.8 to 18.2.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 11:51:20 +00:00
Janis
7da123fb42 Update dependabot.yml package-ecosystem 2023-07-08 13:50:53 +02:00
Janis
e49791cb1e Create dependabot.yml 2023-07-08 13:47:54 +02:00
Janis
e4d6d59d5c Update README.md 2023-07-01 21:33:37 +02:00
Janis
df0e38242c Update bug_report.md 2023-07-01 21:14:56 +02:00
Janis
3864207b6a Update issue templates 2023-07-01 21:14:14 +02:00
Janis
6524b85142 Update README.md 2023-07-01 13:31:13 +02:00
Janis
4677e98bf3 Merge pull request #25 from DerTyp876/19-show-if-a-client-is-hardware-muted
[feature] add hardware muted inputs
2023-06-30 13:59:12 +02:00
Janis Meister
5f81bbf46a [feature] add hardware muted inputs 2023-06-30 13:58:08 +02:00
Janis
3733f7ef79 Merge pull request #24 from DerTyp876/5-show-channel-name
5 show channel name
2023-06-30 13:27:22 +02:00
Janis Meister
14db96d10a [refactor] update readme paramter table 2023-06-30 13:26:58 +02:00
Janis Meister
0d3317802c [refactor] update channel name style 2023-06-30 13:25:44 +02:00
Janis Meister
113fcb28de [feature] get boolean from parameter 2023-06-30 13:22:07 +02:00
Janis
9758520239 Merge pull request #23 from DerTyp876/6-set-a-show-clients-limit
6 set a show clients limit
2023-06-30 13:20:20 +02:00
Janis Meister
d6e0517ddb [refactor] update readme paramter table 2023-06-30 13:19:55 +02:00
Janis Meister
f919b39548 [feature] add client limit 2023-06-30 13:18:37 +02:00
Janis
4ec685bd75 Merge pull request #22 from DerTyp876/16-hide-non-talking-clients
16 hide non talking clients
2023-06-30 13:06:11 +02:00
Janis Meister
7c3b67de4d [feature] update readme parameter table 2023-06-30 13:05:35 +02:00
Janis Meister
c1c9db8f95 [feature] add hideNonTalking boolean 2023-06-30 13:05:24 +02:00
Janis
ba5930cadc [feature] add log tag to logger.log 2023-06-29 21:49:33 +02:00
Janis
afe2376ea0 Merge pull request #21 from DerTyp876/feature_better-logger
Better logger
2023-06-29 21:26:11 +02:00
Janis
f0290bb44d [feature] integrated Logger 2023-06-29 21:17:31 +02:00
Janis
e411c1c126 [feature] integrated Logger 2023-06-29 21:17:30 +02:00
Janis
033cd77e04 [feature] update logger parameters 2023-06-29 21:07:02 +02:00
Janis
619e66375f [feature] created Logger 2023-06-29 18:09:03 +02:00
13 changed files with 601 additions and 464 deletions

23
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,23 @@
---
name: Bug report
about: Something isn't working for you
title: ''
labels: bug
assignees: ''
---
> Screenshots are welcome
## Describe the bug
A clear and concise description of what the bug is.
## To Reproduce
Example or explanation to reproduce the issue.
## Expected behavior
A clear and concise description of what you expected to happen.
**Your environment:**
- OS: [e.g. Windows 11]
- OBS version: [e.g. 29.1.3]
- Overlay version [e.g. v1.1.0]

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

View File

@@ -3,7 +3,7 @@
This is a overlay for OBS to show the current talking clients in your Teamspeak 5 Channel. 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. 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) ![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/d0ab06f2-1a36-479d-826f-bd4bd3d405b7)
- [Teamspeak5-OBS-Overlay](#teamspeak5-obs-overlay) - [Teamspeak5-OBS-Overlay](#teamspeak5-obs-overlay)
- [Setup](#setup) - [Setup](#setup)
@@ -12,44 +12,47 @@ This App uses the new "Remote Apps" feature of Teamspeak 5.
## Setup ## 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. Download the `index.html` of the latest release from [here](https://github.com/DerTyp7/ts5-obs-overlay/releases/latest)
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/04dc3a66-c493-429b-b4ae-44bade473ad6) ![image](https://github.com/DerTyp7/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` 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" 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) ![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/b31bc553-fde2-46ab-b07c-d3c81339cc7d)
3. Add a new Browser Source to your OBS Scene 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/DerTyp7/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) ![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/58ad399f-5344-456f-b243-6e267b489fd5)
4. Tick the checkbox "Local File" and select the downloaded `index.html` 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) 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) ![image](https://github.com/DerTyp7/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) 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) ![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/40faa435-e128-415f-98eb-a9e8809e8f65)
### Customization (Parameters) ### Customization (Parameters)
You can customize the overlay by adding parameters to the URL of the Browser Source. You can customize the overlay by adding parameters to the URL of the Browser Source.
1. Open your Browser Source settings 1. Open your Browser Source settings
2. **Untick** the checkbox "Local File" 2. **Untick** the checkbox "Local File"
3. Add `file://` to the beginning of the URL 3. Add `file://` to the beginning of the URL
![image](https://github.com/DerTyp876/ts5-obs-overlay/assets/76851529/87985b4c-4397-4681-9635-239d1e382c24) ![image](https://github.com/DerTyp7/ts5-obs-overlay/assets/76851529/87985b4c-4397-4681-9635-239d1e382c24)
4. Start adding parameters like discribed below 4. Start adding parameters like discribed below
Start by adding a `?` to the end of the URL and then add the parameters. 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 `&`. To add multiple parameters, you have to seperate them with a `&`.
Like this: `file://C:/Users/.../index.html?parameter1=value1&parameter2=value2` Like this: `file://C:/Users/.../index.html?parameter1=value1&parameter2=value2`
Real example: `file://C:/Users/.../index.html?remoteAppPort=5899` Real example: `file://C:/Users/.../index.html?remoteAppPort=5899&hideNonTalking=true&clientLimit=5`
This is a list of all available parameters (all parameters are optional): This is a list of all available parameters (all parameters are optional):
| Parameter | Description | Default | | Parameter | Description | Type | Default |
| --------------- | -------------------------------------- | ------- | | ----------------- | ---------------------------------------- | ------- | --------------- |
| `remoteAppPort` | The port of the Teamspeak 5 remote app | `5899` | | `remoteAppPort` | The port of the Teamspeak 5 remote app | number | `5899` |
| `hideNonTalking` | Hide all non-talking clients | boolean | `false` |
| `clientLimit` | Count of how many client should be shown | number | `0` (unlimited) |
| `showChannelName` | Display the channel name | boolean | `false` |
## Setup (Developer) ## Setup (Developer)

742
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,22 +11,22 @@
}, },
"dependencies": { "dependencies": {
"@types/node": "^20.3.2", "@types/node": "^20.3.2",
"jest": "^29.5.0", "jest": "^29.6.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.14.0", "react-router-dom": "^6.14.1",
"sass": "^1.62.1" "sass": "^1.63.6"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/react": "^18.0.37", "@types/react": "^18.2.14",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0", "@typescript-eslint/parser": "^5.59.0",
"@vitejs/plugin-react-swc": "^3.0.0", "@vitejs/plugin-react-swc": "^3.0.0",
"eslint": "^8.38.0", "eslint": "^8.38.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4", "eslint-plugin-react-refresh": "^0.4.1",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.3.9", "vite": "^4.3.9",
"vite-plugin-singlefile": "^0.13.5" "vite-plugin-singlefile": "^0.13.5"

View File

@@ -38,6 +38,7 @@ export default function App() {
useEffect(() => { useEffect(() => {
const remoteAppPort = searchParams.get("remoteAppPort"); const remoteAppPort = searchParams.get("remoteAppPort");
console.log(searchParams.get("hideNonTalking"));
const tsConnection: ITS5ConnectionHandler = new TS5ConnectionHandler( const tsConnection: ITS5ConnectionHandler = new TS5ConnectionHandler(
parseInt(remoteAppPort ?? "5899"), parseInt(remoteAppPort ?? "5899"),
setConnections, setConnections,
@@ -55,6 +56,9 @@ export default function App() {
return ( return (
<div className="App"> <div className="App">
<Viewer <Viewer
showChannelName={searchParams.get("showChannelName") === "true"}
hideNonTalking={searchParams.get("hideNonTalking") === "true"}
clientLimit={searchParams.get("clientLimit") ? parseInt(searchParams.get("clientLimit") ?? "0") : 0}
clients={ clients={
clients.map((client) => { clients.map((client) => {
if (client.channel?.id === currentChannel?.id && client.channel.connection.id === activeConnectionId) { if (client.channel?.id === currentChannel?.id && client.channel.connection.id === activeConnectionId) {

View File

@@ -5,19 +5,87 @@ export default function Viewer({
clients, clients,
channel, channel,
showChannelName = false, showChannelName = false,
hideNonTalking = false,
clientLimit = 0,
}: { }: {
clients: IClient[] | undefined; clients: IClient[] | undefined;
channel: IChannel | undefined; channel: IChannel | undefined;
showChannelName?: boolean; showChannelName?: boolean;
hideNonTalking?: boolean;
clientLimit?: number;
}) { }) {
return ( return (
<div className="viewer"> <div className="viewer">
{showChannelName ? <h3>{channel?.properties.name}</h3> : null} {showChannelName ? (
{clients?.map((client) => { <div className="channelNameContainer">
<h3>{channel?.properties.name}</h3>
</div>
) : null}
{clients?.map((client, i) => {
//* Client limit
if (clientLimit != 0 && i >= clientLimit) {
return null;
}
if (client) { if (client) {
//* Non-talking client
if (
hideNonTalking &&
(client.properties.inputMuted || client.properties.outputMuted || client.talkStatus == 0)
) {
return null;
}
//* Normal client
return ( return (
<div className="client" key={`${client.id}-${client.channel?.connection.id}`}> <div className="client" key={`${client.id}-${client.channel?.connection.id}`}>
{client.properties.outputMuted ? ( {client.properties.outputHardware == false ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<title>muted_hardware_output</title>
<g id="muted_hardware_output.svg">
<path
d="M116.62,39a4.78,4.78,0,0,1-1.59.3l-6.29,3.63a45.42,45.42,0,0,1-13.33,57.64,49.4,49.4,0,0,1-5.82,3.62c-1.06.57-2.2.92-3.19,1.49-1.7,1-2.77,2.13-2.77,4.18a4.57,4.57,0,0,0,4.54,4.54,5.33,5.33,0,0,0,1.84-.35,54.49,54.49,0,0,0,26.94-75c-.12,0-.22,0-.34,0M88.18,13.58a4.57,4.57,0,0,0-4.54,4.54c0,2.06,1.06,3.19,2.77,4.18,1,.57,2.13.92,3.19,1.49a49.4,49.4,0,0,1,5.82,3.62,45.68,45.68,0,0,1,8.19,7.78l7-4a2.63,2.63,0,0,1,1.11-.34A54.31,54.31,0,0,0,90,13.94a5.33,5.33,0,0,0-1.84-.35"
fill="#d8d8d8"
/>
<path
d="M59.46,71.4,32.77,86.81l19,19a4.51,4.51,0,0,0,3.19,1.35,4.57,4.57,0,0,0,4.54-4.54V71.4M54.92,20.88a4.51,4.51,0,0,0-3.19,1.35L28.12,45.85H9.54A4.57,4.57,0,0,0,5,50.38V77.62a4.57,4.57,0,0,0,4.54,4.54H22.26l37.2-21.48V25.42a4.57,4.57,0,0,0-4.54-4.54"
fill="#d8d8d8"
/>
<path
d="M85.11,56.6l-7.87,4.54A10,10,0,0,1,77.62,64c0,8.58-8.23,7.09-8.23,12.48A4.53,4.53,0,0,0,73.93,81a4,4,0,0,0,1.77-.35A18.13,18.13,0,0,0,86.69,64a18.34,18.34,0,0,0-1.59-7.4M73.93,47a4.52,4.52,0,0,0-4.54,4.54,3.92,3.92,0,0,0,1.08,2.8l8.76-5.06a16.14,16.14,0,0,0-3.52-1.93A4,4,0,0,0,73.93,47"
fill="#d8d8d8"
/>
<path
d="M100.87,47.49,93,52a27.15,27.15,0,0,1-8.36,33.87A36.79,36.79,0,0,1,79.25,89a4.54,4.54,0,0,0,1.84,8.72,5.24,5.24,0,0,0,1.77-.35,36.34,36.34,0,0,0,18-49.91M81,30.25A4.54,4.54,0,0,0,79.25,39a36.82,36.82,0,0,1,5.39,3.12,27,27,0,0,1,2.86,2.4l8.14-4.7A35.37,35.37,0,0,0,82.86,30.6,5.31,5.31,0,0,0,81,30.25"
fill="#d8d8d8"
/>
<path
d="M126.57,43.71,10.42,110.77a2.88,2.88,0,0,1-3.93-1.05L4.33,106A2.88,2.88,0,0,1,5.38,102L121.53,35A2.88,2.88,0,0,1,125.46,36l2.16,3.75A2.88,2.88,0,0,1,126.57,43.71Z"
fill="#c9070a"
/>
</g>
</svg>
) : client.properties.inputHardware == false ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<title>muted_hardware_input</title>
<g id="muted_hardware_input.svg">
<path
d="M88.62,54.15V64A24.69,24.69,0,0,1,64,88.62a25.26,25.26,0,0,1-8.38-1.46l-7.39,7.39A34,34,0,0,0,64,98.46,34.5,34.5,0,0,0,98.46,64V54.15a4.92,4.92,0,1,1,9.85,0V64a44.31,44.31,0,0,1-39.38,44v10.15H88.62a4.92,4.92,0,0,1,0,9.85H39.38a4.92,4.92,0,1,1,0-9.85H59.08V108A43.3,43.3,0,0,1,41,101.77L21.46,121.31a2.46,2.46,0,0,1-3.54,0L11.62,115a2.46,2.46,0,0,1,0-3.54l94.92-94.92a2.46,2.46,0,0,1,3.54,0l6.31,6.31a2.46,2.46,0,0,1,0,3.54ZM22.92,80.46A43.3,43.3,0,0,1,19.69,64V54.15a4.92,4.92,0,1,1,9.85,0V64a35.94,35.94,0,0,0,1.15,8.69ZM39.38,64V24.62a24.62,24.62,0,0,1,47.77-8.38Z"
fill="#d8d8d8"
/>
<rect
x="-5.93"
y="61.89"
width="139.87"
height="14.02"
rx="2.87"
ry="2.87"
transform="translate(-29.97 65.43) rotate(-45)"
fill="#c9070a"
/>
</g>
</svg>
) : client.properties.outputMuted ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<title>muted_output</title> <title>muted_output</title>
<g id="muted_output"> <g id="muted_output">

View File

@@ -1,6 +1,7 @@
import { IAuthSenderPayload, IChannel, IClient, IConnection, ITS5ConnectionHandler, ITS5DataHandler, ITS5MessageHandler } from "@interfaces/teamspeak"; import { IAuthSenderPayload, IChannel, IClient, IConnection, ITS5ConnectionHandler, ITS5DataHandler, ITS5MessageHandler } from "@interfaces/teamspeak";
import { TS5DataHandler } from "./dataHandler"; import { TS5DataHandler } from "./dataHandler";
import { TS5MessageHandler } from "./messageHandler"; import { TS5MessageHandler } from "./messageHandler";
import Logger from "@/utils/logger";
// Establish connection to TS5 client // Establish connection to TS5 client
@@ -22,6 +23,8 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
setClients: React.Dispatch<React.SetStateAction<IClient[]>>, setClients: React.Dispatch<React.SetStateAction<IClient[]>>,
setActiveConnectionStateId: 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;
this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`); this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);
@@ -32,7 +35,7 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
} }
reconnect() { reconnect() {
console.log("Reconnecting...") Logger.log("Reconnecting...")
this.ws.close(); this.ws.close();
this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`); this.ws = new WebSocket(`ws://localhost:${this.remoteAppPort}`);
@@ -44,8 +47,7 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
// Connect to TS5 client // Connect to TS5 client
connect() { connect() {
console.log('Connecting to TS5 client...'); Logger.log('Connecting to TS5 client...');
console.log(localStorage.getItem("apiKey"))
// Create authentication payload // Create authentication payload
const initalPayload: IAuthSenderPayload = { const initalPayload: IAuthSenderPayload = {
@@ -63,17 +65,17 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
this.ws.onopen = () => { this.ws.onopen = () => {
// Send authentication payload to TS5 client // Send authentication payload to TS5 client
console.log("Sending auth payload...")
this.ws.send(JSON.stringify(initalPayload)); this.ws.send(JSON.stringify(initalPayload));
Logger.wsSent(initalPayload);
}; };
this.ws.onclose = (event) => { this.ws.onclose = (event) => {
console.log("WebSocket connection closed", event); Logger.log("WebSocket connection closed", event);
// If the connection was closed before authentication, remove the API key from local storage // If the connection was closed before authentication, remove the API key from local storage
// OBS weirdly caches the localstorage and is very stubborn about clearing it (even when clicken "Clear Cache") // OBS weirdly caches the localstorage and is very stubborn about clearing it (even when clicken "Clear Cache")
if (!this.authenticated) { if (!this.authenticated) {
console.log("WebSocket connection closed before authentication"); Logger.log("WebSocket connection closed before authentication");
localStorage.removeItem("apiKey"); localStorage.removeItem("apiKey");
} }
@@ -87,7 +89,7 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
this.ws.onmessage = (event) => { this.ws.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log(data) Logger.wsReceived(data)
switch (data.type) { switch (data.type) {
case "auth": case "auth":
@@ -105,7 +107,6 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
break; break;
case "serverPropertiesUpdated": case "serverPropertiesUpdated":
this.messageHandler.handleServerPropertiesUpdatedMessage(data); this.messageHandler.handleServerPropertiesUpdatedMessage(data);
//this.ws.close();
break; break;
case "connectStatusChanged": case "connectStatusChanged":
this.messageHandler.handleConnectStatusChangedMessage(data); this.messageHandler.handleConnectStatusChangedMessage(data);
@@ -117,7 +118,7 @@ export class TS5ConnectionHandler implements ITS5ConnectionHandler {
this.messageHandler.handleChannelsMessage(data); this.messageHandler.handleChannelsMessage(data);
break; break;
default: default:
console.log(`No handler for event type: ${data.type}`); Logger.log(`No handler for event type: ${data.type}`);
break; break;
} }
}; };

View File

@@ -1,3 +1,4 @@
import Logger from "@/utils/logger";
import { IConnection, IChannel, IClient, ITS5DataHandler } from "@interfaces/teamspeak"; import { IConnection, IChannel, IClient, ITS5DataHandler } from "@interfaces/teamspeak";
@@ -58,88 +59,88 @@ export class TS5DataHandler implements ITS5DataHandler {
// Add data to local lists and update states // Add data to local lists and update states
addConnection(connection: IConnection) { addConnection(connection: IConnection) {
console.log("Adding connection...", connection) Logger.log("Adding connection...", connection)
const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id); const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);
if (existingConnection == undefined) { if (existingConnection == undefined) {
this.localConnections.push(connection); this.localConnections.push(connection);
this.updateConnectionsState(); this.updateConnectionsState();
console.log("Connection added") Logger.log("Connection added")
} else { } else {
console.log("Connection already exists") Logger.log("Connection already exists")
} }
} }
addChannel(channel: IChannel) { addChannel(channel: IChannel) {
console.log("Adding channel...", channel) Logger.log("Adding channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id); const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);
if (existingChannel == undefined) { if (existingChannel == undefined) {
this.localChannels.push(channel); this.localChannels.push(channel);
this.updateChannelsState(); this.updateChannelsState();
console.log("Channel added") Logger.log("Channel added")
} else { } else {
console.log("Channel already exists") Logger.log("Channel already exists")
} }
} }
addClient(client: IClient) { addClient(client: IClient) {
console.log("Adding client...", client) Logger.log("Adding client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id); const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);
if (existingClient == undefined) { if (existingClient == undefined) {
this.localClients.push(client); this.localClients.push(client);
this.updateClientsState(); this.updateClientsState();
console.log("Client added") Logger.log("Client added")
} else { } else {
console.log("Client already exists") Logger.log("Client already exists")
} }
} }
// Update data in local lists and update states // Update data in local lists and update states
updateConnection(connection: IConnection) { updateConnection(connection: IConnection) {
console.log("Updating connection...", connection) Logger.log("Updating connection...", connection)
const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id); const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);
if (existingConnection !== undefined) { if (existingConnection !== undefined) {
this.localConnections[this.localConnections.indexOf(existingConnection)] = connection; this.localConnections[this.localConnections.indexOf(existingConnection)] = connection;
this.updateConnectionsState(); this.updateConnectionsState();
console.log("Connection updated") Logger.log("Connection updated")
} else { } else {
console.log("Connection does not exist") Logger.log("Connection does not exist")
} }
} }
updateChannel(channel: IChannel) { updateChannel(channel: IChannel) {
console.log("Updating channel...", channel) Logger.log("Updating channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id); const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);
if (existingChannel !== undefined) { if (existingChannel !== undefined) {
this.localChannels[this.localChannels.indexOf(existingChannel)] = channel; this.localChannels[this.localChannels.indexOf(existingChannel)] = channel;
this.updateChannelsState(); this.updateChannelsState();
console.log("Channel updated") Logger.log("Channel updated")
} else { } else {
console.log("Channel does not exist") Logger.log("Channel does not exist")
} }
} }
updateClient(client: IClient) { updateClient(client: IClient) {
console.log("Updating client...", client) Logger.log("Updating client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id); const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);
if (existingClient !== undefined) { if (existingClient !== undefined) {
this.localClients[this.localClients.indexOf(existingClient)] = client; this.localClients[this.localClients.indexOf(existingClient)] = client;
this.updateClientsState(); this.updateClientsState();
console.log("Client updated") Logger.log("Client updated")
} else { } else {
console.log("Client does not exist") Logger.log("Client does not exist")
} }
} }
// Remove data from local lists and update states // Remove data from local lists and update states
removeConnection(connection: IConnection) { removeConnection(connection: IConnection) {
console.log("Removing connection...", connection) Logger.log("Removing connection...", connection)
const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id); const existingConnection: IConnection | undefined = this.localConnections.find((localConnection: IConnection) => localConnection.id === connection.id);
if (existingConnection !== undefined) { if (existingConnection !== undefined) {
@@ -152,14 +153,14 @@ export class TS5DataHandler implements ITS5DataHandler {
this.updateChannelsState(); this.updateChannelsState();
this.updateClientsState(); this.updateClientsState();
this.updateConnectionsState(); this.updateConnectionsState();
console.log("Connection removed") Logger.log("Connection removed")
} else { } else {
console.log("Connection does not exist") Logger.log("Connection does not exist")
} }
} }
removeChannel(channel: IChannel) { removeChannel(channel: IChannel) {
console.log("Removing channel...", channel) Logger.log("Removing channel...", channel)
const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id); const existingChannel: IChannel | undefined = this.localChannels.find((localChannel: IChannel) => localChannel.id === channel.id && localChannel.connection.id === channel.connection.id);
if (existingChannel !== undefined) { if (existingChannel !== undefined) {
@@ -170,22 +171,22 @@ export class TS5DataHandler implements ITS5DataHandler {
this.updateClientsState(); this.updateClientsState();
this.updateChannelsState(); this.updateChannelsState();
console.log("Channel removed") Logger.log("Channel removed")
} else { } else {
console.log("Channel does not exist") Logger.log("Channel does not exist")
} }
} }
removeClient(client: IClient) { removeClient(client: IClient) {
console.log("Removing client...", client) Logger.log("Removing client...", client)
const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id); const existingClient: IClient | undefined = this.localClients.find((localClient: IClient) => localClient.id === client.id && localClient.channel?.connection.id === client.channel?.connection.id);
if (existingClient !== undefined) { if (existingClient !== undefined) {
this.localClients.splice(this.localClients.indexOf(existingClient), 1); this.localClients.splice(this.localClients.indexOf(existingClient), 1);
this.updateClientsState(); this.updateClientsState();
console.log("Client removed") Logger.log("Client removed")
} else { } else {
console.log("Client does not exist") Logger.log("Client does not exist")
} }
} }

View File

@@ -1,3 +1,4 @@
import Logger from "@/utils/logger";
import { IChannelInfos, IConnection, IChannel, IAuthMessage, IClientInfo, IClientMovedMessage, IClient, IClientPropertiesUpdatedMessage, ITalkStatusChangedMessage, IClientSelfPropertyUpdatedMessage, IServerPropertiesUpdatedMessage, IConnectStatusChangedMessage, IChannelsMessage, ITS5MessageHandler, ITS5DataHandler } from "@interfaces/teamspeak"; import { IChannelInfos, IConnection, IChannel, IAuthMessage, IClientInfo, IClientMovedMessage, IClient, IClientPropertiesUpdatedMessage, ITalkStatusChangedMessage, IClientSelfPropertyUpdatedMessage, IServerPropertiesUpdatedMessage, IConnectStatusChangedMessage, IChannelsMessage, ITS5MessageHandler, ITS5DataHandler } from "@interfaces/teamspeak";
// Handle incoming messages from TS5 client // Handle incoming messages from TS5 client
@@ -36,8 +37,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
// This message is sent by the TS5 server when the client is connected // This message is sent by the TS5 server when the client is connected
// It contains the initial data // It contains the initial data
handleAuthMessage(data: IAuthMessage) { handleAuthMessage(data: IAuthMessage) {
console.log("handleAuthMessage", data);
localStorage.setItem("apiKey", data.payload.apiKey); localStorage.setItem("apiKey", data.payload.apiKey);
// Process auth payload and add initial data // Process auth payload and add initial data
@@ -67,8 +66,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
// This message is sent by the TS5 server when a client moves a channel OR joins/leaves the server // This message is sent by the TS5 server when a client moves a channel OR joins/leaves the server
handleClientMovedMessage(data: IClientMovedMessage) { handleClientMovedMessage(data: IClientMovedMessage) {
console.log("handleClientMoved", data);
const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId); const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId);
@@ -76,7 +73,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
if (+data.payload.oldChannelId == 0) { // Create new client(when connecting to server) if (+data.payload.oldChannelId == 0) { // Create new client(when connecting to server)
//set timout to wait for channels to be created //set timout to wait for channels to be created
setTimeout(() => { setTimeout(() => {
console.log("---> New Client created")
const newChannel = this.dataHandler.getChannelById(data.payload.newChannelId, data.payload.connectionId); const newChannel = this.dataHandler.getChannelById(data.payload.newChannelId, data.payload.connectionId);
if (newChannel !== undefined) { if (newChannel !== undefined) {
this.dataHandler.addClient( this.dataHandler.addClient(
@@ -86,25 +82,23 @@ export class TS5MessageHandler implements ITS5MessageHandler {
channel: newChannel, channel: newChannel,
properties: data.payload.properties, properties: data.payload.properties,
}); });
Logger.ts(`New Client found (${data.payload.connectionId} - ${data.payload.clientId} - ${data.payload.properties.nickname})`)
} }
}, 2000); }, 2000);
} else {//* This gets called when a client moves a channel OR joins/leaves the server } else {//* This gets called when a client moves a channel OR joins/leaves the server
const newChannel: IChannel | undefined = this.dataHandler.getChannelById(data.payload.newChannelId, data.payload.connectionId); const newChannel: IChannel | undefined = this.dataHandler.getChannelById(data.payload.newChannelId, data.payload.connectionId);
if (newChannel === undefined || newChannel.id === 0) { if (newChannel === undefined || newChannel.id === 0) {
console.log("---> Client left") Logger.ts(`Client left (${data.payload.connectionId} - ${data.payload.clientId} - ${data.payload.properties.nickname})`)
if (client !== undefined) { if (client !== undefined) {
this.dataHandler.removeClient(client); this.dataHandler.removeClient(client);
} }
return; return;
} }
if (client !== undefined) { // Client already exists if (client !== undefined) { // Client already exists
Logger.ts(`Client moved (${client.channel.connection.id} - ${client.id} - ${client.properties.nickname})`)
// Client moved
console.log("---> Client moved")
this.dataHandler.updateClient({ this.dataHandler.updateClient({
...client, ...client,
@@ -113,7 +107,8 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} else { // Client does not exist } else { // Client does not exist
// Client joined // Client joined
console.log("---> New Client joined") Logger.ts(`Client joined (${data.payload.connectionId} - ${data.payload.clientId} - ${data.payload.properties.nickname})`)
this.dataHandler.addClient( this.dataHandler.addClient(
{ {
id: data.payload.clientId, id: data.payload.clientId,
@@ -127,8 +122,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} }
handleClientPropertiesUpdatedMessage(data: IClientPropertiesUpdatedMessage) { handleClientPropertiesUpdatedMessage(data: IClientPropertiesUpdatedMessage) {
console.log("handleClientPropertiesUpdate", data);
const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId); const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId);
if (client !== undefined) { if (client !== undefined) {
@@ -140,8 +133,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} }
handleTalkStatusChangedMessage(data: ITalkStatusChangedMessage) { handleTalkStatusChangedMessage(data: ITalkStatusChangedMessage) {
console.log("handleTalkStatusChanged", data);
const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId); const client: IClient | undefined = this.dataHandler.getClientById(data.payload.clientId, data.payload.connectionId);
if (client !== undefined) { if (client !== undefined) {
@@ -151,14 +142,12 @@ export class TS5MessageHandler implements ITS5MessageHandler {
}); });
} }
console.log(this.dataHandler.localConnections) // console.log(this.dataHandler.localConnections)
console.log(this.dataHandler.localChannels) // console.log(this.dataHandler.localChannels)
console.log(this.dataHandler.localClients) // console.log(this.dataHandler.localClients)
} }
handleClientSelfPropertyUpdatedMessage(data: IClientSelfPropertyUpdatedMessage) { handleClientSelfPropertyUpdatedMessage(data: IClientSelfPropertyUpdatedMessage) {
console.log("handleClientSelfPropertyUpdated", data);
const connection: IConnection | undefined = this.dataHandler.getConnectionById(this.activeConnectionId); const connection: IConnection | undefined = this.dataHandler.getConnectionById(this.activeConnectionId);
if (data.payload.flag == "inputHardware" || connection == undefined) { // sadly thats the only way to detect if a server is active or not if (data.payload.flag == "inputHardware" || connection == undefined) { // sadly thats the only way to detect if a server is active or not
@@ -167,8 +156,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} }
handleServerPropertiesUpdatedMessage(data: IServerPropertiesUpdatedMessage) { handleServerPropertiesUpdatedMessage(data: IServerPropertiesUpdatedMessage) {
console.log("handleServerPropertiesUpdated", data);
const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId); const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId);
if (connection !== undefined) { // Update existing connection if (connection !== undefined) { // Update existing connection
@@ -180,8 +167,6 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} }
handleConnectStatusChangedMessage(data: IConnectStatusChangedMessage) { handleConnectStatusChangedMessage(data: IConnectStatusChangedMessage) {
console.log("handleConnectStatusChanged", data);
if (data.payload.status === 0) { // Disconnected from server if (data.payload.status === 0) { // Disconnected from server
const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId); const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId);
@@ -201,19 +186,11 @@ export class TS5MessageHandler implements ITS5MessageHandler {
} }
handleChannelsMessage(data: IChannelsMessage) { handleChannelsMessage(data: IChannelsMessage) {
console.log("handleChannels", data);
// Wait a bit for the connection to be added // Wait a bit for the connection to be added
setTimeout(() => { setTimeout(() => {
console.log(this.dataHandler.localConnections);
console.log(data.payload.connectionId)
console.log(this.dataHandler.localConnections.filter((connection: IConnection) => connection.id === data.payload.connectionId)[0]);
console.log(this.dataHandler.localConnections.find((connection: IConnection) => connection.id === data.payload.connectionId));
const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId); const connection: IConnection | undefined = this.dataHandler.getConnectionById(data.payload.connectionId);
console.log(connection);
if (connection !== undefined) { if (connection !== undefined) {
this.parseChannelInfos(data.payload.info, connection); this.parseChannelInfos(data.payload.info, connection);
console.log(data.payload.info)
} }
}, 1000); }, 1000);
} }

View File

@@ -4,10 +4,19 @@
gap: 0 0; gap: 0 0;
padding: 1rem; padding: 1rem;
h3 { .channelNameContainer {
font-size: 1.5rem; display: flex;
font-weight: 500; align-items: center;
margin: 0; margin-bottom: 20px;
h3 {
font-size: 1.7rem;
font-weight: 500;
margin: 0;
background-color: rgba(47, 49, 54, 0.5);
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
color: white;
}
} }
.client { .client {

31
src/utils/logger.tsx Normal file
View File

@@ -0,0 +1,31 @@
export default class Logger {
// Log message to the console
public static log(message: string, data: object | null = null): void {
console.log(`[Log] %c${message}`.trim(), "color: gray", data ?? "");
}
// Log warning to the console
public static warn(message: string, data: object | null = null): void {
console.warn(`%c${message}`.trim(), data ?? "");
}
// Log error to the console
public static error(message: string, data: object | null = null): void {
console.error(`%c${message}`.trim(), data ?? "");
}
// Log message received from the websocket to the console
public static wsReceived(data: object, message: string | undefined = undefined): void {
console.log(`%c[WS Recieved] ${message ?? ""}`.trim(), "color: #8258c7", data);
}
// Log message sent to the websocket to the console
public static wsSent(data: object, message: string | undefined = undefined): void {
console.log(`%c[WS Sent] ${message ?? ""}`.trim(), "color: #4eb570", data);
}
// Log message to the console with a timestamp
public static ts(message: string, data: object | null = null): void {
console.log(`%c[TS] ${message}`.trim(), "color: #2e6bc7", data ?? "");
}
}

View File

@@ -17,5 +17,9 @@ export default defineConfig({
'@styles': path.resolve(__dirname, './src/styles'), '@styles': path.resolve(__dirname, './src/styles'),
}, },
}, },
build: {
outDir: 'dist',
emptyOutDir: true,
},
plugins: [react(), viteSingleFile({ useRecommendedBuildConfig: false })], plugins: [react(), viteSingleFile({ useRecommendedBuildConfig: false })],
}) })