diff --git a/README.md b/README.md index 0b7b063..cf344c9 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ Schema migrations run automatically on first boot — no manual migration comman | `JWT_SECRET` | JWT signing key (auto-generated if not set) | _(auto)_ | | `SSL_CERT` | Path to SSL certificate | `server/certs/cert.pem` | | `SSL_KEY` | Path to SSL private key | `server/certs/key.pem` | +| `PING_INTERVAL` | Socket.IO Engine.IO ping interval (ms). Raise for slow TV WebKits that miss pongs under decode load. | `30000` | +| `PING_TIMEOUT` | Socket.IO Engine.IO pong wait (ms). Lower = faster dead-socket detection; higher = more forgiving of laggy clients. | `30000` | ### Optional Integrations diff --git a/server/config.js b/server/config.js index 5c541bf..946e72a 100644 --- a/server/config.js +++ b/server/config.js @@ -10,6 +10,12 @@ module.exports = { frontendDir: path.join(__dirname, '..', 'frontend'), heartbeatInterval: 10000, // Check every 10s heartbeatTimeout: 45000, // Offline after 45s (3 missed 15s beats) + // Engine.IO transport-level ping/pong. Raised from Socket.IO defaults + // (25000/20000) because TV WebKits (LG webOS, older Tizen) miss pongs + // under decode load - tighter values cause spurious transport drops. + // Worst-case dead-socket detection: pingInterval + pingTimeout = 60s. + pingInterval: parseInt(process.env.PING_INTERVAL) || 30000, + pingTimeout: parseInt(process.env.PING_TIMEOUT) || 30000, maxFileSize: 500 * 1024 * 1024, // 500MB thumbnailWidth: 320, screenshotQuality: 70, diff --git a/server/player/index.html b/server/player/index.html index 6ba350e..165b7ee 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -467,6 +467,14 @@ reconnectionDelay: 2000, reconnectionDelayMax: 10000, timeout: 20000, + // Prefer WebSocket but allow polling fallback. Socket.IO default is + // polling-first with an upgrade dance that's fragile on TV WebKits + // (LG webOS especially). Reversing the order opens a WebSocket directly; + // if that fails (rare - blocked by firewall), it falls back to polling + // on the same connect attempt. Tradeoff: WS-blocked networks add a few + // seconds to first connect while WS times out. Worth it for the common + // case where WS is fine but the upgrade dance was hanging the device. + transports: ['websocket', 'polling'], }); socket.on('connect', () => { diff --git a/server/server.js b/server/server.js index 2435a42..bc52464 100644 --- a/server/server.js +++ b/server/server.js @@ -43,7 +43,9 @@ const io = new Server(server, { origin: (origin, cb) => corsOriginCheck(origin, cb), credentials: true, }, - maxHttpBufferSize: 10 * 1024 * 1024 // 10MB for screenshot uploads + maxHttpBufferSize: 10 * 1024 * 1024, // 10MB for screenshot uploads + pingInterval: config.pingInterval, + pingTimeout: config.pingTimeout, }); // Middleware