feat: speed up snapshot emission

This commit is contained in:
Rohit Rajan
2025-11-03 22:55:14 +05:30
parent 1f99295c0f
commit 58aa5f5c56

View File

@@ -201,6 +201,11 @@ export class RemoteBrowser {
private networkRequestTimeout: NodeJS.Timeout | null = null; private networkRequestTimeout: NodeJS.Timeout | null = null;
private pendingNetworkRequests: string[] = []; private pendingNetworkRequests: string[] = [];
private readonly NETWORK_QUIET_PERIOD = 8000; private readonly NETWORK_QUIET_PERIOD = 8000;
private readonly INITIAL_LOAD_QUIET_PERIOD = 3000;
private networkWaitStartTime: number = 0;
private progressInterval: NodeJS.Timeout | null = null;
private hasShownInitialLoader: boolean = false;
private isInitialLoadInProgress: boolean = false;
/** /**
* Initializes a new instances of the {@link Generator} and {@link WorkflowInterpreter} classes and * Initializes a new instances of the {@link Generator} and {@link WorkflowInterpreter} classes and
@@ -432,17 +437,19 @@ export class RemoteBrowser {
if (!this.currentPage) return; if (!this.currentPage) return;
this.currentPage.on("domcontentloaded", async () => { this.currentPage.on("domcontentloaded", async () => {
logger.info("DOM content loaded - triggering snapshot"); if (!this.isInitialLoadInProgress) {
await this.makeAndEmitDOMSnapshot(); logger.info("DOM content loaded - triggering snapshot");
await this.makeAndEmitDOMSnapshot();
}
}); });
this.currentPage.on("response", async (response) => { this.currentPage.on("response", async (response) => {
const url = response.url(); const url = response.url();
if ( const isDocumentRequest = response.request().resourceType() === "document";
response.request().resourceType() === "document" ||
url.includes("api/") || if (!this.hasShownInitialLoader && isDocumentRequest && !url.includes("about:blank")) {
url.includes("ajax") this.hasShownInitialLoader = true;
) { this.isInitialLoadInProgress = true;
this.pendingNetworkRequests.push(url); this.pendingNetworkRequests.push(url);
if (this.networkRequestTimeout) { if (this.networkRequestTimeout) {
@@ -450,24 +457,54 @@ export class RemoteBrowser {
this.networkRequestTimeout = null; this.networkRequestTimeout = null;
} }
if (this.progressInterval) {
clearInterval(this.progressInterval);
this.progressInterval = null;
}
this.networkWaitStartTime = Date.now();
this.progressInterval = setInterval(() => {
const elapsed = Date.now() - this.networkWaitStartTime;
const navigationProgress = Math.min((elapsed / this.INITIAL_LOAD_QUIET_PERIOD) * 40, 35);
const totalProgress = 60 + navigationProgress;
this.emitLoadingProgress(totalProgress, this.pendingNetworkRequests.length);
}, 500);
logger.debug( logger.debug(
`Network request received: ${url}. Total pending: ${this.pendingNetworkRequests.length}` `Initial load network request received: ${url}. Using ${this.INITIAL_LOAD_QUIET_PERIOD}ms quiet period`
); );
this.networkRequestTimeout = setTimeout(async () => { this.networkRequestTimeout = setTimeout(async () => {
logger.info( logger.info(
`Network quiet period reached. Processing ${this.pendingNetworkRequests.length} requests` `Initial load network quiet period reached (${this.INITIAL_LOAD_QUIET_PERIOD}ms)`
); );
if (this.progressInterval) {
clearInterval(this.progressInterval);
this.progressInterval = null;
}
this.emitLoadingProgress(100, this.pendingNetworkRequests.length);
this.pendingNetworkRequests = []; this.pendingNetworkRequests = [];
this.networkRequestTimeout = null; this.networkRequestTimeout = null;
this.isInitialLoadInProgress = false;
await this.makeAndEmitDOMSnapshot(); await this.makeAndEmitDOMSnapshot();
}, this.NETWORK_QUIET_PERIOD); }, this.INITIAL_LOAD_QUIET_PERIOD);
} }
}); });
} }
private emitLoadingProgress(progress: number, pendingRequests: number): void {
this.socket.emit("domLoadingProgress", {
progress: Math.round(progress),
pendingRequests,
userId: this.userId,
timestamp: Date.now(),
});
}
private async setupPageEventListeners(page: Page) { private async setupPageEventListeners(page: Page) {
page.on('framenavigated', async (frame) => { page.on('framenavigated', async (frame) => {
if (frame === page.mainFrame()) { if (frame === page.mainFrame()) {
@@ -522,6 +559,12 @@ export class RemoteBrowser {
let retryCount = 0; let retryCount = 0;
let success = false; let success = false;
this.socket.emit("dom-snapshot-loading", {
userId: this.userId,
timestamp: Date.now(),
});
this.emitLoadingProgress(0, 0);
while (!success && retryCount < MAX_RETRIES) { while (!success && retryCount < MAX_RETRIES) {
try { try {
this.browser = <Browser>(await chromium.launch({ this.browser = <Browser>(await chromium.launch({
@@ -546,6 +589,8 @@ export class RemoteBrowser {
throw new Error('Browser failed to launch or is not connected'); throw new Error('Browser failed to launch or is not connected');
} }
this.emitLoadingProgress(20, 0);
const proxyConfig = await getDecryptedProxyConfig(userId); const proxyConfig = await getDecryptedProxyConfig(userId);
let proxyOptions: { server: string, username?: string, password?: string } = { server: '' }; let proxyOptions: { server: string, username?: string, password?: string } = { server: '' };
@@ -623,6 +668,8 @@ export class RemoteBrowser {
this.currentPage = await this.context.newPage(); this.currentPage = await this.context.newPage();
this.emitLoadingProgress(40, 0);
await this.setupPageEventListeners(this.currentPage); await this.setupPageEventListeners(this.currentPage);
const viewportSize = await this.currentPage.viewportSize(); const viewportSize = await this.currentPage.viewportSize();
@@ -646,6 +693,8 @@ export class RemoteBrowser {
this.client = await this.currentPage.context().newCDPSession(this.currentPage); this.client = await this.currentPage.context().newCDPSession(this.currentPage);
} }
this.emitLoadingProgress(60, 0);
success = true; success = true;
logger.log('debug', `Browser initialized successfully for user ${userId}`); logger.log('debug', `Browser initialized successfully for user ${userId}`);
} catch (error: any) { } catch (error: any) {
@@ -1521,9 +1570,6 @@ export class RemoteBrowser {
this.isDOMStreamingActive = true; this.isDOMStreamingActive = true;
logger.info("DOM streaming started successfully"); logger.info("DOM streaming started successfully");
// Initial DOM snapshot
await this.makeAndEmitDOMSnapshot();
this.setupScrollEventListener(); this.setupScrollEventListener();
this.setupPageChangeListeners(); this.setupPageChangeListeners();
} catch (error) { } catch (error) {