# Gaming Hub Plugin-basierte Discord-Bot-Plattform mit Web-Dashboard und Electron Desktop App. Drei Bots (Jukebox, Radio, Notifications) 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, WebSocket │ ├── core/ │ │ ├── discord.ts # Discord.js Client Factory (Multi-Bot) │ │ ├── plugin.ts # Plugin-System + Voice Claims │ │ ├── sse.ts # Server-Sent Events Broadcasting │ │ └── persistence.ts # JSON State auf Disk (Atomic Writes) │ └── plugins/ │ ├── radio/ # World Radio (3D Globe + FFmpeg) │ ├── soundboard/ # Jukebox / Soundboard (PCM Cache) │ ├── lolstats/ # League of Legends Statistiken │ ├── streaming/ # P2P Live-Streaming (WebRTC) │ ├── watch-together/ # Synchron Video schauen │ ├── game-library/ # Steam + GOG Spielebibliothek │ └── notifications/ # Discord Benachrichtigungen │ ├── web/ # Frontend (React 19 + Vite + TypeScript) │ └── src/ │ ├── App.tsx # Root, Tab-Navigation, SSE-Handler │ ├── styles.css # Globale Styles │ └── plugins/ │ ├── radio/ # 3D Globe UI (Three.js Sprites) │ ├── soundboard/ # Sound Grid UI + Admin Panel │ ├── lolstats/ # Summoner-Suche + Match History │ ├── streaming/ # Stream-Erstellung + Viewer │ ├── watch-together/ # Raeume, Queue, Chat, Voting │ └── game-library/ # Spielebibliothek Grid │ ├── electron/ # Desktop App (Windows, Squirrel Installer) │ ├── main.js # BrowserWindow, Auto-Updater, Ad-Blocker │ ├── preload.js # IPC Bridge (contextBridge) │ ├── ad-blocker.js # Request-Filter + Filterliste │ └── forge.config.js # Electron Forge (Squirrel.Windows) │ ├── Dockerfile # Multi-Stage Production Build (4 Stages) ├── .gitlab-ci.yml # CI/CD (Kaniko Build + Deploy + Electron Build) └── VERSION # Aktuelle Version (SemVer) ``` ## 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 | | Streaming | WebSocket (ws) + WebRTC (Browser) | 8.x | | Desktop | Electron + Squirrel.Windows | 33.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 (max 500 Tiles) - 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, Entrance/Exit Sounds - Admin-Panel mit HMAC-Token-Authentifizierung - Party-Modus (Chaos Mode) ### LoL Stats (`lolstats`) - League of Legends Spielerstatistiken via op.gg - Summoner-Suche nach Game Name + Tag Line + Region - Match History mit Detail-Analyse - Letzte Suchen (max 10) persistent gespeichert - Unterstuetzte Regionen: NA, EUW, EUNE, KR, JP, BR, LAN, LAS, OCE, TR, RU, PH, SG, TH, TW, VN ### Streaming (`streaming`) - Peer-to-Peer Live-Streaming via WebRTC (kein Media-Server noetig) - WebSocket-Signaling fuer Offer/Answer/ICE-Austausch - Qualitaets-Presets: 720p30, 1080p30, 1080p60, 1440p60, 4K60, 4K165 Ultra - Einstellbare Bitrate (2.5 Mbit/s bis 50 Mbit/s je nach Preset) - Optionaler Passwortschutz (Server-seitige Verifikation) - Viewer-Liste mit Echtzeit-Updates - Discord-Benachrichtigungen bei Stream-Start/-Ende (via Notifications Plugin) - Stream-Link Auto-Join aus Discord (?viewStream=UUID) - Heartbeat-basierte Client-Erkennung (5s Intervall) ### Watch Together (`watch-together`) - Synchronisiertes Video-Schauen in Raeumen - Video-Queue mit URL-Eingabe - Demokratisches Voting (Skip, Pause) - Integrierter Chat pro Raum - Raum-Passwortschutz - Playback-Sync alle 2.5 Sekunden - Host/Member-Rollen - Auto-Cleanup nach 30s Inaktivitaet ### Game Library (`game-library`) - Multi-Plattform Spielebibliothek (Steam + GOG) - Steam API Integration (Spielzeit-Tracking) - GOG OAuth2 Login Flow (mit Token-Refresh) - IGDB Game-Enrichment (Metadaten, Cover-Bilder) - Mehrere Benutzerprofile pro Instanz - Spiele-Caching mit Ablaufzeit ### Notifications (`notifications`) - Discord Embed-Benachrichtigungen fuer andere Plugins - Kanal-Abonnement-Verwaltung pro Guild - Event-Filterung (stream_start, stream_end, etc.) - Konfigurierbare Ziel-Kanaele pro Event-Typ - Klickbare Stream-Links in Benachrichtigungen ## Electron Desktop App - Native Windows-Anwendung (Squirrel.Windows Installer) - Automatische Updates via `/downloads` Endpoint - Integrierter Ad-Blocker (Request-Filter + Filterliste) - Screen Capture Picker (getDisplayMedia mit nativem Dialog) - GOG OAuth Redirect-Handling in Child Windows - Stream-Warnung beim Schliessen (wenn Broadcast aktiv) - Windows Toast Notifications - Custom User-Agent (`GamingHubDesktop/{version}`) ## Environment Variables ### Erforderlich | Variable | Beschreibung | |----------|-------------| | `DISCORD_TOKEN_JUKEBOX` | Discord Bot Token fuer Soundboard/Jukebox | | `DISCORD_TOKEN_RADIO` | Discord Bot Token fuer Radio | | `DISCORD_TOKEN_NOTIFICATIONS` | Discord Bot Token fuer Benachrichtigungen | > Alle drei 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 geschuetzte Bereiche | | `ALLOWED_GUILD_IDS` | _(leer)_ | Komma-separierte Guild-IDs (Whitelist, leer = alle) | | `PUBLIC_URL` | _(leer)_ | Oeffentliche URL fuer Notification-Links (z.B. `https://hub.example.de`) | | `STEAM_API_KEY` | _(leer)_ | Steam Web API Key fuer Game Library | | `DISCORD_TOKEN` | _(leer)_ | Legacy Fallback fuer Jukebox | ### 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 DISCORD_TOKEN_NOTIFICATIONS="dein-notifications-token" \ -e ADMIN_PWD="dein-admin-passwort" \ -e PUBLIC_URL="https://hub.example.de" \ 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 - DISCORD_TOKEN_NOTIFICATIONS=dein-notifications-token # Optional - ADMIN_PWD=dein-admin-passwort - PUBLIC_URL=https://hub.example.de - STEAM_API_KEY=dein-steam-api-key - ALLOWED_GUILD_IDS= # Soundboard Tuning - PCM_CACHE_MAX_MB=512 - NORMALIZE_ENABLE=true volumes: gaming-hub-data: ``` ### Unraid Docker Template ```xml gaming-hub 192.168.1.100:5050/root/gaming-hub:latest http://192.168.1.100:9080/ bridge false Discord Gaming Hub - Soundboard, Radio, Streaming, LoL Stats, Watch Together, Game Library 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 - Drei Discord Bot Tokens ### Setup ```bash git clone http://192.168.1.100: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="..." \ DISCORD_TOKEN_NOTIFICATIONS="..." \ 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). ### Electron Desktop App (Windows) ```bash cd electron npm install npm start ``` Build fuer Distribution: ```bash cd electron npx electron-forge make ``` Erzeugt einen Squirrel.Windows Installer unter `electron/out/make/`. ### 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) | ### Streaming Plugin | Method | Pfad | Beschreibung | |--------|------|-------------| | WebSocket | `/ws` | Signaling (offer, answer, ice, viewer_joined, stream_ended) | ### Watch Together Plugin | Method | Pfad | Beschreibung | |--------|------|-------------| | WebSocket | `/ws` | Raum-Sync (create, join, queue, vote, chat, playback) | ### Game Library Plugin | Method | Pfad | Beschreibung | |--------|------|-------------| | GET | `/api/game-library/profiles` | Benutzerprofile | | POST | `/api/game-library/profiles` | Profil erstellen/aktualisieren | | GET | `/api/game-library/games` | Spieleliste (Steam + GOG) | | POST | `/api/game-library/gog/exchange` | GOG OAuth Code Exchange | ### Notifications Plugin | Method | Pfad | Beschreibung | |--------|------|-------------| | GET | `/api/notifications/config` | Kanal-Konfiguration lesen | | POST | `/api/notifications/config` | Kanal-Konfiguration speichern | ## CI/CD Pipeline GitLab CI mit Kaniko (`.gitlab-ci.yml`): | Stage | Job | Beschreibung | |-------|-----|-------------| | build | docker-build | Kaniko Multi-Platform Image Build | | deploy | deploy | Docker Pull + Run auf adriahub | | deploy | electron-build | Squirrel.Windows Installer + `.nupkg` + `.exe` | | bump-version | bump-version | PATCH-Version in VERSION auto-inkrementieren | | Branch | Tags | Version | Channel | |--------|------|---------|---------| | `main` | `latest`, `main` | `1.8.0` | `stable` | | `nightly` | `nightly` | `x.y.z-nightly` | `nightly` | | andere | `branch-name` | `x.y.z-dev` | `dev` | Registry: `192.168.1.100:5050/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. **Drei** Applications erstellen (z.B. "Jukebox Bot", "Radio Bot", "Notifications 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`, `Send Messages`, `Embed Links` - Generierte URL im Browser oeffnen, Bot zum Server einladen 4. Tokens als `DISCORD_TOKEN_JUKEBOX`, `DISCORD_TOKEN_RADIO` und `DISCORD_TOKEN_NOTIFICATIONS` setzen ## Persistenz Alle Daten werden im `DATA_DIR` (`/data`) gespeichert: ``` /data/ ├── sounds/ │ ├── hub-state.json # Plugin-State (Volumes, Favorites, Channels, Profiles, ...) │ ├── sound1.mp3 │ ├── subfolder/ │ │ └── sound2.wav │ └── ... ``` State wird automatisch bei Aenderungen geschrieben (Atomic Writes mit `.tmp` + `.bak` Rotation) und beim Start geladen. ## Dockerfile (Multi-Stage) 4 Build-Stages: 1. **web-build** - React/Vite Frontend kompilieren 2. **server-deps** - npm install + native Build-Tools (python3, make, g++) 3. **server-build** - TypeScript Backend kompilieren + Dev-Dependencies entfernen 4. **runtime** - Finales Image (node:24-slim) mit FFmpeg + kompiliertem Code Finales Image enthaelt nur Production Dependencies, kein TypeScript/Dev-Tooling. ## Streaming Qualitaets-Presets Der Streamer kann vor dem Broadcast ein Preset waehlen: | Preset | Aufloesung | FPS | Bitrate | |--------|-----------|-----|---------| | 720p30 | 1280x720 | 30 | 2.5 Mbit/s | | 1080p30 | 1920x1080 | 30 | 5 Mbit/s | | 1080p60 | 1920x1080 | 60 | 8 Mbit/s | | 1440p60 | 2560x1440 | 60 | 14 Mbit/s | | 4K60 | 3840x2160 | 60 | 25 Mbit/s | | 4K165 Ultra | 3840x2160 | 165 | 50 Mbit/s | Standard: 1080p60. Das Dropdown ist waehrend eines aktiven Broadcasts deaktiviert.