# 🎾 Tennis Roots Overlay per streaming tennis (OBS) con controller remoto via WebSocket. ## Architettura ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Controller (admin) │────────────────────→│ β”‚ β”‚ /controller/ │←── STATE_UPDATE ───│ Express + WebSocket β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Server (hub) β”‚ β”‚ Port 3000 β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” WebSocket β”‚ β”‚ β”‚ Scoreboard (OBS) │←── STATE_UPDATE ───│ β”‚ β”‚ Match Info (OBS) │←── STATE_UPDATE ───│ β”‚ β”‚ Clock (OBS) │←── STATE_UPDATE ───│ β”‚ β”‚ Server Ind. (OBS) │←── STATE_UPDATE ───│ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Quick Start ```bash cd server npm install npm start ``` Apri nel browser: - **Controller**: http://localhost:3000/ - **Scoreboard**: http://localhost:3000/overlay/scoreboard.html - **Match info**: http://localhost:3000/overlay/match-info.html - **Clock**: http://localhost:3000/overlay/clock.html - **Server indicator**: http://localhost:3000/overlay/server-indicator.html ## Widget Overlay (per OBS) Ogni widget Γ¨ un file HTML indipendente, dimensionabile e posizionabile liberamente in OBS come **Browser Source**. | Widget | URL | Dimensioni suggerite | |--------|-----|---------------------| | **Scoreboard** | `/overlay/scoreboard.html` | 820Γ—130 px | | **Match Info** | `/overlay/match-info.html` | 600Γ—50 px | | **Clock** | `/overlay/clock.html` | 150Γ—50 px | | **Server Indicator** | `/overlay/server-indicator.html` | 250Γ—50 px | ### Setup in OBS 1. Aggiungi **Browser Source** 2. Inserisci URL del widget (es. `http://localhost:3000/overlay/scoreboard.html`) 3. Imposta larghezza/altezza come da tabella 4. **Importante**: spunta "Controlla audio/visibilitΓ  del browser nella sorgente" 5. Usa filtri di ritaglio/posizionamento per sistemare l'overlay ## Controller Il pannello di controllo admin Γ¨ accessibile a `/controller/`. ### Sezioni - **Stato corrente**: live view del match - **Punteggio**: pulsanti punto P1/P2, Deuce, Reset Game/Match - **Servizio**: cambio battuta manuale - **Giocatori**: modifica nomi e bandiere - **Info Match**: torneo e round ### Scorciatoie da tastiera | Tasto | Azione | |-------|--------| | `1` | Punto P1 | | `2` | Punto P2 | | `D` | Deuce | | `R` | Reset Game | | `M` | Reset Match | ## API HTTP ```bash # Stato corrente curl http://localhost:3000/api/state # Aggiornamento parziale curl -X POST http://localhost:3000/api/state \ -H 'Content-Type: application/json' \ -d '{"player1":"N. Djokovic","tournament":"US Open"}' # Comando curl -X POST http://localhost:3000/api/command \ -H 'Content-Type: application/json' \ -d '{"command":"ADD_POINT","player":1}' ``` ## WebSocket I widget si connettono automaticamente a `ws://localhost:3000/ws`. Il server propaga gli aggiornamenti a tutti i client connessi. ### Formato messaggi **Da controller a server:** ```json { "type": "COMMAND", "command": "ADD_POINT", "player": 1 } { "type": "COMMAND", "command": "SET_DEUCE" } { "type": "COMMAND", "command": "RESET_GAME" } { "type": "COMMAND", "command": "RESET_MATCH" } { "type": "COMMAND", "command": "SET_SERVER", "player": 2 } { "type": "STATE_UPDATE", "state": { "player1": "...", ... } } ``` **Da server a tutti:** ```json { "type": "STATE_UPDATE", "state": { ... } } ``` ## Sviluppo ```bash cd server npm run dev # node --watch ``` ## Deploy su VPS (Docker + Traefik) ### Prerequisiti - Docker e Docker Compose - Rete `traefik-public` giΓ  esistente (`docker network create traefik-public`) ### Procedura ```bash git clone stream-overlay cd stream-overlay cp .env.example .env # imposta DOMAIN con il tuo dominio docker compose up -d ``` ### Accessi | Risorsa | URL | |---------|-----| | Controller | `https:///controller/` | | Scoreboard | `https:///overlay/scoreboard.html` | | Match Info | `https:///overlay/match-info.html` | | Clock | `https:///overlay/clock.html` | | Server Ind. | `https:///overlay/server-indicator.html` | | API stato | `https:///api/state` | | WebSocket | `wss:///ws` | ### Note - Il server Γ¨ stateless (stato in memoria). Un restart azzera il match. - Il WebSocket funziona nativamente con Traefik (upgrade HTTP β†’ WS automatico). ## Regole punteggio implementate - Punti game: 0, 15, 30, 40, Deuce, Vantaggio - Set: primo a 6 game con 2 di scarto - **Tiebreak**: a 6-6, primi a 7 punti con 2 di scarto - Servizio nel tiebreak: punto 1 β†’ P1, punto 2 β†’ P2, poi cambio ogni 2 punti - Match: al meglio dei 3 set ## Struttura file ``` stream-overlay/ β”œβ”€β”€ server/ β”‚ β”œβ”€β”€ package.json β”‚ β”œβ”€β”€ Dockerfile β”‚ └── index.js β”œβ”€β”€ overlay/ β”‚ β”œβ”€β”€ common.js β”‚ β”œβ”€β”€ scoreboard.html β”‚ β”œβ”€β”€ match-info.html β”‚ β”œβ”€β”€ clock.html β”‚ └── server-indicator.html β”œβ”€β”€ controller/ β”‚ └── index.html β”œβ”€β”€ docker-compose.yml β”œβ”€β”€ .env.example β”œβ”€β”€ .gitignore └── README.md ```