diff options
Diffstat (limited to 'packages/meshbay-client/src')
| -rw-r--r-- | packages/meshbay-client/src/cast-relay.js | 53 |
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`); } } |