Das ultimative Musikquiz für Musikfans! Rate Songs, platziere sie chronologisch und teste dein Musikwissen. Spiele mit Freunden, sammle Punkte und werde zum Musik-Champion!
mxster ist ein Multiplayer-Musikquiz mit drei verschiedenen Spielmodi:
- 🔥 Hardcore: Rate Titel, Künstler und Erscheinungsjahr - bis zu 15 Punkte pro Song!
- 👤 Timeline (Persönlich): Baue deine eigene Timeline chronologisch auf
- 🌍 Timeline (Global): Alle Spieler teilen eine gemeinsame Timeline
- Virtuelle Karten (⭐ Empfohlen): Spiele komplett digital ohne physische Karten - sofort spielbereit!
- Physische Karten (Optional): Drucke PDF-Karten aus oder erstelle 3D-gedruckte Karten für ein haptisches Erlebnis
- ✅ Sofort spielbereit - Keine Vorbereitung, kein Setup
- ✅ Alle 209 Songs - Voller Zugriff auf die komplette Datenbank
- ✅ Kein Hardware nötig - Weder Drucker noch 3D-Drucker erforderlich
- ✅ Spontane Runden - Jederzeit und überall spielbar
- ✅ Bot-Gegner - Spiele gegen KI in 3 Schwierigkeitsgraden
- ✅ Für 95% der Nutzer ideal - Unkompliziert und direkt
- 🎵 Self-Hosted Audio - 209 volle Songs (3-5 Min), unbegrenzte Nutzer, keine Authentifizierung
- 🎧 Optional: Spotify Premium - 320 kbps Studio-Qualität, 20/25 Slots verfügbar
- 🎯 Tolerantes Raten - Fuzzy Matching erkennt Tippfehler (bis zu 3 Fehler)
- 📱 Progressive Web App - Installierbar auf Smartphone & Desktop, offline-fähig
- 🏆 Live Punktesystem - Echtzeit-Updates nach jeder Runde (5+5+5/2/1 Punkte)
- 🎖️ 20 Achievements - Volltreffer-Serie, Marathonläufer, Genre-Hopper, Großmeister
- 📊 Spielerstatistiken - Winrate, Dekaden-Verteilung, Genre-Analyse, Siegesserien
- 📈 Spielhistorie - Vollständige Aufzeichnung aller Spiele mit Export-Funktion
- 📸 QR-Code Scanner - Scanne Karten mit Smartphone-Kamera
- 💾 Auto-Save - Kein Fortschritt geht verloren, selbst bei Page Refresh
- 🔄 Backup & Restore - Exportiere und importiere alle Daten als JSON
- 🗑️ Datenverwaltung - Lösche einzelne Spieler oder alle Daten mit Bestätigung
- 🎮 Multiplayer - Spiele mit beliebig vielen Freunden
- ✏️ Song-Editor - Bearbeite Songs nachträglich mit interaktivem Wizard
- ✨ Music-Reactive Particles - 3D-Partikelhintergrund reagiert auf Musikwiedergabe
- 🎉 Unlock Animations - Konfetti beim Freischalten von Achievements
- 🎨 Gameplay Feedback - Konfetti bei richtigen, Gewitter bei falschen Antworten
- 🏆 Score-Based Effects - 5 Animationen je nach Punktzahl (Gold-Konfetti, Blitze, etc.)
- ⌨️ Keyboard Shortcuts - Enter/ESC zum Schließen von Dialogen
- ⌨️ Auto-Focus - Automatischer Fokus auf Eingabefelder (Desktop/Laptop)
- 🤖 Bot-Spieler (NEU v0.0.32) - Spiele gegen 1-3 KI-Gegner (Virtual Mode)
- 3 Schwierigkeitsgrade: Easy (😊), Medium (🎯), Hard (🔥)
- Realistische KI: Tippfehler-Simulation, Denkzeit-Delays, schwierigkeitsbasierte Genauigkeit
- Visuelle Indikatoren: Bot-Emoji, farbcodierte Schwierigkeits-Badges, "Bot denkt nach..." Animation
- Alle Modi unterstützt: Hardcore, Timeline Personal, Timeline Global
Self-Hosted Audio System - Die Hauptlösung für unbegrenztes Spielen:
- ✅ Volle Songs (3-5 Minuten, 128 kbps MP3)
- ✅ Unbegrenzte Spieler (keine 25-User-Limitierung)
- ✅ Keine Authentifizierung (kein Spotify-Login nötig)
- ✅ Sofort verfügbar (209 Songs auf VPS gehostet)
⚠️ Rechtlicher Hinweis: YouTube-Downloads via yt-dlp, rechtliche Grauzone, nur für private/nicht-kommerzielle/bildungsbasierte Zwecke
Technische Details:
- 209 Songs auf
https://mxster.de/audio/gehostet - Gesamtgröße: ~933 MB (Ø 4.47 MB pro Song)
- Qualität: 128 kbps MP3 (gut genug für Quiz-Spiele)
- nginx-served mit CORS und Caching
- Passwortschutz:
ydl(einmalig, rechtlicher Schutz)
Architektur:
YouTube → yt-dlp → MP3 (128kbps) → VPS → nginx → Browser
Nur für Audiophile mit Spotify Premium Account:
- ✅ Höchste Audioqualität (320 kbps)
- ✅ 100% legal über Spotify Web Playback SDK
- ✅ Unbegrenzte Songlänge
⚠️ Nur 20/25 Slots verfügbar (Spotify Development Mode Limitierung)⚠️ Spotify Premium erforderlich
Warum nur 25 Slots? - Spotify API Hürde:
Die App mxster nutzt Spotifys Standard-Web-API mit Playback-Scopes. Damit läuft sie automatisch im Development Mode, der auf 25 registrierte Nutzer begrenzt ist.
Spotify's Quota-Modelle:
- Development Mode: Max 25 Nutzer (aktuell aktiv)
- Extended Quota Mode: Benötigt registrierte Firma + 250.000+ aktive Nutzer
- Indie-Sperre: Seit Mai 2025 keine Anträge von Einzelpersonen möglich
Realität: Von 25 direkt auf 250.000 ist kein "Wachstumsmodell", sondern eine Sperre. Spotify will damit verhindern, dass kleine Projekte massenhaft Musik streamen. Das schneidet genau jene kreative Nische ab, aus der viele innovative Ideen kommen.
Slot anfragen: Noch 20 von 25 Slots frei. Sende eine E-Mail an [email protected] mit der E-Mail-Adresse deines Spotify-Kontos.
- Gratis-Modus (128 kbps): Gut genug für Quiz-Spiele, die meisten merken keinen Unterschied
- Spotify Premium (320 kbps): Studio-Qualität, nur für echte Audiophile wahrnehmbar besser
Slot-Konfiguration: pwa/spotify.slots.json
{
"totalSlots": 25,
"usedSlots": 5,
"availableSlots": 20,
"contactEmail": "[email protected]"
}- JSON-Datei bearbeiten (
usedSlotsändern) cd pwa && npm run build./scripts/deployment/deploy.sh- Dynamisch: Anzeige aktualisiert sich automatisch auf Landing Page
- Öffne https://developer.spotify.com/dashboard
- Wähle mxster App
- User Management → E-Mail-Adresse eintragen
spotify.slots.jsonaktualisieren (usedSlots++)
Architektur:
YouTube → yt-dlp → MP3 (128kbps) → VPS → nginx → Browser
Scripts: scripts/audio-hosting/ (siehe Audio Hosting README für Details)
cd scripts/audio-hosting
node download-songs.js # Alle 209 Songs (~933 MB, ~20 Min)
node download-songs.js --limit 5 # Test mit 5 Songs
node download-songs.js --resume # Überspringe existierendeFeatures:
- Concurrent: 3 parallele Downloads
- Retries: 3 Versuche pro Song
- Qualität: 128 kbps MP3
- Output:
pwa/public/audio/*.mp3
node upload-to-vps.js # Upload via rsync
node upload-to-vps.js --dry-run # Preview ÄnderungenFeatures:
- Incremental sync
- Permission: 644
- Target:
[email protected]:/var/www/html/mxster/audio/
node update-song-urls.js # Aktualisiere songs.json + songs.ts
node update-song-urls.js --dry-run # Preview ÄnderungenFeatures:
- Auto-backup (.backup-YYYY-MM-DD)
- URL-Format:
https://mxster.de/audio/song_XXX.mp3 - 100% Coverage Verification
node validate-audio.js # Test 10 Sample-Songs
node validate-audio.js --full # Test alle 209 SongsChecks:
- HTTP Status 200
- Content-Type: audio/mpeg
- File Size: 1-15 MB
- Report:
docs/validation-report.json
location /audio/ {
alias /var/www/html/mxster/audio/;
add_header Cache-Control "public, max-age=31536000";
add_header Access-Control-Allow-Origin "*";
add_header Content-Type "audio/mpeg";
add_header Accept-Ranges bytes;
}⚠️ YouTube-Downloads via yt-dlp⚠️ Rechtliche Grauzone - Urheberrechtlich bedenklich⚠️ Passwortschutz als rechtlicher Schutz (Passwort:ydl)⚠️ Nur für private, nicht-kommerzielle, bildungsbasierte Zwecke
Die Passwortabfrage dient dem rechtlichen Schutz des Entwicklers und signalisiert, dass die Nutzung auf eigene Verantwortung erfolgt.
Schalte 20 spannende Erfolge frei und zeige dein Können!
- 🎯 Volltreffer-Serie - 3x hintereinander alle Felder (Titel, Artist, Jahr) richtig erraten
- ⭐ Perfektionist - Alle Songs in einem Spiel richtig einsortiert
- 🕰️ Zeitreisender - Songs aus 5 verschiedenen Dekaden korrekt platziert
- 🏆 Hardcore-Champion - 100+ Punkte in einem einzelnen Hardcore-Spiel erreicht
- 🏃 Marathonläufer - 50 Spiele gespielt
- 👑 Unbesiegbar - 5 Spiele in Folge gewonnen
- 🎸 Dekaden-Kenner - 10 Songs einer Dekade perfekt erraten
- ⚡ Blitzschnell - Ein Spiel in unter 5 Minuten abgeschlossen
- 🔥 Comeback-King - Vom letzten Platz zum Gewinner aufgestiegen
- 🎓 Musikexperte - 1000 Gesamtpunkte über alle Spiele erreicht
- ⏰ Zeitmaschine - Songs aus 3 verschiedenen Dekaden erraten
- 🌈 Genre-Hopper - Songs aus 4 verschiedenen Musikrichtungen erraten
- 🎤 Name-Dropper - 5 Künstler hintereinander richtig erraten (Streak!)
- 👥 Gesellschaftsmensch - Mit 5 verschiedenen Spielern gespielt
- 🏆 Punktejäger - 75+ Punkte in einem Hardcore-Spiel erreicht
- 💎 Makellos - Ein Spiel mit 150/150 Punkten gewinnen (10 perfekte Songs!)
- 🌟 Legendäre Serie - 10 Spiele in Folge gewonnen
- 💯 Zenturio - 100 Spiele gespielt
- ⏱️ Meister der Zeit - Ein Spiel in unter 3 Minuten abgeschlossen
- 🔥 Comeback-Profi - 5x vom Rückstand zum Sieg gekommen
- 👑💎 Großmeister - 5000 Gesamtpunkte über alle Spiele erreicht
Features:
- Noch nicht freigeschaltete Achievements sind schwarz maskiert
- Freigeschaltete Achievements werden in Farbe mit Unlock-Datum angezeigt
- Fortschrittsbalken für wiederholbare Achievements
- Spieler-spezifisches Tracking (jeder Spieler hat eigene Achievements)
- Automatisches Speichern und Restore-Funktion
- Öffne mxster.de
- Klicke "Jetzt spielen (Gratis)" → Passwort:
ydl - Wähle Spielmodus & Variante
- Füge Spieler hinzu (min. 2)
- Los geht's! 🎉
Optional: Spotify Premium nutzen → "Mit Spotify Premium" → OAuth Login (20/25 Slots verfügbar)
Fertig! Du brauchst nichts zu installieren.
Voraussetzungen:
- Node.js (Version 18 oder höher)
- Git
- Spotify Premium Account
- Spotify Developer Account (kostenlos)
Installation:
# 1. Repository klonen
git clone https://github.com/pepperonas/mxster.git
cd mxster
# 2. PWA Dependencies installieren
cd pwa
npm install
# 3. Spotify App erstellen
# Gehe zu: https://developer.spotify.com/dashboard
# Erstelle eine neue App
# Kopiere Client ID und Client SecretKonfiguration:
Erstelle pwa/spotify.config.js:
export default {
clientId: 'DEINE_CLIENT_ID', // Von Spotify Dashboard
clientSecret: 'DEIN_CLIENT_SECRET', // Von Spotify Dashboard
redirectUri: 'http://localhost:5174/callback',
playlistId: '', // Optional: Spotify Playlist ID
scopes: [
'streaming',
'user-read-email',
'user-read-private',
'user-read-playback-state',
'user-modify-playback-state'
]
}Wichtig: Füge in deiner Spotify App die Redirect URI hinzu:
- Development:
http://localhost:5174/callback - Production:
https://deine-domain.com/callback
App starten:
# Im pwa/ Verzeichnis
npm run devÖffne http://localhost:5174 - Fertig! 🚀
Ziel: Sammle die meisten Punkte durch richtiges Raten! Maximum: 150 Punkte (15 pro Song × 10 Songs)
- Song abspielen: Song wird gezogen (virtuell oder per QR-Scan)
- Raten: Spieler gibt Titel, Künstler und Jahr ein
- Fuzzy Matching: System akzeptiert auch ähnliche Schreibweisen (z.B. "Fleetwood Mac" = "Fleetwood Mack")
- Punkte vergeben:
- ✅ Titel richtig: +5 Punkte
- ✅ Künstler richtig: +5 Punkte
- ✅ Jahr exakt richtig: +5 Punkte
- ✅ Jahr ±1 Jahr: +2 Punkte
- ✅ Jahr ±2 Jahre: +1 Punkt
- Automatische Platzierung: Karte wird sofort chronologisch in die Timeline einsortiert
- Live-Rangliste: Score-Overview zeigt alle Spieler sortiert nach Punkten
- Gewinner: Spieler mit den meisten Punkten nach 10 Karten
Besonderheiten:
- Keine manuelle Platzierung nötig
- Timeline wird automatisch sortiert
- Skip-Funktion verfügbar (0 Punkte, Karte wird trotzdem platziert)
- Granulares Punktesystem ermöglicht mehr taktische Tiefe
- ✅ 1. Skip pro Spieler: Keine Strafe
⚠️ 2. Skip (wenn alle einmal übersprungen): 33% Chance auf -3 Punkte⚠️ 3. Skip: 66% Chance auf -3 Punkte⚠️ 4.+ Skip: 95% Chance auf -3 Punkte
Bei Strafanwendung:
- Spieler verliert 3 Punkte (kann ins Negative gehen!)
- Ein neuer zufälliger Song wird automatisch gezogen
- Nächster Spieler ist dran
- Skip-Zähler wird für den neuen Song zurückgesetzt
Warndialog: Sobald alle Spieler mindestens 1x übersprungen haben, erscheint vor dem Spielerwechsel ein Warndialog mit:
- Aktueller Strafe-Wahrscheinlichkeit für den nächsten Skip
- Empfehlung zum Raten statt Überspringen
- "Verstanden"-Button zum Fortfahren
Diese Mechanik verhindert, dass schwierige Songs permanent übersprungen werden und fördert aktives Raten.
Ziel: Baue deine eigene Timeline mit 10 Karten auf!
- Individuelle Timelines: Jeder Spieler baut seine eigene separate Timeline
- Song abspielen: Song wird gezogen (virtuell oder per QR-Scan)
- Optional raten: Spieler kann zum Spaß raten (keine Punkte)
- Automatische Platzierung: Karte wird chronologisch in die persönliche Timeline eingefügt
- Bestätigung: Dialog zeigt an, an welcher Position die Karte eingefügt wurde
- Spielerwechsel: Timeline wechselt zur Timeline des nächsten Spielers
- Gewinner: Erster Spieler, der 10 Karten in seiner Timeline hat
Besonderheiten:
- Kein Punktesystem
- Jeder Spieler sieht nur seine eigene Timeline
- Automatische chronologische Sortierung
- Nur Kartenzählung entscheidet
Ziel: Gemeinsam eine Timeline aufbauen - Wer erreicht zuerst 10 Karten?
- Geteilte Timeline: ALLE Spieler teilen EINE gemeinsame Timeline
- Song abspielen: Song wird gezogen (virtuell oder per QR-Scan)
- Optional raten: Spieler kann zum Spaß raten (keine Punkte)
- Automatische Platzierung: Karte wird in die globale Timeline eingefügt
- Timeline bleibt gleich: Beim Spielerwechsel sehen alle die gleiche Timeline
- Kooperativ: Alle bauen gemeinsam an einer chronologischen Timeline
- Gewinner: Erster Spieler, der insgesamt 10 Karten platziert hat
Besonderheiten:
- Kein Punktesystem
- Timeline ändert sich NICHT beim Spielerwechsel
- Alle sehen immer die gleiche globale Timeline
- Kooperatives Timeline-Building
- Nur Kartenzählung pro Spieler entscheidet
- DJ wechselt jede Runde im Uhrzeigersinn
- Im Physical Mode: DJ scannt die Karte (sichtbar im Header: 🎧 DJ: Name)
- Aktiver Spieler platziert die Karte (🎮 Spieler: Name)
- Spieler links vom DJ beginnt
- Nach jedem Spielerzug wechselt zum nächsten Spieler
- Wenn nächster Spieler = aktueller DJ → DJ wechselt auch
- Ensures: DJ spielt nie mit seiner eigenen gescannten Karte
- Song wird gezogen: Virtuell automatisch oder DJ scannt QR-Code mit Kamera (Physical Mode)
- Song beginnt zu spielen (Spotify oder Preview)
- Titel & Artist werden nicht angezeigt
- Eingabefelder für Titel & Artist
- Button "✅ Prüfen" oder "⏭️ Überspringen"
- Ratespiel-Modus: Bei richtigem Guess werden Punkte vergeben
- Timeline-Modi: Raten zum Spaß (keine Punkte)
- Nach Guess/Skip: Song-Info wird enthüllt
- Ratespiel: Automatische chronologische Platzierung nach Bewertung
- Timeline-Modi: Automatische Platzierung mit Bestätigungsdialog
- System prüft ob Timeline noch korrekt sortiert ist
- Songs mit gleichem Jahr sind in beliebiger Reihenfolge erlaubt
- Ratespiel:
- Dialog zeigt ✅/❌ Checkmarks für Titel, Artist, Jahr
- Punkte werden vergeben (0-3 pro Song)
- Score Overview wird aktualisiert
- Timeline-Modi:
- Bestätigung der Platzierung
- Kartenzähler erhöht sich
🎧 DJ: [Name]
🎮 Spieler: [Name] (🎵 X/10 Karten)
Ratespiel zusätzlich: Punktestand sichtbar
- Zeigt alle korrekt platzierten Karten des aktiven Spielers
- Sortiert nach Jahr (älteste links)
- Format pro Karte:
- Jahr (groß)
- Song-Titel
- Artist
- Zeigt für alle Spieler:
- Name
- Punktestand
- Karten-Anzahl (X/10)
- Live-Updates nach jeder Runde
- Medaillen für Top 3
Das Spiel verwendet tolerantes Fuzzy-Matching für Song-Raten, damit Spieler nicht an Tippfehlern scheitern.
Groß-/Kleinschreibung ignoriert:
"Bohemian Rhapsody" = "bohemian rhapsody" = "BOHEMIAN RHAPSODY"Sonderzeichen ignoriert:
"Don't Stop" = "Dont Stop" = "Don't Stop!"
"What's Up?" = "Whats Up" = "Whats Up"Umlaute normalisiert:
"Für Elise" = "Fur Elise"
"Café del Mar" = "Cafe del Mar"Bindestriche → Leerzeichen:
"Re-mix" = "Re mix" = "Remix"Ampersand → "and":
"Rock & Roll" = "Rock and Roll"3 Tippfehler erlaubt (pro Feld):
Das System verwendet die Levenshtein-Distanz um Tippfehler zu erkennen:
- ✅ 1 Tippfehler: "Bohemian Rhapsody" → "Bohemain Rhapsody"
- ✅ 2 Tippfehler: "Fine Young Cannibals" → "Fin Young Cannibls"
- ✅ 3 Tippfehler: "She Drives Me Crazy" → "She Drivs Me Crzy"
- ❌ 4+ Tippfehler: "She Drives Me Crazy" → "She Drvs M Crzy"
Teilstring-Matching:
"Drives Me Crazy" matcht "She Drives Me Crazy"
"Fine Young" matcht "Fine Young Cannibals"Phonetische Ähnlichkeit:
"Freddie Mercury" = "Freddy Mercury"
"Philip" = "Phil"
"Eric" = "Erik"Abkürzungen & Slashes:
"AC/DC" = "ACDC" = "AC DC"
"N.W.A." = "nwa""The" Präfix ignoriert:
"The Beatles" = "Beatles"
"The Rolling Stones" = "Rolling Stones"Track-Suffixe ignoriert (wichtig!):
Spieler müssen keine Remix/Version-Informationen eingeben:
"Like a Prayer" = "Like a Prayer - 12" Extended Remix"
"Drop the Pressure" = "Drop the Pressure - Club Mix"
"Totoish" = "Totoish - Radio Cut"
"Song Name" = "Song Name (Extended Version)"
"Song Name" = "Song Name [Club Mix]"
"Song Name" = "Song Name 2024 Remastered"Erkannte Suffixe:
12",7"(Vinyl-Formate)Extended,Radio,Club,Vocal,InstrumentalRemix,Mix,Edit,Version,CutRemaster,Remastered(auch mit Jahreszahlen)Live,OriginalSingle Version,Album Version
Normalisierung:
function normalizeText(text) {
return text
.toLowerCase() // Lowercase
.normalize('NFD') // Unicode decomposition
.replace(/[\u0300-\u036f]/g, '') // Entferne diakritische Zeichen
.replace(/[''`]/g, '') // Entferne Apostrophe
.replace(/[.,?!;:]/g, '') // Entferne Interpunktion
.replace(/[-_]/g, ' ') // Bindestriche → Leerzeichen
.replace(/&/g, 'and') // & → "and"
.replace(/\s+/g, ' ') // Mehrfache Leerzeichen → einzelnes
.trim()
}Matching-Logik:
function fuzzyMatch(guess, correct, maxErrors = 3) {
const normalizedGuess = normalizeText(guess)
const normalizedCorrect = normalizeText(correct)
// 1. Exakter Match nach Normalisierung
if (normalizedGuess === normalizedCorrect) return true
// 2. Substring-Match
if (normalizedCorrect.includes(normalizedGuess) ||
normalizedGuess.includes(normalizedCorrect)) return true
// 3. Levenshtein-Distanz Check
const distance = levenshteinDistance(normalizedGuess, normalizedCorrect)
return distance <= maxErrors
}Performance:
- Normalisierung: ~0.1ms
- Levenshtein (20 Zeichen): ~0.2ms
- Gesamt pro Guess: ~0.5ms
- Vernachlässigbar für User Experience
Das Spiel speichert den Spielstand automatisch:
- ✅ Automatisch nach jedem Zug gespeichert
- ✅ LocalStorage-basiert - Persistent im Browser
- ✅ Wiederherstellung beim Neustart - Dialog fragt nach Fortsetzung
- ✅ Export/Import - JSON-Datei Download/Upload für Backups
- ✅ Geräte-Wechsel möglich - Exportiere auf Gerät A, importiere auf Gerät B
- ✅ Manueller Save-Button - Zusätzlich zur Auto-Save Funktion
Was wird gespeichert:
- Alle Spieler mit Namen, Timelines, Punkte, Karten
- Aktueller DJ und aktiver Spieler
- Spielmodus (Ratespiel, Timeline Personal, Timeline Global)
- Aktueller Song-State
- Kompletter Spielverlauf
Am einfachsten: Lade fertige Karten direkt von den GitHub Releases herunter!
- All-Cards (3MF) - Alle Karten in einer Datei
- STL Modelle (ZIP)
- SCAD Modelle (ZIP)
- Einzelne Modelle - Direkt auf GitHub
Schnell, einfach und kein 3D-Drucker nötig!
# Generiert alle 4 Varianten automatisch
./scripts/build/generate-all-pdfs.shErstellt:
card-generator/output/pdfs/mxster-cards.pdf- Standard (farbig, nebeneinander)card-generator/output/pdfs/mxster-cards-bw.pdf- Schwarz-Weiß (nebeneinander)card-generator/output/pdfs/mxster-cards-duplex.pdf- Duplex (farbig, getrennte Seiten)card-generator/output/pdfs/mxster-cards-bw-duplex.pdf- Duplex (Schwarz-Weiß, getrennte Seiten)
cd pwa
# Standard: Vorder-/Rückseite nebeneinander (Farbig)
node generate-cards.js
# Schwarz-Weiß (spart Tinte)
node generate-cards.js --bw
# Duplex: Für beidseitigen Druck
node generate-cards.js --duplex
# Duplex + Schwarz-Weiß
node generate-cards.js --duplex --bwDrucken:
- Drucke die PDF-Datei aus
- Nutze mindestens 200g/m² Papier oder Karton
- Schneide entlang der gestrichelten Linien
- Falten und zusammenkleben oder laminieren
- Fertig! 🎉
Vorderseite (QR-Code):
- mxster Logo oben
- QR-Code in der Mitte (Spotify Track URL)
- Anweisung: "Scanne den Code mit der mxster App"
- Song-ID am unteren Rand
Rückseite (Song-Information):
- Jahr groß und prominent (48pt)
- Song-Titel (14pt, fett)
- Artist (11pt)
- Dekorative Linie am unteren Rand
Abmessungen:
- Kartengröße: 2.5" x 3.5" (63mm x 88mm) - Standard Spielkartengröße
- Papierformat: A4 (210mm x 297mm)
- Karten pro Seite: 1 Karte (Vorderseite + Rückseite nebeneinander)
Das generierte PDF enthält:
- Titelseite mit Übersicht (Anzahl der Songs)
- Pro Song eine Seite mit:
- Vorderseite links (QR-Code)
- Rückseite rechts (Song-Info)
- Gestrichelte Schneidelinien
- Kartennummer (z.B. "Karte 1 von 143")
- Anleitungsseite mit Druckanweisungen
Materialien:
- Papier: Mindestens 200g/m² (Karton empfohlen)
- Drucker: Farblaserdrucker oder hochwertiger Tintenstrahldrucker
- Optional: Laminiergerät oder Selbstklebefolie
Schritte:
-
Drucken
- Drucke alle Seiten auf festem Papier (200-300g/m²)
- Empfehlung: Beidseitiger Druck für professionelles Aussehen
-
Schneiden
- Schneide entlang der gestrichelten Linien
- Verwende ein Schneidegerät für präzise Kanten
- Schneide zwischen Vorder- und Rückseite
-
Zusammenfügen (Optional)
- Klebe Vorderseite und Rückseite Rücken an Rücken zusammen
- Oder: Laminiere jede Seite separat und klebe sie dann zusammen
- Oder: Falte das Papier in der Mitte, wenn es dünn genug ist
-
Laminieren (Empfohlen)
- Laminiere die fertigen Karten für längere Haltbarkeit
- Schützt vor Abnutzung und Verschmutzung
Die Karten verwenden das mxster Design:
Vorderseite:
- Hintergrund:
#1A1C27(Dunkelgrau) - Border:
#4A90E2(Blau) - Text:
#FFFFFF(Weiß) /#B0B3C1(Hellgrau)
Rückseite:
- Hintergrund:
#2C2E3B(Dunkelgrau) - Border:
#FF6B35(Orange) - Jahr:
#4A90E2(Blau) - Titel:
#FFFFFF(Weiß) - Artist:
#B0B3C1(Hellgrau)
Dependencies:
pdfkit- PDF-Generierungqrcode- QR-Code Generierung
Song-Daten:
Die Karten werden aus src/data/songs.ts generiert. Jeder Song benötigt:
id- Eindeutige IDtitle- Song-Titelartist- Künstleryear- ErscheinungsjahrspotifyId- Spotify Track-ID für QR-Code
QR-Code Format: Der QR-Code enthält die Spotify Track URL im Format:
https://open.spotify.com/track/{spotifyId}
Ausgabe:
- Dateiname:
mxster-cards.pdf(oder*-bw.pdf,*-duplex.pdf) - Speicherort:
card-generator/output/pdfs/Verzeichnis - Dateigröße: ~50-100 KB pro Karte (abhängig von QR-Code Komplexität)
Kartengröße ändern:
Bearbeite die Konstanten in pwa/generate-cards.js:
const CARD_WIDTH = 2.5 * 72; // Breite in inches * 72 (points)
const CARD_HEIGHT = 3.5 * 72; // Höhe in inches * 72 (points)Farben ändern:
Passe die Farbwerte in den drawCardFront() und drawCardBack() Funktionen an.
Mehr Songs hinzufügen:
Füge neue Songs zu docs/songs.json hinzu und führe das Skript erneut aus.
PDF wird nicht generiert:
- Prüfe, ob alle Dependencies installiert sind:
npm install - Stelle sicher, dass Node.js installiert ist (Version 18+)
- Prüfe die Konsole auf Fehlermeldungen
QR-Codes funktionieren nicht:
- Stelle sicher, dass die
spotifyIdinsongs.jsonkorrekt ist - Teste den QR-Code mit einem Standard-QR-Reader
- Die URL sollte im Format
https://open.spotify.com/track/{id}sein
Schlechte Druckqualität:
- Verwende dickeres Papier (mindestens 200g/m²)
- Stelle den Drucker auf höchste Qualität ein
- Verwende einen Laserdrucker für beste Ergebnisse
Hochwertige, dauerhafte Karten mit graviertem QR-Code!
Voraussetzungen:
- 3D-Drucker
- OpenSCAD (optional, für STL-Export)
# Song hinzufügen (3D-Modelle werden automatisch erstellt)
node scripts/song-management/add-song.js "https://open.spotify.com/track/TRACK_ID"Druckeinstellungen:
- Material: PLA oder PETG
- Layer Height: 0.1-0.15mm (wichtig für QR-Details!)
- Infill: 100%
- Support: Nicht nötig
# Im Hauptverzeichnis
node scripts/song-management/add-song.js "https://open.spotify.com/track/DEINE_TRACK_ID"Was passiert automatisch:
- ✅ Metadaten von Spotify geladen
- ✅ Song-ID generiert
- ✅ Datenbank aktualisiert
- ✅ QR-Code erstellt
- ✅ PWA-Daten synchronisiert
Du hast einen Tippfehler oder möchtest Metadaten korrigieren? Nutze den Song-Editor:
# Im Hauptverzeichnis
node scripts/song-management/edit-song.jsDer Wizard führt dich durch:
- 🔍 Song-ID eingeben (z.B.
song_001oder einfach001/128) - 📋 Aktuelle Daten werden angezeigt
- ✏️ Neue Daten eingeben (leer lassen = Wert behalten)
- 👀 Vorher/Nachher-Vergleich ansehen
- ✅ Änderungen bestätigen
Flexibles ID-Format:
- ✅
song_128(vollständiges Format) - ✅
128(Kurzformat - wird automatisch zusong_128) - ✅
095(mit führender Null - wird zusong_095) - ✅
95(ohne führende Null - wird zusong_095)
Was passiert automatisch:
- ✅ Automatisches Backup erstellt (
songs.json.backup-2025-10-24) - ✅ Datenbank aktualisiert (
docs/songs.json) - ✅ PWA-Daten synchronisiert (
pwa/src/data/songs.ts) - ✅ Alte Dateien gelöscht (falls Titel/Artist geändert)
- ✅ Neue QR-Codes generiert
- ✅ Optional: 3D-Modelle neu erstellen (SCAD + STL)
Beispiel-Ablauf:
$ node edit-song.js
╔══════════════════════════════════════════╗
║ 🎵 mxster Song Editor Wizard 🎵 ║
╚══════════════════════════════════════════╝
📚 35 Songs in der Datenbank gefunden
Schritt 1/4: Song auswählen
─────────────────────────────
Song-ID (z.B. song_001): song_008
✅ Song gefunden:
Titel: Tell It to My Heart
Interpret: Taylor Dayne
Jahr: 1988
Spotify: 4u7EnebtmKWzUH433cf5Qv
Schritt 2/4: Neue Daten eingeben
─────────────────────────────────
(Leer lassen = Wert behalten)
Titel [Tell It to My Heart]: Tell It To My Heart
Interpret [Taylor Dayne]:
Jahr [1988]: 1987
Schritt 3/4: Änderungen bestätigen
───────────────────────────────────
Vorher:
Taylor Dayne - Tell It to My Heart (1988)
Nachher:
Taylor Dayne - Tell It To My Heart (1987)
Änderungen übernehmen? (j/n): j
Schritt 4/4: Dateien aktualisieren
───────────────────────────────────
✅ Backup erstellt: docs/songs.json.backup-2025-10-24
🗑️ 2 alte Dateien gelöscht
✅ songs.json aktualisiert
✅ pwa/src/data/songs.ts aktualisiert
🔄 Generiere QR-Code...
✅ QR-Code generiert
3D-Modelle neu generieren? (j/n): n
╔══════════════════════════════════════════╗
║ ✅ Fertig! ✅ ║
╚══════════════════════════════════════════╝
Aktualisierte Dateien:
• docs/songs.json
• pwa/src/data/songs.ts
• card-generator/output/qr-codes/song_008_*.pngDu möchtest einen Song komplett durch einen anderen ersetzen? Nutze den Song-Exchange-Wizard:
# Im Hauptverzeichnis
node scripts/song-management/exchange-song.jsDer Wizard führt dich durch:
- 🔍 Song-ID zum Ersetzen eingeben (z.B.
song_031) - 📋 Aktueller Song wird angezeigt
- 🎵 Neue Spotify URL/Track-ID eingeben
- 📥 Metadaten von Spotify werden automatisch geladen
- 👀 Vorher/Nachher-Vergleich ansehen
- ✅ Austausch bestätigen
Was passiert automatisch:
- ✅ Automatisches Backup erstellt
- ✅ Song-ID bleibt unverändert (nur Metadaten werden ersetzt)
- ✅ Alte Dateien gelöscht (QR, SCAD, STL)
- ✅ Datenbank aktualisiert (
docs/songs.json) - ✅ PWA-Daten synchronisiert (
pwa/src/data/songs.ts) - ✅ Neue Metadaten von Spotify geladen (Titel, Artist, Jahr, Album)
- ✅ Neue QR-Codes generiert (beide Verzeichnisse)
- ✅ Neue 3D-Modelle generiert (SCAD + STL)
- 🔧 Optional: Alle PDFs neu generieren
Beispiel-Ablauf:
$ node exchange-song.js
╔════════════════════════════════════════╗
║ 🔄 mxster Song Exchange Wizard 🔄 ║
╚════════════════════════════════════════╝
📚 83 Songs in der Datenbank gefunden
Schritt 1/5: Song auswählen zum Ersetzen
──────────────────────────────────────────
Song-ID zum Ersetzen (z.B. song_001): song_031
✅ Song gefunden (wird ersetzt):
Titel: Only You
Interpret: Steve Monite
Jahr: 1984
Spotify: 3d7lH2ppf2aIELQXY4nagn
Schritt 2/5: Neuen Spotify Track eingeben
─────────────────────────────────────────
Spotify URL oder Track-ID: https://open.spotify.com/track/NEW_TRACK_ID
Schritt 3/5: Metadaten von Spotify laden
─────────────────────────────────────────
🔍 Lade Track-Informationen von Spotify...
✅ Neuer Track gefunden:
Titel: Neuer Song
Interpret: Neuer Artist
Jahr: 2020
Album: Neues Album
Schritt 4/5: Austausch bestätigen
─────────────────────────────────────────
🔴 ALT (wird gelöscht):
Steve Monite - Only You (1984)
🟢 NEU (wird eingefügt):
Neuer Artist - Neuer Song (2020)
💡 Song-ID bleibt: song_031
Song austauschen? (j/n): j
Schritt 5/5: Dateien aktualisieren
─────────────────────────────────────────
🗑️ 6 alte Dateien gelöscht
✅ Backup erstellt: docs/songs.json.backup-2025-10-24
✅ songs.json aktualisiert
✅ pwa/src/data/songs.ts aktualisiert
🔄 Generiere Karten-Dateien (QR-Code + 3D-Modelle)...
✅ QR-Code generiert
✅ 3D-Modelle generiert (SCAD + STL)
PDF-Karten neu generieren (alle Songs)? (j/n): n
╔════════════════════════════════════════╗
║ ✅ Fertig! ✅ ║
╚════════════════════════════════════════╝
Aktualisierte Dateien:
• docs/songs.json
• pwa/src/data/songs.ts
• card-generator/output/qr-codes/song_031_*.png
• card-generator/output/models/song_031_*.scad
• card-generator/output/models/song_031_*.stl
📊 Song-Details:
Alt: Steve Monite - Only You (1984)
Neu: Neuer Artist - Neuer Song (2020)
ID: song_031 (unverändert)cd pwa
npm run import-spotifyVorher pwa/spotify.config.js anpassen (siehe Konfiguration oben).
mxster ist als Progressive Web App (PWA) installierbar und funktioniert wie eine native App!
- Öffne https://mxster.de in Safari
- Tippe auf den Teilen-Button (📤) unten in der Mitte
- Scrolle nach unten und wähle "Zum Home-Bildschirm"
- Bearbeite den Namen falls gewünscht (Standard: "mxster")
- Tippe auf "Hinzufügen" oben rechts
- Fertig! Die App erscheint auf deinem Home Screen
Nach Installation:
- App öffnet sich im Vollbild (ohne Browser-UI)
- Sieht aus wie eine native App
- Eigenes Icon auf dem Home Screen
- Öffne https://mxster.de in Chrome oder Edge
- Warte bis die Seite vollständig geladen ist
- Tippe auf das Menü (⋮) oben rechts
- Wähle "App installieren" oder "Zum Startbildschirm hinzufügen"
- Bestätige die Installation
- Fertig! Die App erscheint auf deinem Home Screen
Alternative Methode:
- Einige Browser zeigen automatisch ein Banner "App installieren" am unteren Bildschirmrand
- Tippe einfach auf "Installieren"
- Öffne https://mxster.de in Chrome oder Edge
- Klicke auf das ⊕ Symbol in der Adressleiste (rechts)
- Oder: Menü (⋮) → "mxster installieren..."
- Klicke auf "Installieren"
- Fertig! Die App öffnet sich in einem eigenen Fenster
Shortcut:
- Windows: App erscheint im Startmenü
- Mac: App erscheint im Applications-Ordner
- Linux: App erscheint in den Anwendungen
Nach erfolgreicher Installation:
Offline-Funktionalität:
- ✅ App-Grundgerüst funktioniert offline
- ✅ Cached Songs spielbar (wenn vorher geladen)
- ❌ Spotify Streaming benötigt Internet
Home Screen Icon:
- Eigenes mxster Icon
- Kein Browser-Tab mehr nötig
- Schnellzugriff vom Home Screen
Standalone Mode:
- Kein Browser-UI (keine Adressleiste)
- Mehr Bildschirmplatz
- Native App Feeling
Auto-Updates:
- PWA aktualisiert sich automatisch
- Service Worker lädt neue Versionen im Hintergrund
- Beim nächsten App-Start: Neue Version
"Installieren"-Button wird nicht angezeigt:
-
HTTPS erforderlich
- ✅ mxster.de nutzt HTTPS - sollte funktionieren
- Auf localhost funktioniert es auch (für Development)
-
Service Worker nicht registriert
- Öffne DevTools (F12)
- Gehe zu "Application" → "Service Workers"
- Sollte "mxster Service Worker" zeigen
-
Browser-Cache
- Hard Refresh: Ctrl+F5 (Windows) / Cmd+Shift+R (Mac)
- Oder: DevTools → "Application" → "Clear storage" → "Clear site data"
-
App bereits installiert
- Wenn die App bereits installiert ist, wird der Button nicht angezeigt
- Prüfe deinen Home Screen / Anwendungen
Installation auf iOS funktioniert nicht:
Checkliste:
- Safari Browser verwenden (nicht Chrome/Firefox)
- iOS 11.3 oder neuer
- Seite vollständig geladen
- Teilen-Button (📤) funktioniert
- "Zum Home-Bildschirm" ist verfügbar
App öffnet sich nicht im Vollbild:
- iOS: Stelle sicher, dass du die App vom Home Screen öffnest (nicht über Safari → Lesezeichen)
- Android: App sollte automatisch im Vollbild öffnen. Falls nicht: Deinstallieren und neu installieren
- Desktop: App sollte sich in eigenem Fenster öffnen. Falls nicht: Neu installieren
Um die App lokal oder in Production zu nutzen, musst du die Redirect URIs im Spotify Developer Dashboard eintragen.
Problem: INVALID_CLIENT: Invalid redirect URI Error beim Login
Lösung: Die App verwendet zur Laufzeit automatisch die korrekte Redirect URI:
- Production:
https://mxster.de/callback - Development:
http://localhost:5174/callback
Gehe zu: https://developer.spotify.com/dashboard
Verwende deinen Spotify Account
Finde und klicke auf deine App in der Dashboard-Übersicht (oder erstelle eine neue App)
Klicke auf den "Settings" Button (oben rechts)
Scrolle runter bis zu "Redirect URIs"
WICHTIG: Trage EXAKT diese URIs ein (ohne Leerzeichen oder Trailing Slash):
https://mxster.de/callback
http://localhost:5174/callback
Achtung:
- ❌ FALSCH:
https://mxster.de/callback/(mit Trailing Slash) - ❌ FALSCH:
https://www.mxster.de/callback(mit www) - ✅ RICHTIG:
https://mxster.de/callback
Klicke auf "Add" für jede URI, dann "Save" unten rechts
Warte ca. 1-2 Minuten, bis die Änderungen propagiert sind
- Öffne https://mxster.de
- Klicke auf "Mit Spotify anmelden"
- Der Login sollte jetzt funktionieren!
"redirect_uri mismatch":
- Die URI im Dashboard stimmt nicht mit der App überein
- Prüfe auf Tippfehler, Leerzeichen, Trailing Slashes
"INVALID_CLIENT":
- Client ID oder Secret falsch
- Redirect URI nicht im Dashboard eingetragen
App redirected zu falscher URL:
- Hard Refresh im Browser (Ctrl+F5)
- Service Worker Cache leeren (DevTools → Application → Clear Storage)
Was sendet die App? Die App sendet diese Redirect URI an Spotify:
// Production (mxster.de)
https://mxster.de/callback
// Development (localhost)
http://localhost:5174/callbackWie überprüfen?
- Öffne https://mxster.de
- Öffne Browser DevTools (F12)
- Gehe zu Network Tab
- Klicke "Mit Spotify anmelden"
- Schau dir die Request-URL an, die zu
accounts.spotify.com/authorizegeht - In der URL findest du:
&redirect_uri=https%3A%2F%2Fmxster.de%2Fcallback%3A%2F%2F=://%2F=/- Dekodiert:
https://mxster.de/callback
Erforderliche Scopes:
streaming(Web Playback SDK)user-read-email(User Info)user-read-private(User Info)user-modify-playback-state(Playback Control)user-read-playback-state(Playback State)
mxster/
├── docs/ # Source of Truth
│ ├── songs.json # Primary song database
│ ├── song_template.json
│ ├── songs_removed.json
│ └── *.png # QR code PNGs (in Git for docs)
├── card-generator/ # Card generation tools
│ ├── *.js # Generation scripts
│ ├── template.scad
│ └── output/ # All generated files
│ ├── qr-codes/ # Generated QR codes
│ ├── models/ # SCAD, STL, 3MF files
│ └── pdfs/ # Generated PDF cards
├── build/ # Release artifacts
│ └── archives/ # ZIP archives for releases
│ ├── mxster-scad-models.zip
│ ├── mxster-stl-models.zip
│ └── mxster-source.zip
├── pwa/ # Progressive Web App (React + Vite)
│ ├── src/
│ │ ├── main.tsx # App entry point
│ │ ├── components/ # React components
│ │ ├── utils/ # Spotify Auth, Game State, etc.
│ │ ├── data/songs.ts # Generated song data
│ │ └── styles/ # Tailwind CSS
│ ├── public/
│ └── package.json
└── scripts/ # Organized utility scripts
├── song-management/
│ ├── add-song.js # CLI: Add new song
│ ├── edit-song.js # CLI: Edit existing song
│ ├── exchange-song.js # CLI: Replace song
│ └── update-song-count.js # CLI: Update README
├── build/
│ ├── generate-all-pdfs.sh # Generate all PDF variants
│ └── update-and-release.sh # Build and release workflow
├── deployment/
│ └── deploy.sh # Deploy PWA to production
└── setup/
└── install_dependencies.sh # Install dependencies
# Development
cd pwa
npm run dev # Dev-Server starten (localhost:5174)
# Production
npm run build # Production Build
npm run preview # Build testen
# Songs verwalten
node scripts/song-management/add-song.js "SPOTIFY_URL" # Song hinzufügen
node scripts/song-management/edit-song.js # Song bearbeiten (Metadaten)
node scripts/song-management/exchange-song.js # Song austauschen (komplett)
npm run import-spotify # Aus Playlist
npm run update-previews # Preview URLs updaten
npm run filter-songs # Ungültige Songs entfernen
# Karten generieren
./scripts/build/generate-all-pdfs.sh # Alle 4 PDF-Varianten
node pwa/generate-cards.js # PDF Standard
node pwa/generate-cards.js --bw # PDF Schwarz-Weiß
node pwa/generate-cards.js --duplex # PDF Duplex
# Release erstellen (automatisch via GitHub Actions)
git tag v1.0.0 # Tag erstellen
git push origin v1.0.0 # Push löst CI/CD aus
# GitHub Actions generiert automatisch:
# - PDFs, STL-ZIP, SCAD-ZIP
# - GitHub Release mit all-cards.3mfDas Projekt enthält einen umfassenden Integritätstest, der die Qualität und Konsistenz aller Songs, generierten Dateien und Konfigurationen überprüft.
# Im Hauptverzeichnis
npm test
# Mit ausführlicher Ausgabe
npm run test:verboseBasis-Tests (1-10) - Laufen immer:
- ✅ Song-Datenbank-Integrität - Struktur, Pflichtfelder, Duplikate
- ✅ PWA-Sync -
pwa/src/data/songs.ts↔docs/songs.json - ✅ Song-IDs - Fortlaufende Nummerierung, keine Lücken
- ✅ Spotify IDs - Format-Validierung (22 Zeichen, alphanumerisch)
- ✅ Preview URLs - Gültige Spotify-URLs
⚠️ QR-Codes - Prüft Existenz (gitignored, Warning bei Fehlen)⚠️ 3D-Modelle - Prüft SCAD/STL (gitignored, Warning bei Fehlen)- ✅ Jahreszahlen - Plausibilität (1950-2030)
- ✅ PDF-Karten - Prüft Existenz (4 Varianten)
⚠️ Docs-Sync - QR-Code-Kopien indocs/(gitignored)
Erweiterte Tests (11-15) - Nur mit Abhängigkeiten: 11. 🔍 QR-Code Decodierung - Verifiziert Scanbarkeit (5 Sample-Songs) 12. 🌐 Spotify API - Validiert Track-IDs live (5 Sample-Songs) 13. 📐 Bild-Dimensionen - Prüft QR-Code-Größe (5 Sample-Songs) 14. 🎲 OpenSCAD Syntax - Validiert 3D-Modelle (5 Sample-Songs) 15. 📄 PDF-Generierung - Verifiziert PDF-Integrität (alle 4 Varianten)
Die Tests 11-15 benötigen optionale Abhängigkeiten:
# Im Hauptverzeichnis
npm install --save-dev jsqr pngjs sharp
# Für Test 14 (OpenSCAD):
# macOS: brew install openscad
# Ubuntu: sudo apt install openscad
# Windows: https://openscad.org/downloads.htmlOhne Installation: Tests werden übersprungen mit hilfreichen Hinweisen
- ⚡ Schnell: Sample-basiert (5 statt 179 Songs)
- 🔧 Flexibel: Erweiterte Tests sind optional
- 🤖 CI/CD-freundlich: Keine Fehler bei fehlenden Dependencies
- 📊 Informativ: Klare Warnungen vs. Fehler
✅ Success - Test bestanden, alles in Ordnung
Location: /scripts/testing/ - Browser-Console-Skripte zum Testen der Achievement-Animationen
Empfohlenes Test-Skript (test-animations-simple.js):
// In Browser DevTools Console kopieren
const unlockAchievement = (playerName, achievementId) => {
const event = new CustomEvent('test-achievement-unlock', {
detail: { playerName, achievementId }
})
window.dispatchEvent(event)
console.log(`🎯 Triggered: ${achievementId} for ${playerName}`)
}
// Teste einzelne Achievement-Freischaltung
unlockAchievement('TestPlayer', 'hardcore_champion')
// Teste mehrere Achievements nacheinander (mit 5s Verzögerung)
setTimeout(() => {
unlockAchievement('TestPlayer', 'time_traveler')
unlockAchievement('TestPlayer', 'perfectionist')
unlockAchievement('TestPlayer', 'lightning_fast')
unlockAchievement('TestPlayer', 'comeback_king')
}, 5000)Weitere Test-Skripte:
test-animations-simple.js- Empfohlen: Custom Event Methode (einfachste Variante)test-achievement-animations.js- Initialer Test-Ansatztest-achievement-animations-simple.js- Game History SetupTESTING-INSTRUCTIONS.md- Vollständige Anleitung
Wie testen?:
- Öffne https://mxster.de oder
npm run dev(localhost:5174) - Drücke F12 (DevTools öffnen)
- Gehe zu "Console" Tab
- Kopiere Code aus
scripts/testing/test-animations-simple.js - Füge Code ein und drücke Enter
- Beobachte die Achievement-Unlock-Animationen (3s pro Animation + 1s Pause)
Features der Animationen:
- 🎉 Konfetti-Effekt mit canvas-confetti
- ⏱️ 3 Sekunden Animation pro Achievement
- 📋 Queue-System für mehrere Achievements
- 🔁 Sequentielle Abarbeitung mit 1s Pause zwischen Animationen
- 🎨 Farbiger Achievement-Badge mit Icon und Beschreibung
- 🎵 Sound-Effekte (optional, falls implementiert)
Releases werden manuell mit GitHub CLI erstellt:
# 1. Assets lokal generieren
./scripts/build/generate-all-pdfs.sh
cd card-generator/output/models
zip -r ../../../build/archives/mxster-stl-models.zip *.stl
zip -r ../../../build/archives/mxster-scad-models.zip *.scad
cd ../../..
# 2. all-cards.3mf in PrusaSlicer erstellen (manuell)
# 3. all-cards.3mf zu Git hinzufügen
git add card-generator/output/models/all-cards.3mf
git commit -m "Update all-cards.3mf"
git push
# 4. Einmalig: GitHub CLI authentifizieren
gh auth login
# 5. Release erstellen mit allen Assets
gh release create v0.0.X-beta \
--title "Release v0.0.X-beta" \
--prerelease \
card-generator/output/pdfs/mxster-cards*.pdf \
build/archives/mxster-*.zip \
card-generator/output/models/all-cards.3mf
# 6. WICHTIG: Download-Links aktualisieren!
# Ersetze in folgenden Dateien "v0.0.X-beta" mit der neuen Version:
# - README.md (Zeilen 169-177)
# - pwa/src/components/LandingPage.js (Zeilen 259-307)- README.md: Alle URLs von
/download/v0.0.X-beta/auf neue Version ändern - pwa/src/components/LandingPage.js: Alle URLs von
/download/v0.0.X-beta/auf neue Version ändern - Build & Deploy:
cd pwa && npm run build && cd .. && ./deploy.sh - Commit & Push:
git add -A && git commit -m "Update download links to vX.X.X-beta" && git push
Warum /latest/ nicht funktioniert:
- Releases mit
--prereleaseFlag werden NICHT als "latest" erkannt - Nur full releases (ohne --prerelease) haben
/latest/Support - Daher: Immer spezifisches Tag verwenden (
/download/v0.0.X-beta/)
Was wird hochgeladen:
- ✅ mxster-cards.pdf (Standard, farbig) - aus
card-generator/output/pdfs/ - ✅ mxster-cards-bw.pdf (Schwarz-Weiß) - aus
card-generator/output/pdfs/ - ✅ mxster-cards-duplex.pdf (Duplex, farbig) - aus
card-generator/output/pdfs/ - ✅ mxster-cards-bw-duplex.pdf (Duplex, Schwarz-Weiß) - aus
card-generator/output/pdfs/ - ✅ mxster-stl-models.zip (alle STL Dateien) - aus
build/archives/ - ✅ mxster-scad-models.zip (alle SCAD Dateien) - aus
build/archives/ - ✅ all-cards.3mf (alle Karten für 3D-Drucker) - aus
card-generator/output/models/
Fügt neue Songs zur Datenbank hinzu. Lädt Metadaten automatisch von Spotify.
Modi:
Automatischer Modus (Standard):
node scripts/song-management/add-song.js "https://open.spotify.com/track/TRACK_ID"
node scripts/song-management/add-song.js "TRACK_ID" # Alternativ: Nur die IDVerwendet Spotify-Metadaten ohne Nachfrage (Titel, Artist, Jahr).
Interaktiver Modus (mit --edit):
node scripts/song-management/add-song.js --edit "https://open.spotify.com/track/TRACK_ID"Zeigt Spotify-Metadaten als Vorauswahl, erlaubt manuelle Anpassungen vor dem Speichern.
Features:
- 🎵 Automatischer Download von Spotify-Metadaten
- ✏️ Optionale manuelle Bearbeitung mit
--edit - 🔄 Generiert automatisch QR-Codes, SCAD und STL-Dateien
- 📝 Aktualisiert
songs.jsonundsongs.ts
Interaktiver Wizard zum Bearbeiten bestehender Songs. Ideal für Korrekturen und Updates.
Features:
- 🎨 Farbiger Terminal-Output mit Emojis
- 🔍 Zeigt aktuelle Song-Daten an
- ✏️ Inkrementelle Eingabe (nur ändern was nötig ist)
- 👀 Vorher/Nachher-Vergleich
- 💾 Automatisches Backup vor jeder Änderung
- 🗑️ Löscht alte Dateien bei Namensänderung
- 🔄 Regeneriert QR-Codes automatisch
- 🎲 Optionale 3D-Modell-Regenerierung
Verwendung:
node scripts/song-management/edit-song.js
# Folge den Anweisungen im WizardTechnische Details:
- Nutzt Node.js
readlinefür interaktive Prompts - Erstellt Backups mit Zeitstempel:
songs.json.backup-YYYY-MM-DD - Führt
generateCard.jsautomatisch aus für neue QR-Codes - Synchronisiert beide Datenbanken:
docs/songs.jsonundpwa/src/data/songs.ts - Säubert alte Dateien aus
docs/,qr-codes/undmodels/Verzeichnissen
- Frontend: Vite 5.0, React 19, TypeScript, Tailwind CSS
- Audio: Spotify Web Playback SDK + Howler.js (Fallback)
- PWA: vite-plugin-pwa, Service Worker, Offline-Support
- QR: qr-scanner (Browser-basiert)
- PDF: PDFKit
- 3D: OpenSCAD (optional für Karten), Three.js (für Hintergrund-Animationen)
Du hast Ideen, Verbesserungen oder Bugs gefunden?
- Issue erstellen: GitHub Issues
- Pull Request: Fork → Branch → Commit → PR
- Diskussion: GitHub Discussions
mxster ist komplett kostenlos und werbefrei. Wenn dir das Projekt gefällt:
- ⭐ Star auf GitHub - Zeige deine Unterstützung!
- 💬 Teile mxster - Erzähle deinen Freunden davon
- ☕ Spende - Unterstütze die Weiterentwicklung via PayPal
Jeder Beitrag hilft, mxster noch besser zu machen! ❤️
Dieses Projekt ist unter der MIT-Lizenz veröffentlicht. Siehe LICENSE für Details.
- Entwickelt von Martin Pfeffer
- QR-Scanner: nimiq/qr-scanner
- Audio: Howler.js & Spotify Web Playback SDK
- PDF: PDFKit
- 3D: OpenSCAD
- Spotify Premium erforderlich: Web Playback SDK funktioniert nur mit Premium
- HTTPS erforderlich: Kamera-Zugriff benötigt HTTPS (außer localhost)
- Browser-Kompatibilität: Chrome, Firefox, Edge, Safari (neueste Versionen)
🚀 OpenSCAD Performance Improvements
Upgraded all 3D card generation scripts to use modern Manifold backend for significantly faster STL generation:
- Performance: Added
--enable=manifold --enable=fast-csgflags to all OpenSCAD commands - Speed Boost: 2-5x faster STL/3MF generation depending on model complexity
- Requirements: OpenSCAD 2021.01+ (older versions fall back gracefully)
- Scripts Updated:
card-generator/generateCard.js- Standard full-size cardscard-generator/generateCard-xs.js- XS test cards (embossed)card-generator/generateCard-xs-v2.js- XS-V2 test cards (flat multi-color)test-integrity.js- Syntax validation
🎵 YouTube Download Integration
Added automatic song download and upload system for self-hosted audio:
- New Utility:
scripts/audio-hosting/download-single-song.js- Searches YouTube automatically:
ytsearch1:${artist} - ${title} - Downloads 128 kbps MP3 to
pwa/public/audio/ - Uploads to VPS via rsync
- Returns self-hosted URL:
https://mxster.de/audio/song_XXX.mp3
- Searches YouTube automatically:
- Integration: Automatic download/upload in
add-song.jsandexchange-song.js - Bug Fix: Fixed path resolution in
add-song.js(SONGS_JSON_PATH + PWA_SONGS_PATH now use properpath.join(__dirname, ...)) - Fallback: Gracefully falls back to Spotify preview if download fails
Impact: All new songs automatically get self-hosted audio, no manual intervention needed!
🎮 Retro-Style Cookie/LocalStorage Notice
Completely redesigned cookie banner in authentic 8-bit pixel style with improved legal compliance:
- 8-Bit Design: Green neon pixel borders, retro game aesthetics, "Press Start 2P" font
- Legal Improvements: Removed misleading "Ablehnen" button, single "Verstanden" button
- Clear Communication: Explains LocalStorage is technically necessary, no external data transmission
- Pixel Animations: Pulse, float, and bounce effects for authentic retro feel
- Icon: 💾 Floppy disk instead of 🍪 cookie (fitting for "storage")
Files modified: pwa/src/components/CookieBanner.tsx, pwa/src/styles/tailwind.css
📖 Improved In-App Documentation
Added comprehensive tie-breaking rules to the How To Play guide for better player understanding:
- Hardcore Mode: Added tie-breaking explanation (most cards → last finisher)
- Timeline Global: Clarified win condition + tie-breaking (last finisher → first in list)
- Timeline Personal: No changes needed (race to 10 cards)
Files modified: pwa/src/components/HowToPlayContent.tsx
🐛 Critical Logic Fix
Fixed incorrect tie-breaking logic in Timeline Global mode that attempted to use score as a tiebreaker despite Timeline Global having no scoring system. Simplified to: currentPlayer wins (finisher advantage) → first player in list wins.
Files modified: pwa/src/services/gameLogic.ts, pwa/src/services/__tests__/gameLogic.test.ts
🎨 Improved Interaction-Reactive Particle Animations
Made particle animation transitions equally smooth in both directions:
Changes:
- Before: Fast ramp-up (cubic easing), slow fade-out (0.3 multiplier)
- After: Symmetrically smooth transitions - both directions use 0.3 multiplier
- Removed asymmetric easing for rising intensity
- Both scroll start and scroll end now have same 1-2 second smooth transitions
Technical Details:
- File:
pwa/src/utils/interactionActivityTracker.ts(lines 114-120) - Rising intensity: Changed from
easedDelta * easingFactortoeasedDelta * 0.3 - Falling intensity: Already was
easedDelta * 0.3 - Result: Perfectly balanced, gentle transitions for all user interactions
User Experience:
- ✅ No harsh contrasts when scrolling starts
- ✅ Smooth 1-2 second delay for both directions
- ✅ Consistent feel across all interactions (mouse, touch, scroll)
- ✅ More predictable and pleasant animation behavior
📱 Completed Mobile Layout Fixes
Fixed all remaining custom dialogs that were still cut off on mobile:
Additional Dialogs Fixed:
- PasswordProtectionDialog - Self-hosted audio access dialog
- AchievementsDialog - Both main dialog and onboarding screen
- AchievementUnlockAnimation - Achievement unlock notifications
Implementation: Applied same mobile fixes to all custom dialogs:
- Added
pt-20 pb-6 px-4to backdrop containers - Updated
max-h-[90vh]tomax-h-[calc(100vh-12rem)] sm:max-h-[85vh] - Added
overflow-autowhere needed for scrollable content
Files Modified:
pwa/src/components/PasswordProtectionDialog.tsx(lines 55-56)pwa/src/components/AchievementsDialog.tsx(lines 48, 88, 93)pwa/src/components/AchievementUnlockAnimation.tsx(line 73)pwa/package.json,pwa/src/components/Sidebar.tsx(version 0.0.45)
Coverage:
- ✅ Modal.tsx (standard modals)
- ✅ PasswordProtectionDialog (audio access)
- ✅ AchievementsDialog (achievements + onboarding)
- ✅ AchievementUnlockAnimation (notifications)
- ✅ SettingsDialog (uses Modal.tsx, already fixed)
- ✅ All other dialogs (use Modal.tsx, already fixed)
📱 Fixed Critical Mobile Display Issues
Fixed dialogs being cut off at bottom and covered by ActionBar at top on mobile devices:
Problems Solved:
- Modal Headers Overlapping: Fixed ActionBar (fixed header) covering modal headers
- Bottom Content Cut-Off: Fixed modal buttons being hidden behind Android gesture navigation bar
- Viewport Height Issues: Fixed incorrect viewport height calculations on mobile browsers
Implementation:
1. Modal Positioning (Modal.tsx):
// Added top/bottom padding to modal container
className="... pt-20 pb-6" // 80px top (ActionBar), 24px bottom (safe area)
// Updated modal max-height for proper spacing
className="... max-h-[calc(100vh-12rem)] sm:max-h-[85vh] ..."
// 12rem = 192px (ActionBar + safe areas) on mobile
// 85vh on desktop (no mobile browser UI)2. Viewport Meta Tag (index.html):
<!-- Added viewport-fit=cover for safe area support -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">3. Safe Area CSS Support (tailwind.css):
/* Support for iOS notches and Android gesture bars */
@supports (padding: env(safe-area-inset-bottom)) {
.pb-safe {
padding-bottom: max(1.5rem, env(safe-area-inset-bottom)) !important;
}
.pt-safe {
padding-top: max(1rem, env(safe-area-inset-top)) !important;
}
.px-safe {
padding-left: max(1rem, env(safe-area-inset-left)) !important;
padding-right: max(1rem, env(safe-area-inset-right)) !important;
}
}Technical Details:
- ActionBar Height: 80px (py-4 padding + content)
- Modal Top Spacing:
pt-20(5rem = 80px) accounts for ActionBar - Modal Bottom Spacing:
pb-6(1.5rem = 24px) minimum safe area - Mobile Max-Height:
calc(100vh - 12rem)= viewport - 192px (ActionBar + safe areas) - Desktop Max-Height:
85vh(no mobile browser UI)
Browser Compatibility:
env(safe-area-inset-*): Chrome 69+, Safari 11.1+ (2018+)viewport-fit=cover: Safari 11+, Chrome 69+ (2018+)- Samsung S24 Ultra (Android 14, One UI 6) fully supported
Files Modified:
pwa/src/components/Modal.tsx(lines 108, 112)pwa/index.html(line 5)pwa/src/styles/tailwind.css(lines 669-684)pwa/package.json(version 0.0.44)pwa/src/components/Sidebar.tsx(version display)
Impact:
- ✅ Modal headers no longer covered by ActionBar
- ✅ Modal buttons fully visible above gesture bar
- ✅ Works on all Android/iOS devices with different screen configurations
- ✅ Proper handling of notches, cutouts, and gesture bars
✂️ Simplified Text Display
Removed "Punkten" for cleaner, shorter text:
- Before: "687/1000 Punkten bis Musikexperte"
- After: "687/1000 bis Musikexperte" ✅
📊 Visual Progress Bars & Absolute Numbers
Enhanced achievement progress display:
- Replaced percentages with absolute numbers: "687/1000 Punkten"
- Added yellow-amber gradient progress bars for visual feedback
- Example: 687 pts → "687/1000 Punkten bis Musikexperte (🎓)" + progress bar
🎯 Progressive Achievement Display
Fixed misleading progress indicator with staged achievement goals:
- < 1000 pts: Shows progress to "Musikexperte" (69% for 687 pts) ✅
- 1000-4999 pts: Shows "Musikexperte" achieved + progress to "Großmeister"
- ≥ 5000 pts: Shows both achievements completed
- More motivating short-term goals for new players
👑 GRAND_MASTER Achievement Progress Display
Player statistics now show total score with achievement progress:
- New "Gesamtpunkte" card shows total points earned across all games
- Progress indicator: "X% bis Großmeister" (target: 5000 points)
- Achievement badge: "💎 Großmeister erreicht!" when unlocked
- GRAND_MASTER achievement is fully functional and trackable
🔍 Enhanced Preview Logging
Improved console logging for better debugging:
- Preview player now logs release year:
✅ Preview loaded: Ring of Fire by Johnny Cash (1963) - Helps identify song context during testing
🏆 Bot Achievements
Added 3 new achievements for beating AI opponents:
- 🤖 Bot-Jäger: Beat 3 easy bots
- 🤖🎯 Bot-Bezwinger: Beat 3 medium bots
- 🤖🔥 Bot-Meister: Beat 3 hard bots
Cross-game tracking counts victories against each bot difficulty level. Requires Bot Player System (v0.0.32).
Total Achievements: 23
🔧 Comprehensive TypeScript Type Safety & Code Quality Improvements
TypeScript Fixes:
- Fixed GameMode enum usage in test files (string literals → enum values)
- Resolved unused imports and variables across 30+ files
- Fixed implicit any types in utility scripts and test files
- Corrected Toast and GlobalTimelineCard type exports
- Fixed TimelineDisplay GlobalTimelineCard import
- Resolved UIContext Toast type references
- Fixed React namespace imports (removed unused React imports)
- Added proper type annotations to spotifyImport utility
- Fixed error handling types (catch blocks with any)
Component Fixes:
- ActionBar.tsx: Removed unused gameMode, variant type correction
- GameEndStatsDialog.tsx: Removed unused index parameter
- HowToPlayContent.tsx: JSX.Element → ReactElement
- Modal.tsx: Unused event parameter prefix
- SettingsDialog.tsx: Removed unused destructuring
- Statistics.ts: Fixed scoreDistribution keyof type, HistoryEntry property access
- WinnerScreen.ts: Added explicit any types for parameters
Test Infrastructure:
- botGameFlow.test.tsx: Removed unused mock declarations
- audioPlayer.integration.test.ts: Fixed Promise return types
- botPlayer.test.ts: Fixed GameMode type usage
- gameLogic.test.ts: Fixed GameMode type usage
Files Modified:
pwa/src/types/index.ts- Type exportspwa/src/contexts/UIContext.tsx,AuthContext.tsx,AppProviders.tsx- React importspwa/src/components/*.tsx- 10+ component filespwa/src/services/__tests__/*.ts- 5 test filespwa/src/utils/spotifyImport.ts- Type annotationspwa/package.json- Version bump to 0.0.37
Impact:
- ✅ Reduced TypeScript errors from ~80 to ~30 (remaining are test warnings)
- ✅ Improved type safety across entire codebase
- ✅ Better code quality and maintainability
- ✅ All production code properly typed
🔄 CI/CD Verification & Test Suite Update
Purpose:
- Verification release to validate all TypeScript fixes from v0.0.35
- Confirms production deployment matches CI/CD build expectations
- Validates test infrastructure improvements
Status:
- ✅ All critical TypeScript errors resolved (GameContext null types, test mocks)
- ✅ Production build deploys successfully to mxster.de
- ✅ Test suite passes with 41/41 tests
- ℹ️ Remaining non-critical warnings in CI output (unused variables, test utility types)
Files Modified:
pwa/package.json- Version bump to 0.0.36README.md- Changelog updateCLAUDE.md- Documentation update
🔧 Critical TypeScript Fixes for CI/CD
GameContext Null Value Support:
- Fixed
GameActiontypes to acceptGameMode | nullandGameVariant | null - Ensures proper state reset when navigating between screens
- Resolves "Type 'null' is not assignable to type 'GameMode'" errors
Test Infrastructure:
- Fixed TypeScript error in
test/setup.tsfor spotify config mock import - Added
as anytype assertion to handle dynamic mock imports - Resolves "implicitly has an 'any' type" error in CI/CD pipeline
Files Modified:
pwa/src/contexts/GameContext.tsx- GameAction type updatespwa/src/test/setup.ts- Mock import type assertionpwa/package.json- Version bump to 0.0.35
Impact:
- ✅ Fixes critical TypeScript errors preventing CI/CD builds
- ✅ Maintains type safety while allowing null state resets
- ✅ Improves test reliability across environments
🚀 Production Deployment & CI/CD Verification
Deployment:
- Successfully deployed v0.0.33 fixes to production (mxster.de)
- Verified all TypeScript errors resolved in production build
- Confirmed test infrastructure improvements working in live environment
Status:
- ✅ Live on https://mxster.de
- ✅ Build: 5.55s (optimized)
- ✅ Bundle: 1.05 MB (gzipped: 284 KB)
- ✅ Audio: 209 songs preserved during deployment
- ✅ CI/CD: Awaiting pipeline verification on latest commit
🔧 TypeScript Type Safety & Test Infrastructure
Critical Bug Fixes:
- Fixed Vitest dynamic import resolution for
spotify.config.jsduring test execution- Added path alias in
vitest.config.tsto map../../spotify.config.js→spotify.config.mock.js - Resolved "Failed to resolve import" error in CI/CD pipeline
- Added path alias in
- Fixed multiple TypeScript errors in
GameScreen.tsx:- Type mismatch:
Player | nullvstypeof players[0]inshowWinnerModal() - Missing imports: Added
Playertype from@/types - Missing context methods: Added
setGameModeandsetGameVarianttouseGame()destructuring - Nullish coalescing: Fixed
string | undefinedvsstring | nullforlastBotSongRef - Removed unused beat sync state and handler to eliminate
SpotifyPlayerStatevsPlayerStateconflict
- Type mismatch:
Type System Improvements:
- Updated
GameContext.tsxinterface to acceptGameMode | nullandGameVariant | nullin setter functions - Ensures game state can be properly reset when navigating away from game screen
Test Results:
- ✅ All 41/41 audio player integration tests passing
- ✅ Zero TypeScript errors in
GameScreen.tsx - ✅ CI/CD pipeline stable
Files Modified:
pwa/vitest.config.ts- Added spotify config path aliaspwa/src/screens/GameScreen.tsx- Type fixes, removed unused codepwa/src/contexts/GameContext.tsx- Updated setter signatures
📊 Genre Statistics & Landing Page UX Overhaul
🎨 Landing Page - Spotify API Transparenz & Slot Management:
- Spotify API Hürden-Erklärung: Ausführliche Dokumentation der Spotify Development Mode Limitierung
- Development Mode: Max 25 authentifizierte Nutzer
- Extended Quota Mode: Erfordert registrierte Firma + 250.000+ aktive Nutzer
- Indie-Sperre: Seit Mai 2025 keine Anträge von Einzelpersonen möglich
- "Absurd"-Hinweis: Kein organisches Wachstum von 25 auf 250.000 möglich
- YDL Rechtlicher Hinweis: Umfassende Disclaimer zur rechtlichen Grauzone
- Orange Warning Box auf Landing Page
- Erklärung in PasswordProtectionDialog
- Hinweis auf private, nicht-kommerzielle, bildungsbasierte Nutzung
- Passwortschutz als rechtliche Absicherung für Entwickler
- Spotify Slot Management System:
- Neue Datei:
pwa/spotify.slots.json- Zentrale Konfiguration für verfügbare Slots - Dynamischer Slot-Counter: "Noch 20 von 25 Slots frei"
- Pre-filled mailto: Link zum Anfordern von Spotify-Zugang
- User muss E-Mail-Adresse des Spotify-Kontos senden (für Developer Dashboard)
- Contact Email: [email protected]
- Neue Datei:
📊 Genre Statistics in Player Stats:
- Added
bestGenreandgenreStatstracking to player statistics - New "Lieblings-Genre" stats card (pink theme, 🎸 icon)
- Genre distribution chart with color-coded bars
- Pink-orange gradient for favorite genre
- Cyan-blue gradient for other genres
- Sorted by play count (descending)
- Truncated names with tooltip support
Implementation:
- Parallel genre analysis alongside decade statistics
- Safe null-checking for songs without genre field
- Visual bars show relative percentages
🧪 CI/CD Improvements:
- Fixed GitHub Actions test failures
- Created
spotify.config.mock.jsfor test environment - Updated test setup to mock gitignored config file
- All 41/41 integration tests now pass in CI
Files Modified:
pwa/spotify.slots.json(new) - Slot configurationpwa/src/screens/LandingPage.tsx(+150 lines) - API explanations, slot management, mailto: linkpwa/src/components/PasswordProtectionDialog.tsx(+15 lines) - YDL grey area explanationpwa/src/components/PlayerStatsDialog.tsx(+59 lines) - Genre statisticspwa/spotify.config.mock.js(new) - Test mockpwa/src/test/setup.ts(updated) - Mock configuration
🎯 Sidebar Navigation - 3-State System Overhaul
Complete Restructure:
- Implemented context-aware navigation with 3 distinct states (Landing/Setup/Game)
- Consolidated duplicate warning logic into single parameterized function
- Progressive disclosure - only show relevant options for current context
Navigation States:
- Landing Page - 6 buttons (Anleitung + 5 scroll sections)
- Setup Phase - 2 buttons (Anleitung + Zurück zum Menü with warning)
- In Game - 2 buttons (Hilfe + Spiel beenden with red highlighting)
Improvements:
- ✅ State-based logic using
location.pathname+isGameStarted - ✅ Context-aware button labels ("Anleitung" → "Hilfe" in game)
- ✅ Smart warnings only when data would be lost
- ✅ Red destructive action styling for "Spiel beenden"
- ✅ Removed redundant "Startseite" button during games
- ✅ Hidden scroll buttons during active gameplay
- ✅ Added warnings when leaving setup phase with configured players
Technical:
- File:
pwa/src/components/Sidebar.tsx(complete rewrite, 372 lines) - Reduced code duplication with unified
showNavigationWarning()function - Conditional rendering for 3 distinct menu structures
- Improved UX with focused, clutter-free navigation
🎵 Hybrid Audio System - Spotify 25-User Limit Lösung
Problem: Seit Mai 2025 limitiert Spotify alle Indie-Developer-Apps auf maximal 25 authentifizierte Nutzer im Development Mode. Extended Quota Mode ist nur für registrierte Firmen mit 250.000+ aktiven Nutzern verfügbar.
Lösung: Implementierung eines Hybrid Audio Systems mit zwei Modi:
Audio-Modi:
-
🎵 Preview-Modus (Gratis) - Standard für alle Nutzer:
- 30-Sekunden-Clips von Spotify
- Kein Login erforderlich
- Unbegrenzte Spieler
- Ausreichend für Quiz-Gameplay
-
🎧 Spotify Premium - Optional für VIP-Nutzer (max. 25):
- Volle Song-Wiedergabe
- Hochauflösendes Audio
- Spotify Premium Account erforderlich
- Begrenzt auf 25 Spieler (Development Mode)
⚠️ Wichtig: Der Entwickler muss jeden Spieler manuell in der Spotify Developer Console unter "Users and Access" hinzufügen. Nur 25 Slots verfügbar.
Technische Implementation:
PreviewPlayerService.ts- Howler.js-basierter Player für 30s ClipsMusicPlayerService.ts- Abstraktionsschicht mit automatischem FallbackLandingPage.tsx- Zwei-Button-Auswahl mit VergleichstabelleMusicPlayer.tsx- Mode-Badges und unified Player-InterfaceGameScreen.tsx- Transparente Integration ohne Gameplay-Änderungen
User Experience:
- Klarer Mode-Indikator im Player (🎧 Spotify Premium / 🎵 Preview 30s)
- Automatischer Fallback bei Spotify-Fehlern
- localStorage-basierte Präferenz-Speicherung
- Keine Änderungen am Gameplay erforderlich
Dependencies:
[email protected]- HTML5 Audio Player für Preview-Clips- Bestehende Spotify Web Playback SDK Integration beibehalten
🎵 Self-Hosted Audio System
Complete Self-Hosting Infrastructure:
- ✅ Full Song Downloads: 209 songs (128 kbps MP3, ~933 MB total) via yt-dlp
- ✅ VPS Integration: Automated upload to https://mxster.de/audio/
- ✅ URL Management: Auto-update of previewUrl fields in songs.json and songs.ts
- ✅ Validation: HTTPS accessibility checks for all 209 audio files
- ✅ nginx Configuration: Optimized audio serving with CORS, caching, and range requests
New Scripts (scripts/audio-hosting/):
download-songs.js- Downloads full songs from YouTube (128 kbps MP3)- Options:
--limit N(test mode),--resume(skip existing files) - Features: 3 concurrent downloads, 3 retries per song, batch processing
- Output: ~3.5 MB per song average, progress tracking
- Options:
upload-to-vps.js- Uploads MP3s to VPS via rsync- Options:
--dry-run(preview changes) - Features: Incremental sync, permission setting, nginx verification
- Options:
update-song-urls.js- Updates previewUrl in songs.json and songs.ts- Options:
--dry-run(preview changes) - Features: Auto-backup, 100% coverage verification
- Options:
validate-audio.js- Validates HTTPS accessibility- Options:
--full(all 209 songs), default: 10 sample songs - Features: HTTP status, content-type, file size checks
- Options:
Benefits:
- 🌟 Unlimited Users: No 25-user Development Mode limit
- 🎵 Full Songs: 3-5 minute tracks instead of 30-second previews
- 🔓 No Auth Required: Direct audio playback without Spotify login
- 💾 Offline-Ready: PWA can cache songs for offline playback
Technical Details:
- Average Song Size: 4.47 MB (128 kbps MP3)
- Total Storage: 933.43 MB on VPS
- Quality: 128 kbps MP3 (balance of size vs quality)
- nginx: Range requests enabled for seeking, 1-year cache, CORS enabled
🐛 Bug Fixes
- Fixed random start position feature for self-hosted audio (now works in preview mode)
- Fixed Timeline Global win condition (now counts total cards across all players, winner = most cards after 10 total)
- Fixed game end dialog not closing when clicking "Zur Startseite" or "Neue Runde"
🧪 Tests
- Added comprehensive tests for Timeline Global win condition
- All 64 unit tests passing
- HTTP/2 compatibility fix in validation script
🎨 Visual Feedback & UX Improvements
New Animations:
- ✨ Timeline Placement Animations:
- ✅ Correct placement → 3 randomized confetti patterns (Center Burst, Side Cannons, Spiral)
- ❌ Wrong placement → Thunder/rain animation with lightning bolts and screen shake
- 🏆 Hardcore Mode Guess Animations:
- 15 points (Perfect) → Gold confetti + "PERFECT!" overlay + golden glow
- 10-14 points (Great) → Silver confetti + "GREAT!" overlay + cyan glow
- 5-9 points (Good) → Green particles + "NICE!" overlay + green glow
- 1-4 points (Partial) → Yellow sparks + "+X" points display + weak yellow glow
- 0 points (Wrong) → Falling ❌ symbols + screen shake + red flash
- 🎲 Full Randomization: Colors, particle counts, timing, and intensity all randomized
- 📱 Mobile Optimized: 50% fewer particles on mobile devices
- ♿ Accessibility:
prefers-reduced-motionsupport
UI Improvements:
- 📏 Enlarged Placement Buttons: Timeline placement dialog text now 2-3x larger (
text-2xl md:text-3xl) - ⌨️ Keyboard Shortcuts: Press Enter or ESC to close evaluation and placement result dialogs (desktop/laptop)
Technical:
- New Files:
placementAnimations.ts,guessAnimations.ts,animationHelpers.ts,animations.css - Dependencies: Uses existing
canvas-confettilibrary - Build Size: +7 KB (animations + CSS keyframes)
📱 Fixed All Custom Dialogs on Mobile
- Fixed PasswordProtectionDialog (audio access)
- Fixed AchievementsDialog (main + onboarding)
- Fixed AchievementUnlockAnimation (notifications)
- Applied pt-20 pb-6 px-4 spacing to all custom dialogs
- Updated max-height to calc(100vh-12rem) on mobile
- Complete mobile dialog coverage
📱 Mobile Layout Fix for Samsung S24 Ultra
- Fixed modal dialogs being cut off at bottom (Android gesture bar)
- Fixed modal headers being covered by ActionBar at top
- Added viewport-fit=cover for safe area support (notches/cutouts)
- Added CSS safe area support with env() for iOS/Android
- Modal top spacing: pt-20 (80px for ActionBar)
- Modal bottom spacing: pb-6 (24px minimum safe area)
- Mobile max-height: calc(100vh-12rem) accounts for browser UI
- Desktop max-height: 85vh (no mobile browser chrome)
- Files: Modal.tsx, index.html, tailwind.css
✂️ Simplified Achievement Progress Text
- Removed "Punkten" from progress display
- Changed "687/1000 Punkten bis Musikexperte" to "687/1000 bis Musikexperte"
- Cleaner, more concise UI text
- File: PlayerStatsDialog.tsx
📊 Visual Progress Bars for Achievements
- Added absolute numbers instead of percentages (687/1000 vs 69%)
- Added yellow-amber gradient progress bars
- 2px height, smooth transitions, proper borders
- File: PlayerStatsDialog.tsx
🎯 Progressive Achievement Display
- 3-state system: < 1000 (MUSIC_EXPERT), 1000-4999 (GRAND_MASTER), ≥ 5000 (both)
- Fixed misleading "14% bis Großmeister" for low scores
- More motivating short-term goals
- File: PlayerStatsDialog.tsx
👑 Total Score Display with GRAND_MASTER Progress
- Added Gesamtpunkte card in player stats
- Shows progress to GRAND_MASTER achievement (5000 points)
- Yellow-gold theme with crown emoji
- File: PlayerStatsDialog.tsx
🔍 Enhanced Preview Logging
- Added release year to preview player log statements
- "Ring of Fire by Johnny Cash (1963)"
- File: PreviewPlayerService.ts
🏆 Bot Achievement Tracking
- 3 new achievements: BOT_SLAYER_EASY, BOT_SLAYER_MEDIUM, BOT_SLAYER_HARD
- Track victories against AI opponents by difficulty level
- Target: 3 wins each
- Files: achievements.ts, AchievementContext.tsx
🏆 Achievement System - Critical Bug Fixes
- Fixed 3 critical achievement detection bugs identified through code analysis
- NAME_DROPPER: Added
guessDetailsinterface to accurately track artist correctness (can't use points alone) - PERFECT_STREAK: Implemented dual verification (points OR guessDetails) for reliability
- COMEBACK_PROFI: Improved comeback detection logic (checks fewer cards OR came from behind)
🧪 Test Scripts - Complete Overhaul
- generate-game-history.js: Now includes all 20 achievements (was missing 10 new ones)
- GRAND_MASTER now unlocks correctly with 5000+ points
- Player "m" reaches ~35,000 points in 1000 games
- All new achievements properly tracked
- unlock-all-achievements.js: Updated to all 20 achievements
- Player "m": 20/20 unlocked (100%)
- Player "n": 10/20 unlocked with progress (50%)
🔧 Technical Improvements
- Added
guessDetailsto Song interface for accurate field tracking - Updated
placeCardInTimeline()to store guess metadata - Enhanced achievement debugging with progress logs after each game
- All 20 achievements now fully functional and tested
🔧 TypeScript & Code Quality
- Fixed all TypeScript errors in AchievementContext, GameContext, and GameScreen
- Migrated from GameHistory class to useGameHistory hook
- Fixed modal button interface (removed invalid properties: label, variant, closeOnClick)
- Added proper type annotations for all implicit
anytypes - Removed unused variables and imports
- Fixed SaveGameData structure for game history persistence
- Improved code maintainability and type safety
🐛 Bug Fixes
- Fixed double winner dialog in Timeline modes
- Fixed win condition check timing with proper state updates
- Fixed Player type issues in winner modal
- Fixed history data structure (Array vs Object with .games property)
📊 Analysis Tools
- Added decade distribution report generator (
scripts/analysis/generate-decade-report.js) - Interactive HTML visualization showing song distribution across decades
- Styled with celox design system for consistent branding
🎨 UI Improvements
- All modals now use consistent button structure
- Improved error handling in game history operations
- Better TypeScript IntelliSense support across the app
- Website: mxster.de
- GitHub: @pepperonas
- Email: [email protected]
Made with ❤️ for music lovers | © 2025 Martin Pfeffer
