348 lines
13 KiB
Markdown
348 lines
13 KiB
Markdown
# Gaming Hub
|
|
|
|
Plugin-basierte Discord-Bot-Plattform mit Web-Dashboard. Zwei Bots (Jukebox + Radio) laufen parallel mit eigenem Discord-Token und eigener Voice-Connection pro Guild.
|
|
|
|
## Architektur
|
|
|
|
```
|
|
gaming-hub/
|
|
├── server/ # Backend (Node.js + Express + TypeScript)
|
|
│ └── src/
|
|
│ ├── index.ts # Entry Point, SSE, Plugin-Loader
|
|
│ ├── core/
|
|
│ │ ├── discord.ts # Discord.js Client Factory
|
|
│ │ ├── plugin.ts # Plugin-System + Voice Claims
|
|
│ │ ├── sse.ts # Server-Sent Events Broadcasting
|
|
│ │ └── persistence.ts # JSON State auf Disk
|
|
│ └── plugins/
|
|
│ ├── radio/ # World Radio Plugin
|
|
│ │ ├── index.ts # Routes, Voice, Streaming
|
|
│ │ └── api.ts # Radio Garden API Client
|
|
│ └── soundboard/ # Jukebox / Soundboard Plugin
|
|
│ └── index.ts # Sounds, Upload, Normalisierung
|
|
│
|
|
├── web/ # Frontend (React 19 + Vite + TypeScript)
|
|
│ ├── public/
|
|
│ │ └── nasa-blue-marble.jpg # NASA Blue Marble Globus-Textur (~28 MB)
|
|
│ └── src/
|
|
│ ├── App.tsx # Root, Tab-Navigation, SSE-Handler
|
|
│ ├── styles.css # Globale Styles + Radio-Styles
|
|
│ └── plugins/
|
|
│ ├── radio/
|
|
│ │ ├── RadioTab.tsx # 3D Globe UI (Sprite Particles)
|
|
│ │ └── TileTextureManager.ts # Tile Loader (Fallback)
|
|
│ └── soundboard/
|
|
│ ├── SoundboardTab.tsx # Sound Grid UI
|
|
│ └── soundboard.css # Soundboard Styles
|
|
│
|
|
├── Dockerfile # Multi-Stage Production Build (4 Stages)
|
|
├── .gitlab-ci.yml # CI/CD Pipeline (Kaniko)
|
|
└── .dockerignore
|
|
```
|
|
|
|
## Tech Stack
|
|
|
|
| Komponente | Technologie | Version |
|
|
|------------|-------------|---------|
|
|
| Runtime | Node.js | 24 (slim) |
|
|
| Backend | Express | 5.x |
|
|
| Frontend | React + Vite | 19.x / 6.x |
|
|
| Discord | discord.js + @discordjs/voice | 14.x / 0.19.x |
|
|
| 3D Globe | globe.gl + three.js | 2.35 / 0.172 |
|
|
| Audio | FFmpeg (GPL Build) + @discordjs/opus | latest / 0.10.x |
|
|
| TypeScript | tsc | 5.9.x |
|
|
| CI/CD | GitLab + Kaniko | v1.23.2 |
|
|
|
|
## Plugins
|
|
|
|
### Radio (`radio`)
|
|
- 3D-Globus mit 30.000+ Radiosendern weltweit (Radio Garden API)
|
|
- NASA Blue Marble Globus-Textur (statisches Satellitenbild)
|
|
- Sender als Sprite-Partikel mit radialer Gradient-Textur (Theme-Farbe)
|
|
- Server-seitiger Tile-Proxy mit In-Memory Cache (Fallback fuer CDN)
|
|
- FFmpeg PCM-Streaming in Discord Voice Channels
|
|
- Per-Guild Lautstaerke, Favoriten, Suche
|
|
- Voice Stats (Ping, Status, Uptime) via SSE
|
|
|
|
### Soundboard / Jukebox (`soundboard`)
|
|
- Sound-Upload (MP3/WAV) mit Drag & Drop
|
|
- Automatische Audio-Normalisierung (LUFS, FFmpeg)
|
|
- PCM Memory Cache (Standard 512 MB) fuer sofortige Wiedergabe
|
|
- Kategorien, Badges, Statistiken
|
|
- Admin-Panel mit Passwortschutz
|
|
- Party-Modus (Chaos Mode)
|
|
|
|
## Environment Variables
|
|
|
|
### Erforderlich
|
|
|
|
| Variable | Beschreibung |
|
|
|----------|-------------|
|
|
| `DISCORD_TOKEN_JUKEBOX` | Discord Bot Token fuer Soundboard/Jukebox |
|
|
| `DISCORD_TOKEN_RADIO` | Discord Bot Token fuer Radio |
|
|
|
|
> Beide Bots muessen im Discord Developer Portal separat erstellt werden.
|
|
> Jeder braucht die Intents: `GUILDS`, `GUILD_VOICE_STATES`, `GUILD_MEMBERS`.
|
|
|
|
### Optional
|
|
|
|
| Variable | Default | Beschreibung |
|
|
|----------|---------|-------------|
|
|
| `PORT` | `8080` | HTTP Server Port |
|
|
| `DATA_DIR` | `/data` | Persistenter Speicher (State, Sounds) |
|
|
| `ADMIN_PWD` | _(leer)_ | Admin-Passwort fuer Soundboard-Verwaltung |
|
|
| `ALLOWED_GUILD_IDS` | _(leer)_ | Komma-separierte Guild-IDs (Whitelist, leer = alle) |
|
|
| `DISCORD_TOKEN` | _(leer)_ | Legacy Fallback fuer Jukebox (wenn `DISCORD_TOKEN_JUKEBOX` fehlt) |
|
|
|
|
### Soundboard-spezifisch
|
|
|
|
| Variable | Default | Beschreibung |
|
|
|----------|---------|-------------|
|
|
| `SOUNDS_DIR` | `/data/sounds` | Verzeichnis fuer Sound-Dateien |
|
|
| `NORMALIZE_ENABLE` | `true` | Audio-Normalisierung aktivieren |
|
|
| `NORMALIZE_I` | `-16` | LUFS Integrated Loudness |
|
|
| `NORMALIZE_LRA` | `11` | LUFS Loudness Range |
|
|
| `NORMALIZE_TP` | `-1.5` | LUFS True Peak |
|
|
| `NORM_CONCURRENCY` | `2` | Parallele Normalisierungs-Threads |
|
|
| `PCM_CACHE_MAX_MB` | `512` | Max. PCM Memory Cache in MB |
|
|
|
|
### Build-Time (Vite)
|
|
|
|
| Variable | Default | Beschreibung |
|
|
|----------|---------|-------------|
|
|
| `VITE_APP_VERSION` | `1.0.0-dev` | Angezeigte Version im Frontend |
|
|
| `VITE_BUILD_CHANNEL` | `dev` | Release-Kanal (`stable` / `nightly` / `dev`) |
|
|
|
|
## Docker
|
|
|
|
### Image bauen
|
|
|
|
```bash
|
|
docker build -t gaming-hub:latest .
|
|
```
|
|
|
|
### Container starten
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name gaming-hub \
|
|
--restart unless-stopped \
|
|
-p 8080:8080 \
|
|
-v gaming-hub-data:/data \
|
|
-e DISCORD_TOKEN_JUKEBOX="dein-jukebox-token" \
|
|
-e DISCORD_TOKEN_RADIO="dein-radio-token" \
|
|
-e ADMIN_PWD="dein-admin-passwort" \
|
|
gaming-hub:latest
|
|
```
|
|
|
|
### Docker Compose
|
|
|
|
```yaml
|
|
version: "3.8"
|
|
|
|
services:
|
|
gaming-hub:
|
|
image: gaming-hub:latest
|
|
container_name: gaming-hub
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8080:8080"
|
|
volumes:
|
|
- gaming-hub-data:/data
|
|
environment:
|
|
# Erforderlich
|
|
- DISCORD_TOKEN_JUKEBOX=dein-jukebox-token
|
|
- DISCORD_TOKEN_RADIO=dein-radio-token
|
|
# Optional
|
|
- ADMIN_PWD=dein-admin-passwort
|
|
- ALLOWED_GUILD_IDS=
|
|
# Soundboard Tuning
|
|
- PCM_CACHE_MAX_MB=512
|
|
- NORMALIZE_ENABLE=true
|
|
|
|
volumes:
|
|
gaming-hub-data:
|
|
```
|
|
|
|
### Unraid Docker Template
|
|
|
|
```xml
|
|
<?xml version="1.0"?>
|
|
<Container version="2">
|
|
<Name>gaming-hub</Name>
|
|
<Repository>10.10.10.10:9080/root/gaming-hub:latest</Repository>
|
|
<Registry>http://10.10.10.10:9080/</Registry>
|
|
<Network>bridge</Network>
|
|
<Privileged>false</Privileged>
|
|
<Support/>
|
|
<Overview>Discord Gaming Hub - Soundboard + World Radio mit Web-Dashboard</Overview>
|
|
<Category>MediaApp:Other</Category>
|
|
<WebUI>http://[IP]:[PORT:8080]/</WebUI>
|
|
<ExtraParams>--restart unless-stopped</ExtraParams>
|
|
<Config Name="Web UI Port" Target="8080" Default="8080" Mode="tcp" Description="Web Dashboard Port" Type="Port" Display="always" Required="true" Mask="false">8080</Config>
|
|
<Config Name="Data" Target="/data" Default="/mnt/user/appdata/gaming-hub" Mode="rw" Description="Persistenter Speicher (State + Sounds)" Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/gaming-hub</Config>
|
|
<Config Name="Discord Token Jukebox" Target="DISCORD_TOKEN_JUKEBOX" Default="" Mode="" Description="Discord Bot Token fuer Soundboard" Type="Variable" Display="always" Required="true" Mask="true"/>
|
|
<Config Name="Discord Token Radio" Target="DISCORD_TOKEN_RADIO" Default="" Mode="" Description="Discord Bot Token fuer Radio" Type="Variable" Display="always" Required="true" Mask="true"/>
|
|
<Config Name="Admin Passwort" Target="ADMIN_PWD" Default="" Mode="" Description="Admin-Passwort fuer Soundboard-Verwaltung" Type="Variable" Display="always" Required="false" Mask="true"/>
|
|
<Config Name="Erlaubte Guild IDs" Target="ALLOWED_GUILD_IDS" Default="" Mode="" Description="Komma-separierte Guild-IDs (leer = alle)" Type="Variable" Display="advanced" Required="false" Mask="false"/>
|
|
<Config Name="PCM Cache MB" Target="PCM_CACHE_MAX_MB" Default="512" Mode="" Description="Max. PCM Memory Cache in MB" Type="Variable" Display="advanced" Required="false" Mask="false">512</Config>
|
|
<Config Name="Normalisierung" Target="NORMALIZE_ENABLE" Default="true" Mode="" Description="Audio-Normalisierung aktivieren" Type="Variable" Display="advanced" Required="false" Mask="false">true</Config>
|
|
<Config Name="Sound-Verzeichnis" Target="SOUNDS_DIR" Default="/data/sounds" Mode="" Description="Sound-Dateien Pfad im Container" Type="Variable" Display="advanced" Required="false" Mask="false">/data/sounds</Config>
|
|
</Container>
|
|
```
|
|
|
|
## Lokale Entwicklung
|
|
|
|
### Voraussetzungen
|
|
|
|
- Node.js 20+
|
|
- FFmpeg im PATH
|
|
- Zwei Discord Bot Tokens
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
git clone http://10.10.10.10:9080/root/gaming-hub.git
|
|
cd gaming-hub
|
|
|
|
# Server Dependencies
|
|
cd server && npm install && cd ..
|
|
|
|
# Web Dependencies
|
|
cd web && npm install && cd ..
|
|
```
|
|
|
|
### Dev Server starten
|
|
|
|
```bash
|
|
# Terminal 1: Backend
|
|
cd server
|
|
DISCORD_TOKEN_JUKEBOX="..." DISCORD_TOKEN_RADIO="..." ADMIN_PWD="test" DATA_DIR="./data" npm run dev
|
|
|
|
# Terminal 2: Frontend (Proxy auf localhost:8080)
|
|
cd web
|
|
npm run dev
|
|
```
|
|
|
|
Backend laeuft auf `http://localhost:8080`, Frontend auf `http://localhost:5173` (mit API-Proxy).
|
|
|
|
### Production Build
|
|
|
|
```bash
|
|
cd server && npm run build # -> server/dist/
|
|
cd ../web && npm run build # -> web/dist/
|
|
|
|
# Starten
|
|
cd ../server
|
|
NODE_ENV=production node dist/index.js
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Core
|
|
|
|
| Method | Pfad | Beschreibung |
|
|
|--------|------|-------------|
|
|
| GET | `/api/health` | Server-Status, Plugins, Bots, SSE-Clients |
|
|
| GET | `/api/plugins` | Plugin-Liste (Name, Version, Description) |
|
|
| GET | `/api/events` | SSE-Stream (Snapshot + Live-Updates) |
|
|
| POST | `/api/admin/login` | Admin-Login (Body: `{ password }`) |
|
|
|
|
### Radio Plugin
|
|
|
|
| Method | Pfad | Beschreibung |
|
|
|--------|------|-------------|
|
|
| GET | `/api/radio/places` | Alle Orte fuer den Globus |
|
|
| GET | `/api/radio/place/:id/channels` | Sender an einem Ort |
|
|
| GET | `/api/radio/search?q=...` | Sender/Orte suchen |
|
|
| GET | `/api/radio/guilds` | Verfuegbare Guilds + Voice Channels |
|
|
| POST | `/api/radio/play` | Stream starten |
|
|
| POST | `/api/radio/stop` | Stream stoppen |
|
|
| POST | `/api/radio/volume` | Lautstaerke setzen (0-1) |
|
|
| GET | `/api/radio/favorites` | Favoriten lesen |
|
|
| POST | `/api/radio/favorites` | Favorit togglen |
|
|
| GET | `/api/radio/status` | Aktueller Wiedergabestatus |
|
|
| GET | `/api/radio/tile/:z/:x/:y` | Tile-Proxy (Fallback Satellitenbild) |
|
|
|
|
### Soundboard Plugin
|
|
|
|
| Method | Pfad | Beschreibung |
|
|
|--------|------|-------------|
|
|
| GET | `/api/soundboard/channels` | Voice Channels aller Guilds |
|
|
| GET | `/api/soundboard/sounds` | Sound-Liste (mit Filter/Suche) |
|
|
| POST | `/api/soundboard/play` | Sound abspielen |
|
|
| POST | `/api/soundboard/stop` | Wiedergabe stoppen |
|
|
| POST | `/api/soundboard/volume` | Lautstaerke setzen |
|
|
| POST | `/api/soundboard/upload` | Sound hochladen (Multipart) |
|
|
|
|
## CI/CD Pipeline
|
|
|
|
GitLab CI mit Kaniko (`.gitlab-ci.yml`):
|
|
|
|
| Branch | Tag | Version | Channel |
|
|
|--------|-----|---------|---------|
|
|
| `main` | `latest`, `main` | `1.0.0` | `stable` |
|
|
| `nightly` | `nightly` | `1.0.0-nightly` | `nightly` |
|
|
| andere | `branch-name` | `1.0.0-dev` | `dev` |
|
|
|
|
Registry: `10.10.10.10:9080/root/gaming-hub`
|
|
|
|
## SSE Event Types
|
|
|
|
Alle Clients empfangen Events ueber `GET /api/events`:
|
|
|
|
| Event Type | Plugin | Beschreibung |
|
|
|-----------|--------|-------------|
|
|
| `snapshot` | - | Initialer Gesamtstatus beim Connect |
|
|
| `radio` | radio | Wiedergabestatus (playing/stopped) |
|
|
| `radio_volume` | radio | Lautstaerkeaenderung |
|
|
| `radio_voicestats` | radio | Voice-Ping, Gateway-Ping, Status, Uptime |
|
|
| `radio_favorites` | radio | Favoriten-Update |
|
|
| `soundboard_voicestats` | soundboard | Voice-Verbindungsdetails |
|
|
| `soundboard_nowplaying` | soundboard | Aktuell gespielter Sound |
|
|
| `soundboard_volume` | soundboard | Lautstaerkeaenderung |
|
|
| `soundboard_channel` | soundboard | Channel-Wechsel |
|
|
| `soundboard_party` | soundboard | Party-Modus Status |
|
|
|
|
## Discord Bot Setup
|
|
|
|
1. [Discord Developer Portal](https://discord.com/developers/applications) oeffnen
|
|
2. **Zwei** Applications erstellen (z.B. "Jukebox Bot" und "Radio Bot")
|
|
3. Fuer jede Application:
|
|
- Bot erstellen unter "Bot"
|
|
- Token kopieren
|
|
- **Privileged Gateway Intents** aktivieren:
|
|
- Server Members Intent
|
|
- Message Content Intent (optional)
|
|
- Unter "OAuth2 > URL Generator":
|
|
- Scopes: `bot`
|
|
- Bot Permissions: `Connect`, `Speak`, `Use Voice Activity`
|
|
- Generierte URL im Browser oeffnen, Bot zum Server einladen
|
|
4. Tokens als `DISCORD_TOKEN_JUKEBOX` und `DISCORD_TOKEN_RADIO` setzen
|
|
|
|
## Persistenz
|
|
|
|
Alle Daten werden im `DATA_DIR` (`/data`) gespeichert:
|
|
|
|
```
|
|
/data/
|
|
├── state.json # Plugin-State (Volumes, Favorites, Channels, ...)
|
|
└── sounds/ # Soundboard Audio-Dateien
|
|
├── sound1.mp3
|
|
├── subfolder/
|
|
│ └── sound2.wav
|
|
└── ...
|
|
```
|
|
|
|
State wird automatisch bei Aenderungen geschrieben und beim Start geladen.
|
|
|
|
## Dockerfile (Multi-Stage)
|
|
|
|
4 Build-Stages:
|
|
|
|
1. **web-build** - React/Vite Frontend kompilieren
|
|
2. **server-build** - TypeScript Backend kompilieren + native Dependencies (python3, make, g++)
|
|
3. **ffmpeg-fetch** - FFmpeg Binary von yt-dlp/FFmpeg-Builds herunterladen
|
|
4. **runtime** - Finales Image (node:24-slim) mit yt-dlp, ffmpeg, kompiliertem Code
|
|
|
|
Finales Image enthaelt nur Production Dependencies, kein TypeScript/Dev-Tooling.
|