diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-21 13:52:11 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-21 13:52:11 +0200 |
| commit | 8730281d739d9ed1d6f2d366772582eea8ba0294 (patch) | |
| tree | 4df2f3c8d42dc17147bcdec23109aa9c0f1b5b35 /packages/meshbay-hub/src/meshbay_hub/static/style.css | |
| parent | ae52b69b14a6997d01aad66f49fbea7b5ca2dfe6 (diff) | |
| download | meshbay-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/style.css')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/style.css | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 7ce073a..ee51a8a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -1060,6 +1060,39 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } } .video-close:hover { background: rgba(255, 255, 255, 0.25); } .video-close.dl-active { background: rgba(34, 197, 94, 0.25); pointer-events: none; } +.video-close.cast-active { background: rgba(59, 130, 246, 0.35); } +.video-close.cast-active:hover { background: rgba(59, 130, 246, 0.5); } +.cast-url-input { + background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 4px; color: #e2e8f0; font-size: 0.75em; font-family: inherit; + padding: 4px 8px; min-width: 0; flex: 1; max-width: 420px; + cursor: text; outline: none; +} +.cast-url-input:focus { border-color: rgba(59, 130, 246, 0.6); } +.cast-wrapper { display: inline-block; } +.cast-picker { + position: absolute; top: calc(100% + 4px); right: 0; z-index: 100; + background: rgba(28, 28, 32, 0.97); border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: 8px; padding: 4px 0; min-width: 230px; + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); +} +.cast-picker-item { + display: flex; align-items: center; gap: 8px; width: 100%; + padding: 9px 14px; background: none; border: none; + color: #e2e8f0; font-size: 0.82em; cursor: pointer; text-align: left; + font-family: inherit; +} +.cast-picker-item:hover { background: rgba(255, 255, 255, 0.09); } +.cast-picker-item svg { width: 16px; height: 16px; flex-shrink: 0; } +.cast-picker-scanning { color: #94a3b8; cursor: default; } +.cast-picker-scanning:hover { background: none; } +.cast-picker-empty { color: #64748b; cursor: default; font-style: italic; } +.cast-picker-empty:hover { background: none; } +.cast-picker-sep { height: 1px; margin: 4px 10px; background: rgba(255,255,255,0.1); } +.cast-status-label { + color: #94a3b8; font-size: 0.78em; display: flex; align-items: center; + max-width: 420px; overflow: hidden; white-space: nowrap; +} .video-container { width: 100%; |