Dynamically adjust reported aspect ratio based on GEOMETRY (#794)

Closes #793 
Related to #733

Adjusts the reported aspect ratio based on GEOMETRY env var.
Also adjusts stylesheet in screencast HTML to match.
---------
Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
This commit is contained in:
Emma Segal-Grossman 2025-04-01 21:26:12 -04:00 committed by GitHub
parent 2b00c1f065
commit 41b968baac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 45 deletions

1
.prettierrc Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -25,6 +25,17 @@
resp = JSON.parse(resp);
switch (resp.msg) {
case "init":
if (resp.width && resp.height) {
try {
self.document.styleSheets[0].rules[1].style.width = resp.width + "px";
self.document.styleSheets[0].rules[1].style.height = resp.height + "px";
} catch (e) {
console.log("Error adjusting stylesheet: ", e);
}
}
break;
case "screencast":
img = createImage(resp.id);
if (resp.data) {

View file

@ -436,7 +436,11 @@ export class Crawler {
return null;
}
return new ScreenCaster(transport, this.params.workers);
return new ScreenCaster(
transport,
this.params.workers,
this.browser.screenWHRatio,
);
}
launchRedis() {

View file

@ -26,6 +26,7 @@ import puppeteer, {
import { CDPSession, Target, Browser as PptrBrowser } from "puppeteer-core";
import { Recorder } from "./recorder.js";
import { timedRun } from "./timing.js";
import assert from "node:assert";
type BtrixChromeOpts = {
proxy?: string;
@ -70,8 +71,21 @@ export class Browser {
crashed = false;
screenWidth: number;
screenHeight: number;
screenWHRatio: number;
constructor() {
this.profileDir = fs.mkdtempSync(path.join(os.tmpdir(), "profile-"));
// must be provided, part of Dockerfile
assert(process.env.GEOMETRY);
const geom = process.env.GEOMETRY.split("x");
this.screenWidth = Number(geom[0]);
this.screenHeight = Number(geom[1]);
this.screenWHRatio = this.screenWidth / this.screenHeight;
}
async launch({
@ -106,16 +120,10 @@ export class Browser {
args.push(`--display=${DISPLAY}`);
}
let defaultViewport = null;
if (process.env.GEOMETRY) {
const geom = process.env.GEOMETRY.split("x");
defaultViewport = {
width: Number(geom[0]),
height: Number(geom[1]) - (recording ? 0 : BROWSER_HEIGHT_OFFSET),
};
}
const defaultViewport = {
width: this.screenWidth,
height: this.screenHeight - (recording ? 0 : BROWSER_HEIGHT_OFFSET),
};
const launchOpts: LaunchOptions = {
args,

View file

@ -1,13 +1,18 @@
import ws, { WebSocket } from "ws";
import http, { IncomingMessage, ServerResponse } from "http";
import ws, { type WebSocket } from "ws";
import http, {
type IncomingMessage,
type Server,
type ServerResponse,
} from "http";
import url from "url";
import fs from "fs";
import { initRedis } from "./redis.js";
import { logger } from "./logger.js";
import { Duplex } from "stream";
import { CDPSession, Page } from "puppeteer-core";
import { WorkerId } from "./state.js";
import { type Duplex } from "stream";
import { type CDPSession, type Page } from "puppeteer-core";
import { type WorkerId } from "./state.js";
import type Redis from "ioredis";
const indexHTML = fs.readFileSync(
new URL("../../html/screencast.html", import.meta.url),
@ -20,9 +25,7 @@ class WSTransport {
// eslint-disable-next-line no-use-before-define
caster!: ScreenCaster;
wss: ws.Server;
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
httpServer: any;
httpServer: Server;
constructor(port: number) {
this.allWS = new Set();
@ -94,9 +97,7 @@ class WSTransport {
});
}
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendAll(packet: Record<any, any>) {
sendAll(packet: unknown) {
const packetStr = JSON.stringify(packet);
for (const ws of this.allWS) {
ws.send(packetStr);
@ -115,9 +116,7 @@ class RedisPubSubTransport {
// eslint-disable-next-line no-use-before-define
caster!: ScreenCaster;
ctrlChannel: string;
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
redis: any;
redis!: Redis;
constructor(redisUrl: string, crawlId: string) {
this.castChannel = `c:${crawlId}:cast`;
@ -162,18 +161,21 @@ class RedisPubSubTransport {
});
}
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async sendAll(packet: Record<any, any>) {
async sendAll(packet: unknown) {
await this.redis.publish(this.castChannel, JSON.stringify(packet));
}
async isActive() {
const result = await this.redis.pubsub("numsub", this.castChannel);
const result = (await this.redis.pubsub(
"NUMSUB",
this.castChannel,
)) as number[];
return result.length > 1 ? result[1] > 0 : false;
}
}
type CDPSessionWithCastInfo = CDPSession & { _startedCast?: boolean };
// ===========================================================================
class ScreenCaster {
transport: WSTransport;
@ -182,14 +184,23 @@ class ScreenCaster {
cdps = new Map<WorkerId, CDPSession>();
maxWidth = 640;
maxHeight = 480;
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
initMsg: { [key: string]: any };
initMsg: {
msg: "init";
width: number;
height: number;
browsers: number;
};
constructor(transport: WSTransport, numWorkers: number) {
constructor(transport: WSTransport, numWorkers: number, ratio?: number) {
this.transport = transport;
this.transport.caster = this;
console.log("RATIO", ratio);
if (ratio) {
this.maxHeight = this.maxWidth / ratio;
}
this.initMsg = {
msg: "init",
width: this.maxWidth,
@ -276,15 +287,11 @@ class ScreenCaster {
}
async startCast(cdp: CDPSession, id: WorkerId) {
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((cdp as any)._startedCast) {
if ((cdp as CDPSessionWithCastInfo)._startedCast) {
return;
}
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(cdp as any)._startedCast = true;
(cdp as CDPSessionWithCastInfo)._startedCast = true;
logger.info("Started Screencast", { workerid: id }, "screencast");
@ -297,15 +304,11 @@ class ScreenCaster {
}
async stopCast(cdp: CDPSession, id: WorkerId) {
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!(cdp as any)._startedCast) {
if (!(cdp as CDPSessionWithCastInfo)._startedCast) {
return;
}
// TODO: Fix this the next time the file is edited.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(cdp as any)._startedCast = false;
(cdp as CDPSessionWithCastInfo)._startedCast = false;
logger.info("Stopping Screencast", { workerid: id }, "screencast");