You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Application web multi-joueur pour organiser des soirées musicales interactives. Les participants scannent un QR code, rejoignent une session, et la roue de la fortune sélectionne aléatoirement les chansons de la playlist Spotify.
✨ Fonctionnalités
Fonctionnalité
Description
Roue de la Fortune
Sélection aléatoire de chansons tour par tour
Mode Quiz
Deviner le titre/l'artiste d'un extrait musical
Mode Jukebox
Les invités votent pour la prochaine chanson
Connexion Spotify
Lecture en temps réel via le Web Playback SDK
Multi-joueur
Rejoindre via QR code, jusqu'à 50+ participants
Persistance
Sessions sauvegardées localement (pas de perte au refresh)
Tunnel permanent
URL fixe via Tailscale Funnel — jamais de changement
Active le Web Playback SDK dans les paramètres de l'app Spotify.
3. Lancer en production
# Build + copie frontendcd frontend && npm run build:backend
# Lancer le backendcd ../backend
PUBLIC_URL="https://the-chosen-1ne-1.taile64e86.ts.net" NODE_ENV=production node src/index.js
4. Exposer via tunnel permanent
Avec Tailscale Funnel (recommandé — URL permanente) :
tailscale funnel 3001
L'app est accessible sur : https://<machine>.<tailnet>.ts.net/
Avec cloudflared (temporaire — URL change à chaque restart) :
cloudflared tunnel --url http://localhost:3001
📱 Routes Web
Route
Description
/
Création de session (écran hôte)
/host/:sessionId
Tableau de bord hôte (QR code, playlist, Spotify)
/join/:sessionId
Inscription invité (2 steps : nom + préférences)
/guest/:sessionId
Vue invité (participer à la roue/quiz/jukebox)
🔌 API REST
Méthode
Endpoint
Description
GET
/api/health
Health check
GET
/api/config/url
Retourne l'URL publique du QR code
GET
/api/spotify/login?session={id}
Redirige vers l'auth Spotify (démarre OAuth)
GET
/api/spotify/callback?code=...&state=...
Callback OAuth Spotify (redirige vers le dashboard)
POST
/api/spotify/token
{ sessionId } → { access_token } pour Web Playback SDK
📡 Events Socket.IO
Host → Serveur
Event
Payload
Description
host:create-session
{ password, hostName }
Crée une session, retourne { sessionId }
host:rejoin-session
{ sessionId }
Rejoindre la room après refresh
host:spotify-device
{ deviceId }
Enregistrer le device Spotify
host:start-evening
——
Passer en mode quiz (soirée)
host:start-jukebox
——
Passer en mode jukebox
host:next-track
——
Passer à la chanson suivante
host:play-track
{ trackUri, positionMs }
Lancer une chanson sur Spotify
Guest → Serveur
Event
Payload
Description
guest:join
{ sessionId, username, password }
Rejoindre une session
guest:vote
{ trackId, vote }
Voter pour une chanson (jukebox)
guest:spin
——
Actionner la roue
Serveur → Host/Guest
Event
Payload
Description
game:state-update
{ session, players, mode, ... }
Mise à jour de l'état de la session
spotify:device-ready
——
Le device Spotify est prêt
🔐 Connexion Spotify (flux détaillé)
1. Host clique "Connexion Spotify"
→ GET /api/spotify/login?session={sessionId}
→ Redirection vers accounts.spotify.com/authorize
2. Utilisateur autorise l'application
→ Spotify redirige vers {redirect_uri}?code=...&state={sessionId}
→ Le callback reçoit le code, l'échange contre un refresh token
→ Token stocké dans une Map (sessionId → refreshToken)
→ Redirection vers /host/{sessionId}?spotify=connected
3. Le dashboard détecte ?spotify=connected
→ Charge le Spotify Web Playback SDK
→ SDK appelle POST /api/spotify/token avec sessionId
→ Le backend échange le refresh token contre un access token court
→ Player initialisé, écoute l'event 'ready'
4. Device enregistré sur le socket
→ host:spotify-device envoie le deviceId au backend
→ La lecture des chansons se fait via l'API Spotify Connect
🎮 Modes de jeu
Mode Quiz (Soirée)
Une chanson aléatoire est jouée
Les invités doivent deviner le titre ou l'artiste
Points attribués selon la rapidité
Mode Jukebox
Les invités ajoutent des chansons à la file d'attente
Vote collectif pour la prochaine chanson
La roue choisit aléatoirement si égalité
🗄️ Persistance des données
Couche
Description
Mémoire
Sessions actives dans une Map JavaScript (rapide, volatil)
Fichier JSON
backend/data/session-*.json — sauvegardé à chaque join/rejoin
Supabase
Optionnel — si SUPABASE_URL et SUPABASE_ANON_KEY sont configurés
Comportement : Si le serveur redémarre, les sessions sont restaurées depuis les fichiers JSON. Si Supabase n'est pas configuré, tout fonctionne en fallback local.
🔧 Tunnel permanent (Tailscale Funnel)
Pourquoi : Spotify OAuth nécessite une URL de callback fixe. cloudflared génère une nouvelle URL à chaque lancement → le callback Spotify casse.
Solution : Tailscale Funnel donne une URL HTTPS permanente.
Installation
# Installer Tailscale
winget install Tailscale.Tailscale
# Ou depuis tailscale.com/download# Connecter (s'ouvre dans le navigateur)"C:\Program Files\Tailscale\tailscale.exe" up
# Activer Funnel (1 clic sur le lien qui s'ouvre)"C:\Program Files\Tailscale\tailscale.exe" funnel 3001
Résultat
https://<machine-name>.<tailnet-id>.ts.net/ ← PERMANENT, ne change jamais
🛠️ Commandes utiles
Backend
cd backend
npm install # Installer les dépendances
node src/index.js # Lancer en dev (require .env)
NODE_ENV=production node src/index.js # Lancer en production
Frontend
cd frontend
npm install # Installer les dépendances
npm run dev # Dev avec HMR (sur :5173)
npm run build # Build uniquement
npm run build:backend # Build + copie dans backend/public/