# 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 gaming-hub 10.10.10.10:9080/root/gaming-hub:latest http://10.10.10.10:9080/ bridge false Discord Gaming Hub - Soundboard + World Radio mit Web-Dashboard MediaApp:Other http://[IP]:[PORT:8080]/ --restart unless-stopped 8080 /mnt/user/appdata/gaming-hub 512 true /data/sounds ``` ## 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.