diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-02 08:39:56 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-02 08:39:56 +0200 |
| commit | e941cc4c39c38a12220153ea572bd4c7bb92fde0 (patch) | |
| tree | b7a0c1f04eb642f4aa050508b3e24bb4cf66a67a /packages/meshbay-client | |
| parent | fee265e0046e85c3db0e5bb9608b666d2e9547ab (diff) | |
| download | meshbay-e941cc4c39c38a12220153ea572bd4c7bb92fde0.tar.gz | |
feat(client): list cast receivers as they answer
The scan still runs six seconds, but the picker polls what it has found
and shows each receiver immediately. A rescan no longer has its timer
cut short by the scan it replaced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-client')
| -rw-r--r-- | packages/meshbay-client/src/cast-chromecast.js | 71 | ||||
| -rw-r--r-- | packages/meshbay-client/src/main.js | 7 | ||||
| -rw-r--r-- | packages/meshbay-client/src/preload.js | 3 |
3 files changed, 50 insertions, 31 deletions
diff --git a/packages/meshbay-client/src/cast-chromecast.js b/packages/meshbay-client/src/cast-chromecast.js index 1355cdd..7fc8079 100644 --- a/packages/meshbay-client/src/cast-chromecast.js +++ b/packages/meshbay-client/src/cast-chromecast.js @@ -78,19 +78,25 @@ class CastChromecast { constructor() { this._bonjour = null; this._browser = null; + this._scanTimer = null; this._devices = new Map(); this._client = null; this._player = null; this._connectedDevice = null; } - async discover() { + /** + * Start a scan and return at once; `devices()` reads what it has found so far. + * + * The scan runs its full length because a receiver coming back from a reset + * can take several seconds to answer, but most answer within two, and a + * picker that waits the full length to show any of them is a picker that is + * slow every single time. So the page polls and lists each one as it lands. + * A scan started over an unfinished one replaces it, timer included. + */ + startScan() { this._devices.clear(); - - if (this._browser) { - this._browser.stop(); - this._browser = null; - } + this._stopScan(); if (!this._bonjour) { this._bonjour = new Bonjour(); @@ -98,30 +104,39 @@ class CastChromecast { debug('[cast-chromecast] scanning for devices...'); - return new Promise((resolve) => { - this._browser = this._bonjour.find({ type: 'googlecast' }, (service) => { - const id = service.txt?.id || service.name; - const name = service.txt?.fn || service.name; - const host = service.addresses?.find((a) => /^\d+\.\d+\.\d+\.\d+$/.test(a)) - || (service.referer && service.referer.address); - const port = service.port || 8009; + this._browser = this._bonjour.find({ type: 'googlecast' }, (service) => { + const id = service.txt?.id || service.name; + const name = service.txt?.fn || service.name; + const host = service.addresses?.find((a) => /^\d+\.\d+\.\d+\.\d+$/.test(a)) + || (service.referer && service.referer.address); + const port = service.port || 8009; - if (host && id) { - this._devices.set(id, { id, name, host, port }); - debug(`[cast-chromecast] discovered: "${name}" at ${host}:${port}`); - } - }); - - setTimeout(() => { - if (this._browser) { - this._browser.stop(); - this._browser = null; - } - const devices = Array.from(this._devices.values()); - debug(`[cast-chromecast] scan complete: ${devices.length} device(s)`); - resolve(devices); - }, SCAN_DURATION_MS); + if (host && id) { + this._devices.set(id, { id, name, host, port }); + debug(`[cast-chromecast] discovered: "${name}" at ${host}:${port}`); + } }); + + this._scanTimer = setTimeout(() => { + this._stopScan(); + debug(`[cast-chromecast] scan complete: ${this._devices.size} device(s)`); + }, SCAN_DURATION_MS); + } + + devices() { + return { + devices: Array.from(this._devices.values()), + scanning: this._browser !== null, + }; + } + + _stopScan() { + clearTimeout(this._scanTimer); + this._scanTimer = null; + if (this._browser) { + this._browser.stop(); + this._browser = null; + } } async connect(deviceId, mediaUrl, subtitle) { diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js index c1ff540..cbaabc4 100644 --- a/packages/meshbay-client/src/main.js +++ b/packages/meshbay-client/src/main.js @@ -2330,10 +2330,13 @@ function registerBridge() { // ── Chromecast discovery + control ────────────────────────────────────── - handle('cast:discover', async () => { - return castChromecast.discover(); + handle('cast:scan', async () => { + castChromecast.startScan(); + return true; }); + handle('cast:devices', async () => castChromecast.devices()); + handle('cast:chromecast:connect', async (_e, { deviceId, mediaUrl, subtitle }) => { return castChromecast.connect(deviceId, mediaUrl, subtitle === undefined ? castRelay.subtitle : subtitle); diff --git a/packages/meshbay-client/src/preload.js b/packages/meshbay-client/src/preload.js index e9c34d2..665ec1f 100644 --- a/packages/meshbay-client/src/preload.js +++ b/packages/meshbay-client/src/preload.js @@ -175,7 +175,8 @@ contextBridge.exposeInMainWorld('meshbay', { subtitle: (sub) => ipcRenderer.invoke('cast:subtitle', sub), finish: () => ipcRenderer.invoke('cast:finish'), status: () => ipcRenderer.invoke('cast:status'), - discover: () => ipcRenderer.invoke('cast:discover'), + scan: () => ipcRenderer.invoke('cast:scan'), + devices: () => ipcRenderer.invoke('cast:devices'), chromecastConnect: (opts) => ipcRenderer.invoke('cast:chromecast:connect', opts), chromecastReload: (opts) => ipcRenderer.invoke('cast:chromecast:reload', opts), chromecastDisconnect: () => ipcRenderer.invoke('cast:chromecast:disconnect'), |