2026-03-05 11:13:03 +01:00
|
|
|
|
# Jukebox Vibe – Discord Soundboard
|
|
|
|
|
|
|
|
|
|
|
|
Self-hosted Discord Soundboard mit Web-UI. Spielt Sounds in Discord Voice Channels, verwaltet per Browser. Dockerized, ein Container, fertig.
|
|
|
|
|
|
|
|
|
|
|
|
## Tech Stack
|
|
|
|
|
|
|
|
|
|
|
|
| Komponente | Technologie |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| **Backend** | Node.js 20, Express, TypeScript, discord.js 14 |
|
|
|
|
|
|
| **Voice** | @discordjs/voice 0.19, @discordjs/opus, DAVE E2EE (@snazzah/davey) |
|
|
|
|
|
|
| **Frontend** | React 18, Vite, TypeScript, Custom CSS |
|
|
|
|
|
|
| **Audio** | ffmpeg (EBU R128 Loudnorm), yt-dlp |
|
|
|
|
|
|
| **Deployment** | Multi-Stage Dockerfile, GitLab CI/CD (Kaniko) |
|
|
|
|
|
|
|
|
|
|
|
|
## Features
|
|
|
|
|
|
|
|
|
|
|
|
### Soundboard
|
|
|
|
|
|
- **MP3/WAV Playback** in Discord Voice Channels
|
|
|
|
|
|
- **Loudness Normalization** (EBU R128) mit PCM-Cache fuer Instant-Playback
|
|
|
|
|
|
- **Per-Guild Volume** (0-100%), live adjustable, persistiert
|
|
|
|
|
|
- **Party Mode** – zufaellige Sounds alle 30-90 Sekunden
|
|
|
|
|
|
- **Stop/Panic** – sofortiger Playback-Stop fuer alle
|
|
|
|
|
|
|
|
|
|
|
|
### Discord Bot
|
|
|
|
|
|
- **Entrance Sounds** – persoenlicher Sound beim Channel-Join
|
|
|
|
|
|
- **Exit Sounds** – Sound beim Disconnect (nicht bei Channel-Wechsel)
|
|
|
|
|
|
- **DM Upload** – Sounds per Direktnachricht an den Bot hochladen
|
|
|
|
|
|
- **Voice Resilience** – 3-Tier Reconnect mit Exponential Backoff
|
|
|
|
|
|
|
|
|
|
|
|
### Web-UI
|
|
|
|
|
|
- **Sound Grid** mit klickbaren Cards
|
|
|
|
|
|
- **Suche** mit optionalem Fuzzy-Matching
|
|
|
|
|
|
- **Ordner-Filter** mit farbigen Chips
|
|
|
|
|
|
- **Favoriten** (Cookie-basiert)
|
|
|
|
|
|
- **Kategorien** und **Badges** (Admin-verwaltet)
|
|
|
|
|
|
- **Channel-Selector** gruppiert nach Guild
|
|
|
|
|
|
- **Now Playing** Anzeige mit Wellenform-Animation
|
|
|
|
|
|
- **5 Themes** – Discord, Midnight, Forest, Sunset, Ocean
|
|
|
|
|
|
- **Card-Size Slider** (80-160px)
|
|
|
|
|
|
- **Drag & Drop Upload** (Admin)
|
|
|
|
|
|
- **MP3-URL Import** – direkter Download und Playback
|
|
|
|
|
|
- **Real-Time Sync** via Server-Sent Events (SSE)
|
|
|
|
|
|
|
|
|
|
|
|
### Admin
|
|
|
|
|
|
- **Login** mit HMAC-SHA256 Token (HttpOnly Cookie, 7 Tage)
|
|
|
|
|
|
- **Bulk Delete / Rename** von Sounds
|
|
|
|
|
|
- **Upload** bis zu 20 Dateien (je max. 50MB)
|
|
|
|
|
|
- **Kategorien** erstellen, bearbeiten, loeschen, zuweisen
|
|
|
|
|
|
- **Custom Badges** zuweisen/entfernen
|
|
|
|
|
|
|
|
|
|
|
|
## Quickstart
|
|
|
|
|
|
|
|
|
|
|
|
### Voraussetzungen
|
|
|
|
|
|
- Docker
|
|
|
|
|
|
- Discord Bot Token mit Intents: `Guilds`, `GuildVoiceStates`, `GuildMembers`, `DirectMessages`, `MessageContent`
|
|
|
|
|
|
|
|
|
|
|
|
### Setup
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2025-08-09 01:10:09 +02:00
|
|
|
|
```bash
|
2026-03-05 11:13:03 +01:00
|
|
|
|
git clone https://git.daddelolymp.de/root/jukebox-vibe.git
|
2025-08-09 01:10:09 +02:00
|
|
|
|
cd jukebox-vibe
|
|
|
|
|
|
cp .env.example .env
|
2026-03-05 11:13:03 +01:00
|
|
|
|
# .env anpassen (DISCORD_TOKEN, ADMIN_PWD)
|
|
|
|
|
|
docker compose up -d
|
2025-08-09 01:10:09 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
### Docker Run (ohne Compose)
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2025-08-07 23:24:56 +02:00
|
|
|
|
```bash
|
2026-03-05 11:13:03 +01:00
|
|
|
|
docker run -d \
|
|
|
|
|
|
--name jukebox \
|
|
|
|
|
|
-p 8199:8080 \
|
|
|
|
|
|
-e DISCORD_TOKEN=dein_token \
|
|
|
|
|
|
-e ADMIN_PWD=dein_passwort \
|
|
|
|
|
|
-v $(pwd)/sounds:/data/sounds \
|
|
|
|
|
|
--restart unless-stopped \
|
|
|
|
|
|
git.daddelolymp.de/root/jukebox-vibe:latest
|
2025-08-07 23:24:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Konfiguration
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
| Variable | Default | Beschreibung |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `DISCORD_TOKEN` | *erforderlich* | Discord Bot Token |
|
|
|
|
|
|
| `ADMIN_PWD` | `''` | Admin-Passwort fuer Web-UI |
|
|
|
|
|
|
| `PORT` | `8080` | Server Port |
|
|
|
|
|
|
| `SOUNDS_DIR` | `/data/sounds` | Sound-Verzeichnis (Docker Volume) |
|
|
|
|
|
|
| `ALLOWED_GUILD_IDS` | `''` | Komma-getrennte Guild-IDs (leer = alle) |
|
|
|
|
|
|
| `NORMALIZE_ENABLE` | `true` | Loudness Normalization an/aus |
|
|
|
|
|
|
| `NORMALIZE_I` | `-16` | Ziel-Lautstaerke in LUFS |
|
|
|
|
|
|
| `NORMALIZE_LRA` | `11` | Loudness Range |
|
|
|
|
|
|
| `NORMALIZE_TP` | `-1.5` | True Peak in dBTP |
|
|
|
|
|
|
| `NORM_CONCURRENCY` | `2` | Parallele ffmpeg-Prozesse |
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Bot Commands
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
Alle Commands per DM an den Bot oder im Server-Chat:
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
| Command | Beschreibung |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `?help` | Hilfe anzeigen |
|
|
|
|
|
|
| `?list` | Alle Sounds auflisten |
|
|
|
|
|
|
| `?entrance <datei>` | Entrance-Sound setzen |
|
|
|
|
|
|
| `?entrance remove` | Entrance-Sound entfernen |
|
|
|
|
|
|
| `?exit <datei>` | Exit-Sound setzen |
|
|
|
|
|
|
| `?exit remove` | Exit-Sound entfernen |
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
**Upload via DM:** MP3/WAV als Anhang an den Bot senden – wird automatisch gespeichert und normalisiert.
|
2025-08-10 21:30:46 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## API
|
2025-08-10 21:30:46 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
### Public
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2025-08-09 01:10:09 +02:00
|
|
|
|
```
|
2026-03-05 11:13:03 +01:00
|
|
|
|
GET /api/health Healthcheck + Stats
|
|
|
|
|
|
GET /api/sounds Sound-Liste (query: q, folder, categoryId, fuzzy)
|
|
|
|
|
|
GET /api/analytics Top 10 Most Played, Gesamt-Plays
|
|
|
|
|
|
GET /api/channels Verfuegbare Voice Channels
|
|
|
|
|
|
GET /api/selected-channels Gespeicherte Channel-Auswahl
|
|
|
|
|
|
POST /api/selected-channel Channel-Auswahl setzen
|
|
|
|
|
|
POST /api/play Sound abspielen (body: soundName, guildId, channelId)
|
|
|
|
|
|
POST /api/play-url MP3-URL downloaden und abspielen
|
|
|
|
|
|
POST /api/stop Playback stoppen
|
|
|
|
|
|
GET /api/volume Volume abfragen
|
|
|
|
|
|
POST /api/volume Volume setzen (0-1)
|
|
|
|
|
|
POST /api/party/start Party Mode starten
|
|
|
|
|
|
POST /api/party/stop Party Mode stoppen
|
|
|
|
|
|
GET /api/events SSE Stream (Real-Time Updates)
|
2025-08-09 01:10:09 +02:00
|
|
|
|
```
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
### Admin (Cookie-Auth)
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
```
|
|
|
|
|
|
POST /api/admin/login Login (body: password)
|
|
|
|
|
|
POST /api/admin/logout Logout
|
|
|
|
|
|
GET /api/admin/status Auth-Status pruefen
|
|
|
|
|
|
POST /api/upload Dateien hochladen (multipart, max 20x50MB)
|
|
|
|
|
|
POST /api/admin/sounds/delete Sounds loeschen (body: paths[])
|
|
|
|
|
|
POST /api/admin/sounds/rename Sound umbenennen
|
|
|
|
|
|
GET /api/categories Kategorien auflisten
|
|
|
|
|
|
POST /api/categories Kategorie erstellen
|
|
|
|
|
|
PATCH /api/categories/:id Kategorie bearbeiten
|
|
|
|
|
|
DELETE /api/categories/:id Kategorie loeschen
|
|
|
|
|
|
POST /api/categories/assign Kategorien zuweisen
|
|
|
|
|
|
POST /api/badges/assign Badges zuweisen
|
|
|
|
|
|
POST /api/badges/clear Badges entfernen
|
2025-08-07 23:24:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Sound-Dateien
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
```
|
|
|
|
|
|
/data/sounds/
|
|
|
|
|
|
state.json # Persistierter State (Volumes, Plays, Kategorien, etc.)
|
|
|
|
|
|
.norm-cache/ # Normalisierte PCM-Caches
|
|
|
|
|
|
airhorn.mp3 # Sounds im Root
|
|
|
|
|
|
memes/ # Ordner-Struktur (1 Ebene)
|
|
|
|
|
|
bruh.mp3
|
|
|
|
|
|
oof.wav
|
2025-08-07 23:24:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
- Formate: `.mp3`, `.wav`
|
|
|
|
|
|
- Ordner werden als Filter-Chips im Frontend angezeigt
|
|
|
|
|
|
- `.norm-cache/` wird automatisch verwaltet (Cache-Invalidierung bei Datei-Aenderung)
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Loudness Normalization
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
Alle Sounds werden per ffmpeg `loudnorm` (EBU R128) normalisiert:
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
1. **Startup:** Hintergrund-Sync normalisiert alle uncached Sounds
|
|
|
|
|
|
2. **Cache Hit:** Gecachte PCM-Datei wird direkt gestreamt (kein ffmpeg, instant)
|
|
|
|
|
|
3. **Cache Miss:** ffmpeg streamt live zum Player UND schreibt gleichzeitig in Cache
|
|
|
|
|
|
4. **Upload:** Neue Dateien werden sofort im Hintergrund normalisiert
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Voice Connection
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
3-stufiges Recovery bei Verbindungsproblemen:
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
1. **Wait for Ready** (15s Timeout)
|
|
|
|
|
|
2. **Rejoin** im selben Channel (15s Timeout)
|
|
|
|
|
|
3. **Destroy + Fresh Join** (15s Timeout)
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
Lifecycle-Handler mit:
|
|
|
|
|
|
- Max 3 Reconnect-Versuche mit Exponential Backoff
|
|
|
|
|
|
- Anti-Reentrancy Guard gegen Endlosschleifen
|
|
|
|
|
|
- Automatisches State-Cleanup bei totalem Verbindungsverlust
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Projektstruktur
|
2025-08-09 01:10:09 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
```
|
|
|
|
|
|
jukebox-vibe/
|
|
|
|
|
|
server/
|
|
|
|
|
|
src/index.ts # Server + Discord Bot (Hauptdatei)
|
|
|
|
|
|
package.json
|
|
|
|
|
|
tsconfig.json
|
|
|
|
|
|
web/
|
|
|
|
|
|
src/
|
|
|
|
|
|
App.tsx # React SPA
|
|
|
|
|
|
styles.css # Themes + Layout
|
|
|
|
|
|
types.ts # TypeScript Types
|
|
|
|
|
|
package.json
|
|
|
|
|
|
vite.config.ts
|
|
|
|
|
|
Dockerfile # Multi-Stage Build (web + server + runtime)
|
|
|
|
|
|
docker-compose.yml
|
|
|
|
|
|
.gitlab-ci.yml # CI/CD Pipeline (Kaniko)
|
|
|
|
|
|
.env.example
|
2025-08-07 23:24:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## CI/CD
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
GitLab CI baut automatisch bei jedem Push:
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
| Branch | Image Tag |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `main` | `:main`, `:latest`, `:sha` |
|
|
|
|
|
|
| `nightly` | `:nightly`, `:sha` |
|
|
|
|
|
|
| andere | `:<branch-name>`, `:sha` |
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
Registry: `git.daddelolymp.de/root/jukebox-vibe`
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
## Lizenz
|
2025-08-07 23:24:56 +02:00
|
|
|
|
|
2026-03-05 11:13:03 +01:00
|
|
|
|
MIT
|