aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/platform.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-21 13:52:11 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-21 13:52:11 +0200
commit8730281d739d9ed1d6f2d366772582eea8ba0294 (patch)
tree4df2f3c8d42dc17147bcdec23109aa9c0f1b5b35 /packages/meshbay-hub/src/meshbay_hub/static/platform.js
parentae52b69b14a6997d01aad66f49fbea7b5ca2dfe6 (diff)
downloadmeshbay-8730281d739d9ed1d6f2d366772582eea8ba0294.tar.gz
feat: LAN Wi-Fi casting to Chromecast via local HTTP relay
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 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/platform.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/platform.js55
1 files changed, 53 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/platform.js b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
index 02107f5..d400b4e 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/platform.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
@@ -46,6 +46,8 @@ export const capabilities = {
localFolders: Boolean(bridge && bridge.capabilities && bridge.capabilities.localFolders),
// A real save dialog and a write that does not pass through the page.
nativeSave: Boolean(bridge && bridge.capabilities && bridge.capabilities.nativeSave),
+ // Cast decrypted video to a device on the same LAN via a local HTTP relay.
+ lanCast: Boolean(bridge && bridge.capabilities && bridge.capabilities.lanCast),
};
/**
@@ -234,8 +236,56 @@ export const node = {
},
};
+/**
+ * LAN cast relay — re-serve decrypted video segments over HTTP so a
+ * Chromecast or Smart TV on the same Wi-Fi can play the stream.
+ *
+ * Absent in a browser, where the relay cannot run: there is no main process
+ * to bind a server socket in, and a page cannot open one.
+ */
+export const cast = {
+ available: Boolean(bridge && bridge.cast),
+ async start(opts) {
+ if (!bridge || !bridge.cast) return null;
+ return bridge.cast.start(opts);
+ },
+ async push(data) {
+ if (!bridge || !bridge.cast) return false;
+ return bridge.cast.push(data);
+ },
+ async stop() {
+ if (!bridge || !bridge.cast) return false;
+ return bridge.cast.stop();
+ },
+ async finish() {
+ if (!bridge || !bridge.cast) return false;
+ return bridge.cast.finish();
+ },
+ async status() {
+ if (!bridge || !bridge.cast) return null;
+ return bridge.cast.status();
+ },
+ async discover() {
+ if (!bridge || !bridge.cast) return [];
+ return bridge.cast.discover();
+ },
+ async chromecastConnect(opts) {
+ if (!bridge || !bridge.cast) return null;
+ return bridge.cast.chromecastConnect(opts);
+ },
+ async chromecastReload(opts) {
+ if (!bridge || !bridge.cast) return null;
+ return bridge.cast.chromecastReload(opts);
+ },
+ async chromecastDisconnect() {
+ if (!bridge || !bridge.cast) return false;
+ return bridge.cast.chromecastDisconnect();
+ },
+};
+
export default { isNative, hubBase, capabilities, secrets, nativeSave,
- apiFetch, device, bridgeMessage, folder, rootPicker, node };
+ apiFetch, device, bridgeMessage, folder, rootPicker, node,
+ cast };
// Also a global, because `transport.js` is loaded as a classic script — it
// predates the module graph and exposes `MeshBayTransport` the same way. The
@@ -244,5 +294,6 @@ export default { isNative, hubBase, capabilities, secrets, nativeSave,
if (typeof window !== 'undefined') {
window.MeshBayPlatform = { isNative, hubBase, capabilities, secrets,
nativeSave, apiFetch, device,
- bridgeMessage, folder, rootPicker, node };
+ bridgeMessage, folder, rootPicker, node,
+ cast };
}