From 8730281d739d9ed1d6f2d366772582eea8ba0294 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 21 Aug 2026 13:52:11 +0200 Subject: feat: LAN Wi-Fi casting to Chromecast via local HTTP relay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-serve decrypted fMP4 video over HTTP on the LAN so a Chromecast can play the stream. The relay runs in the Electron main process — same trust boundary as downloads and MSE playback. - cast-relay.js: HTTP server with BoxAccumulator (reassembles WebRTC chunks into moof+mdat pairs), ring buffer, backpressure, finish() for clean end-of-stream, fixed port range 19550-19553 - cast-chromecast.js: mDNS discovery (bonjour-service) + CASTV2 protocol (castv2-client), connect/reload/disconnect lifecycle - Seek-aware: relay restarts on every seek, Chromecast reloads new URL; generation counter prevents stale async errors from killing active restarts; landingPlayheadRef suppresses programmatic seeking events - Device picker in video top bar with scan, device selection, copy-URL fallback, and cast status indicator - IPC bridge (main/preload/platform) for start/push/stop/finish/status/ discover/chromecastConnect/chromecastReload/chromecastDisconnect - Phase 3 design doc for DLNA/Smart TV in docs/cast-smart-tv.md Co-Authored-By: Claude Opus 4.6 --- packages/meshbay-client/src/preload.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'packages/meshbay-client/src/preload.js') diff --git a/packages/meshbay-client/src/preload.js b/packages/meshbay-client/src/preload.js index 04dfe44..e9fd375 100644 --- a/packages/meshbay-client/src/preload.js +++ b/packages/meshbay-client/src/preload.js @@ -40,6 +40,7 @@ contextBridge.exposeInMainWorld('meshbay', { nodeAdmin: true, localFolders: true, nativeSave: true, + lanCast: true, }, // Ask the main process to call the hub. The renderer has an `app://` origin, @@ -90,6 +91,21 @@ contextBridge.exposeInMainWorld('meshbay', { setPairingCode: (code) => ipcRenderer.invoke('node:set-pairing-code', code), }, + // LAN cast relay. The main process runs a local HTTP server and the + // renderer feeds it decrypted segments. A Chromecast or Smart TV on the + // same Wi-Fi plays from the URL. + cast: { + start: (opts) => ipcRenderer.invoke('cast:start', opts), + push: (data) => ipcRenderer.invoke('cast:push', data), + stop: () => ipcRenderer.invoke('cast:stop'), + finish: () => ipcRenderer.invoke('cast:finish'), + status: () => ipcRenderer.invoke('cast:status'), + discover: () => ipcRenderer.invoke('cast:discover'), + chromecastConnect: (opts) => ipcRenderer.invoke('cast:chromecast:connect', opts), + chromecastReload: (opts) => ipcRenderer.invoke('cast:chromecast:reload', opts), + chromecastDisconnect: () => ipcRenderer.invoke('cast:chromecast:disconnect'), + }, + // A sink that writes to disk as chunks arrive, never a buffer handed over at // the end. `auto` uses the remembered folder without a dialog, which is what // "save automatically" means; without one, or when the person asked to be -- cgit v1.2.3