summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-client
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-18 11:10:00 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-18 11:10:00 +0200
commit0d56cd33b975afe7648dd6f6380a30a7b5230c90 (patch)
tree1244665a98f34ba4988f5d2c6e25bfb147481ded /packages/meshbay-client
parent063c5f0750b775362e45e21a97e0dc2250c89f77 (diff)
downloadmeshbay-0d56cd33b975afe7648dd6f6380a30a7b5230c90.tar.gz
fix(cast): put the relay's progress log behind the same switch
Most of what the relay says repeats without bound — a line every fiftieth fragment for the length of a film, one per dropped fragment whenever a client falls behind — and it lands in the terminal the app was started from. Those go through `debuglog` now, restored with: NODE_DEBUG=cast-relay npm start The loss of fMP4 framing stays on console.warn. It recovers by rescanning, so nothing throws and no other part of the system hears about it; that line is the only trace that the picture on the television is missing a piece. Every other failure here throws, and the renderer already reports the rejection. The logging guard now covers both cast modules, each with the one line it is meant to keep audible. Also corrects the header comment, which still claimed CORS was granted on the subtitle path only. It has been on both since the stream needed it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
Diffstat (limited to 'packages/meshbay-client')
-rw-r--r--packages/meshbay-client/src/cast-relay.js53
1 files changed, 38 insertions, 15 deletions
diff --git a/packages/meshbay-client/src/cast-relay.js b/packages/meshbay-client/src/cast-relay.js
index 71c07a4..58eb9ac 100644
--- a/packages/meshbay-client/src/cast-relay.js
+++ b/packages/meshbay-client/src/cast-relay.js
@@ -19,7 +19,7 @@
* · Bind to the LAN interface, never 0.0.0.0
* · Fixed port range (19550-19553), opened only during active cast
* · Unguessable token in the URL (32 hex chars)
- * · CORS is granted on the subtitle path only, and still behind the token
+ * · CORS is granted on both served paths, and both stay behind the token
* · Cache-Control: no-store on every response
* · Server destroyed when playback stops — zero residual surface
*/
@@ -29,6 +29,25 @@
const crypto = require('node:crypto');
const http = require('node:http');
const os = require('node:os');
+const util = require('node:util');
+
+/**
+ * Progress logging, off unless asked for:
+ *
+ * NODE_DEBUG=cast-relay npm start
+ *
+ * Most of what this class has to say repeats without bound — a line every
+ * fiftieth fragment for the length of a film, one per dropped fragment
+ * whenever a client falls behind — and it lands in the terminal the app was
+ * started from. Node's own `debuglog` is used rather than a flag of our own:
+ * it costs nothing when disabled, since the message is never even formatted.
+ *
+ * Nothing that stops a cast is hidden behind it. Every hard failure here
+ * throws — no free port, no such file — and the renderer already reports the
+ * rejection. The one fault that neither throws nor reaches anybody else is a
+ * loss of fMP4 framing, and that one stays on `console.warn`.
+ */
+const debug = util.debuglog('cast-relay');
const RING_CAP = 64;
const BACKPRESSURE_HIGH = 8 * 1024 * 1024;
@@ -71,7 +90,7 @@ class BoxAccumulator {
if (!this._synced) {
const idx = this._findMoof();
if (idx === -1) return fragments;
- console.log(`[cast-relay] box sync: found first moof at byte offset ${idx}, discarded ${idx} bytes`);
+ debug(`[cast-relay] box sync: found first moof at byte offset ${idx}, discarded ${idx} bytes`);
this._buf = this._buf.subarray(idx);
this._synced = true;
}
@@ -81,7 +100,11 @@ class BoxAccumulator {
const type = this._buf.readUInt32BE(4);
if (size < 8) {
- console.log(`[cast-relay] box sync lost: invalid size ${size}, rescanning`);
+ // Not `debug`: the byte stream stopped being framed fMP4. It recovers
+ // by rescanning, so nothing throws and nobody else ever hears about
+ // it — this line is the only trace that the picture on the television
+ // is missing a piece.
+ console.warn(`[cast-relay] box sync lost: invalid size ${size}, rescanning`);
this._synced = false;
const idx = this._findMoof();
if (idx === -1) return fragments;
@@ -182,7 +205,7 @@ class CastRelay {
if (!sub || !sub.vtt) {
this._subtitle = null;
this._subtitleVersion++;
- console.log('[cast-relay] subtitle cleared');
+ debug('[cast-relay] subtitle cleared');
return null;
}
this._subtitle = {
@@ -191,7 +214,7 @@ class CastRelay {
label: sub.label || '',
};
this._subtitleVersion++;
- console.log(`[cast-relay] subtitle set: ${this._subtitle.vtt.length} bytes`
+ debug(`[cast-relay] subtitle set: ${this._subtitle.vtt.length} bytes`
+ `, lang "${this._subtitle.language}", v${this._subtitleVersion}`);
return this.subtitle;
}
@@ -232,14 +255,14 @@ class CastRelay {
bound = true;
} catch (err) {
if (err.code !== 'EADDRINUSE') throw err;
- console.log(`[cast-relay] port ${port} busy, trying next`);
+ debug(`[cast-relay] port ${port} busy, trying next`);
}
}
if (!bound) throw new Error('All cast relay ports are in use');
this._server = server;
- console.log(`[cast-relay] started on ${this.url}`);
- console.log(`[cast-relay] init segment: ${this._initSegment ? this._initSegment.length + ' bytes' : 'none'}`);
+ debug(`[cast-relay] started on ${this.url}`);
+ debug(`[cast-relay] init segment: ${this._initSegment ? this._initSegment.length + ' bytes' : 'none'}`);
return {
url: this.url,
port: this._port,
@@ -257,7 +280,7 @@ class CastRelay {
for (const frag of fragments) {
this._fragCount++;
if (this._fragCount <= 3 || this._fragCount % 50 === 0) {
- console.log(`[cast-relay] fragment #${this._fragCount}: ${frag.length} bytes (from ${this._chunkCount} chunks), ${this._clients.size} client(s)`);
+ debug(`[cast-relay] fragment #${this._fragCount}: ${frag.length} bytes (from ${this._chunkCount} chunks), ${this._clients.size} client(s)`);
}
if (this._ring.length >= RING_CAP) {
@@ -267,7 +290,7 @@ class CastRelay {
for (const res of this._clients) {
if (res.writableLength > BACKPRESSURE_HIGH) {
- console.log(`[cast-relay] backpressure: dropping fragment for slow client`);
+ debug(`[cast-relay] backpressure: dropping fragment for slow client`);
continue;
}
res.write(frag);
@@ -277,14 +300,14 @@ class CastRelay {
}
finish() {
- console.log('[cast-relay] finishing stream');
+ debug('[cast-relay] finishing stream');
for (const res of this._clients) {
try { res.end(); } catch { /* already closed */ }
}
}
async stop() {
- console.log(`[cast-relay] stopping — ${this._chunkCount} chunks, ${this._fragCount} fragments, ${(this._bytesSent / 1048576).toFixed(1)} MB sent`);
+ debug(`[cast-relay] stopping — ${this._chunkCount} chunks, ${this._fragCount} fragments, ${(this._bytesSent / 1048576).toFixed(1)} MB sent`);
for (const res of this._clients) {
try { res.end(); } catch { /* already closed */ }
}
@@ -372,12 +395,12 @@ class CastRelay {
sent += frag.length;
}
- console.log(`[cast-relay] client connected from ${req.socket.remoteAddress} — sent init + ${this._ring.length} fragments (${(sent / 1024).toFixed(0)} KB)`);
+ debug(`[cast-relay] client connected from ${req.socket.remoteAddress} — sent init + ${this._ring.length} fragments (${(sent / 1024).toFixed(0)} KB)`);
this._clients.add(res);
req.on('close', () => {
this._clients.delete(res);
- console.log(`[cast-relay] client disconnected, ${this._clients.size} remaining`);
+ debug(`[cast-relay] client disconnected, ${this._clients.size} remaining`);
});
}
@@ -394,7 +417,7 @@ class CastRelay {
'Cache-Control': 'no-store',
});
res.end(this._subtitle.vtt);
- console.log(`[cast-relay] subtitle served: ${this._subtitle.vtt.length} bytes`);
+ debug(`[cast-relay] subtitle served: ${this._subtitle.vtt.length} bytes`);
}
}