From 1522b3b3a88c13dc727b93625c62ac4abe77de4b Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 13 Jan 2026 14:43:10 -0600 Subject: [PATCH] address comments --- src/browser.ts | 16 +++++++++++++--- src/stream-server.ts | 40 +++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/browser.ts b/src/browser.ts index 23f2585..efe06b4 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -84,6 +84,7 @@ export class BrowserManager { private screencastActive: boolean = false; private screencastSessionId: number = 0; private frameCallback: ((frame: ScreencastFrame) => void) | null = null; + private screencastFrameHandler: ((params: any) => void) | null = null; /** * Check if browser is launched @@ -952,8 +953,8 @@ export class BrowserManager { this.frameCallback = callback; this.screencastActive = true; - // Listen for screencast frames - cdp.on('Page.screencastFrame', async (params: any) => { + // Create and store the frame handler so we can remove it later + this.screencastFrameHandler = async (params: any) => { const frame: ScreencastFrame = { data: params.data, metadata: params.metadata, @@ -967,7 +968,10 @@ export class BrowserManager { if (this.frameCallback) { this.frameCallback(frame); } - }); + }; + + // Listen for screencast frames + cdp.on('Page.screencastFrame', this.screencastFrameHandler); // Start the screencast await cdp.send('Page.startScreencast', { @@ -990,12 +994,18 @@ export class BrowserManager { try { const cdp = await this.getCDPSession(); await cdp.send('Page.stopScreencast'); + + // Remove the event listener to prevent accumulation + if (this.screencastFrameHandler) { + cdp.off('Page.screencastFrame', this.screencastFrameHandler); + } } catch { // Ignore errors when stopping } this.screencastActive = false; this.frameCallback = null; + this.screencastFrameHandler = null; } /** diff --git a/src/stream-server.ts b/src/stream-server.ts index cf10628..002d3cc 100644 --- a/src/stream-server.ts +++ b/src/stream-server.ts @@ -304,26 +304,32 @@ export class StreamServer { * Start screencasting */ private async startScreencast(): Promise { + // Set flag immediately to prevent race conditions with concurrent calls if (this.isScreencasting) return; - - // Check if browser is launched - if (!this.browser.isLaunched()) { - throw new Error('Browser not launched'); - } - - await this.browser.startScreencast((frame) => this.broadcastFrame(frame), { - format: 'jpeg', - quality: 80, - maxWidth: 1280, - maxHeight: 720, - everyNthFrame: 1, - }); - this.isScreencasting = true; - // Notify all clients - for (const client of this.clients) { - this.sendStatus(client); + try { + // Check if browser is launched + if (!this.browser.isLaunched()) { + throw new Error('Browser not launched'); + } + + await this.browser.startScreencast((frame) => this.broadcastFrame(frame), { + format: 'jpeg', + quality: 80, + maxWidth: 1280, + maxHeight: 720, + everyNthFrame: 1, + }); + + // Notify all clients + for (const client of this.clients) { + this.sendStatus(client); + } + } catch (error) { + // Reset flag on failure so caller can retry + this.isScreencasting = false; + throw error; } }