Hördle Bot 587fa59b79 feat(special-curation): complete implementation with all components
- Database: SpecialSong model with startTime
- Backend: API endpoints for curation
- Admin: Waveform editor and curation page
- Game: startTime support in AudioPlayer
- UI: Curate button in admin dashboard
2025-11-23 00:50:35 +01:00
2025-11-21 12:25:19 +01:00
2025-11-21 12:25:19 +01:00
2025-11-21 12:25:19 +01:00

Hördle

Eine Web-App inspiriert von Heardle, bei der Nutzer täglich einen Song anhand kurzer Audio-Schnipsel erraten müssen.

Features

  • Tägliches Rätsel: Jeden Tag ein neuer Song für alle Nutzer.
  • Inkrementelle Hinweise: Startet mit 2 Sekunden, dann 4s, 7s, 11s, 16s, 30s, bis 60s (7 Versuche).
  • Admin Dashboard:
    • Upload von MP3-Dateien.
    • Automatische Extraktion von ID3-Tags (Titel, Interpret).
    • Bearbeitung von Metadaten.
    • Sortierbare Song-Bibliothek (Titel, Interpret, Hinzugefügt am).
    • Play/Pause-Funktion zum Vorhören in der Bibliothek.
  • Cover Art:
    • Automatische Extraktion von Cover-Bildern aus MP3-Dateien.
    • Anzeige des Covers nach Spielende (Sieg/Niederlage).
    • Automatische Migration bestehender Songs.
  • Teilen-Funktion: Ergebnisse können als Emoji-Grid geteilt werden.
  • PWA Support: Installierbar als App auf Desktop und Mobilgeräten (Manifest & Icons).
  • Persistenz: Spielstatus wird lokal im Browser gespeichert.
  • Benachrichtigungen: Integration mit Gotify für Push-Nachrichten bei Spielabschluss.
  • Genre-Management:
    • Erstellen und Verwalten von Musik-Genres.
    • Manuelle Zuweisung von Genres zu Songs.
    • KI-gestützte automatische Kategorisierung mit OpenRouter (Claude 3.5 Haiku).
    • Genre-spezifische tägliche Rätsel.

Tech Stack

  • Framework: Next.js 16 (App Router)
  • Styling: Vanilla CSS
  • Datenbank: SQLite (via Prisma ORM)
  • Deployment: Docker & Docker Compose

Lokale Entwicklung

  1. Abhängigkeiten installieren:

    npm install
    
  2. Datenbank initialisieren:

    npx prisma generate
    npx prisma db push
    
  3. Optional: Cover-Bilder migrieren: Falls MP3-Dateien ohne Cover in der Datenbank sind:

    node scripts/migrate-covers.mjs
    
  4. Entwicklungsserver starten:

    npm run dev
    

    Die App läuft unter http://localhost:3000.

Deployment mit Docker

Das Projekt ist für den Betrieb mit Docker optimiert.

  1. Vorbereitung: Kopiere die Beispiel-Konfiguration:

    cp docker-compose.example.yml docker-compose.yml
    

    Passe die Umgebungsvariablen in der docker-compose.yml an:

    • ADMIN_PASSWORD: Admin-Passwort als Bcrypt-Hash. Erstelle den Hash mit: node scripts/hash-password.js <dein-passwort> Wichtig: In docker-compose.yml müssen alle $ Zeichen im Hash verdoppelt werden ($$), damit sie nicht als Variablen interpretiert werden! Beispiel: $$2b$$10$$...
    • TZ: Zeitzone für täglichen Puzzle-Wechsel (Standard: Europe/Berlin)
    • GOTIFY_URL: URL deines Gotify Servers (z.B. https://gotify.example.com)
    • GOTIFY_APP_TOKEN: App Token für Gotify (z.B. A...)
    • OPENROUTER_API_KEY: API-Key für OpenRouter (für KI-Kategorisierung, optional)
  2. Starten:

    docker compose up --build -d
    

    Die App ist unter http://localhost:3010 erreichbar (Port in docker-compose.yml konfiguriert).

  3. Daten-Persistenz:

    • Die SQLite-Datenbank wird im Ordner ./data gespeichert.
    • Hochgeladene Songs und Cover liegen in ./public/uploads.
    • Beide Ordner werden als Docker Volumes eingebunden, sodass Daten auch bei Container-Neustarts erhalten bleiben.
    • Beim Start des Containers wird automatisch ein Migrations-Skript ausgeführt, das fehlende Cover-Bilder aus den MP3s extrahiert.
  4. Admin-Zugang:

    • URL: /admin
    • Standard-Passwort: admin123 (Bitte in docker-compose.yml ändern! Muss als Hash hinterlegt werden.)

Nginx-Konfiguration (für Reverse Proxy)

Wenn du Nginx als Reverse Proxy verwendest, benötigst du spezielle Einstellungen für Audio-Streaming:

server {
    listen 80;
    server_name your-domain.com;

    # Erhöhe Upload-Limit
    client_max_body_size 50M;

    location / {
        proxy_pass http://localhost:3010;
        proxy_http_version 1.1;
        
        # Wichtig für Audio-Streaming: Range Requests weiterleiten
        proxy_set_header Range $http_range;
        proxy_set_header If-Range $http_if_range;
        proxy_no_cache $http_range $http_if_range;
        
        # Standard Proxy Headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Eine vollständige Beispiel-Konfiguration findest du in nginx.conf.example.

Troubleshooting

Audio-Dateien lassen sich nicht abspielen (in Produktion mit Nginx)

Problem: MP3-Dateien funktionieren lokal, aber nicht hinter Nginx.

Lösung:

  1. Stelle sicher, dass Nginx Range Requests unterstützt (siehe Nginx-Konfiguration oben)
  2. Prüfe die Nginx-Logs: sudo tail -f /var/log/nginx/error.log
  3. Teste direkt ohne Nginx: http://localhost:3010/uploads/dateiname.mp3
  4. Überprüfe die Response-Headers im Browser (Developer Tools → Network)

Wichtige Nginx-Einstellungen:

  • proxy_set_header Range $http_range; - Leitet Range Requests weiter
  • proxy_buffering off; - Deaktiviert Buffering für große Dateien
  • client_max_body_size 50M; - Erlaubt große Uploads

Admin Login schlägt fehl (Docker)

Problem: "Wrong password" trotz korrekt generiertem Hash.

Ursache: Docker Compose interpretiert $ Zeichen im Hash als Variablen.

Lösung: In der docker-compose.yml müssen alle $ Zeichen im Hash verdoppelt werden ($$). Falsch: $2b$10$... Richtig: $$2b$$10$$...

Das Skript node scripts/hash-password.js <pw> gibt nun auch direkt den passenden String für Docker Compose aus.

Lizenz

MIT

Description
No description provided
Readme 1.9 MiB
Languages
TypeScript 90.7%
JavaScript 3.8%
CSS 3.3%
Dockerfile 1.2%
Shell 1%