Rename and fix screenshots

This commit is contained in:
Andras Schmelczer 2026-02-07 22:19:06 +00:00
parent 9e71ed77df
commit e5d5819098
8 changed files with 81 additions and 18 deletions

View file

@ -20,19 +20,19 @@ services:
- cargo-target:/app/server-rs/target
environment:
POCKETBASE_URL: http://pocketbase:8090
OG_SIDECAR_URL: http://og-screenshot:8002
SCREENSHOT_URL: http://screenshot:8002
OLLAMA_URL: http://host.docker.internal:11434
depends_on:
pocketbase:
condition: service_healthy
og-screenshot:
build: /volumes/syncthing/Projects/property-map/og-screenshot
screenshot:
build: /volumes/syncthing/Projects/property-map/screenshot
environment:
NARROWIT_URL: http://server:8001
NARROWIT_URL: http://frontend:3001
CACHE_DIR: /cache
volumes:
- og-cache:/cache
- screenshot-cache:/cache
networks:
- dev-network
healthcheck:
@ -88,7 +88,7 @@ volumes:
cargo-registry:
cargo-target:
frontend-node-modules:
og-cache:
screenshot-cache:
networks:
dev-network:

View file

@ -9,9 +9,14 @@ WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install
# Install Chromium for Playwright
# Install Chromium for Playwright (including system deps for headless rendering)
RUN npx playwright install --with-deps chromium
# Ensure EGL/GLES libraries are available for SwiftShader WebGL rendering
RUN apt-get update && \
apt-get install -y --no-install-recommends libegl1 libgles2 && \
rm -rf /var/lib/apt/lists/*
COPY tsconfig.json ./
COPY src/ src/
RUN npm run build

View file

@ -1,11 +1,11 @@
{
"name": "og-screenshot",
"name": "screenshot",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "og-screenshot",
"name": "screenshot",
"version": "1.0.0",
"dependencies": {
"express": "^4.21.0",

View file

@ -1,5 +1,5 @@
{
"name": "og-screenshot",
"name": "screenshot",
"version": "1.0.0",
"private": true,
"scripts": {

View file

@ -36,14 +36,19 @@ function detectGpu(): boolean {
const hasGpu = detectGpu();
function getBrowserArgs(): string[] {
const baseArgs = ['--no-sandbox', '--disable-dev-shm-usage'];
const baseArgs = [
'--no-sandbox',
'--disable-dev-shm-usage',
'--enable-webgl',
'--ignore-gpu-blocklist',
];
if (hasGpu) {
// Use hardware GPU acceleration
return [...baseArgs, '--enable-gpu', '--enable-webgl'];
return [...baseArgs, '--enable-gpu', '--use-gl=egl'];
} else {
// Fall back to SwiftShader software rendering
return [...baseArgs, '--use-gl=angle', '--use-angle=swiftshader'];
return [...baseArgs, '--disable-gpu', '--use-gl=swiftshader'];
}
}
@ -116,17 +121,34 @@ async function releasePage(page: Page): Promise<void> {
export async function takeScreenshot(url: string): Promise<Buffer> {
const page = await acquirePage();
// Log browser console messages for diagnostics
page.on('console', (msg) => {
if (msg.type() === 'error' || msg.type() === 'warning') {
console.log(`[browser ${msg.type()}] ${msg.text()}`);
}
});
page.on('pageerror', (err) => {
console.log(`[browser exception] ${err.message}`);
});
try {
// Use domcontentloaded instead of networkidle - let __og_ready handle readiness
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: NAVIGATION_TIMEOUT });
const response = await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: NAVIGATION_TIMEOUT,
});
if (response) {
console.log(`Page loaded: ${response.status()} ${response.statusText()}`);
}
// Wait for the frontend to signal readiness
try {
await page.waitForFunction('window.__og_ready === true', {
timeout: NAVIGATION_TIMEOUT,
});
console.log('Frontend signalled ready');
} catch {
// Proceed anyway — partial screenshot is better than nothing
console.warn('Timed out waiting for __og_ready, proceeding with partial screenshot');
}
// Extra buffer for map tiles to finish rendering
@ -134,6 +156,33 @@ export async function takeScreenshot(url: string): Promise<Buffer> {
const screenshot = await page.screenshot({ type: 'png' });
return Buffer.from(screenshot);
} finally {
// Remove listeners before releasing page back to pool
page.removeAllListeners('console');
page.removeAllListeners('pageerror');
await releasePage(page);
}
}
export async function checkWebGL(): Promise<Record<string, unknown>> {
const page = await acquirePage();
try {
await page.setContent('<canvas id="c" width="1" height="1"></canvas>');
const info = await page.evaluate(() => {
const canvas = document.getElementById('c') as HTMLCanvasElement;
const gl =
canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) return { webgl: false, error: 'No WebGL context available' };
const g = gl as WebGLRenderingContext;
const debugExt = g.getExtension('WEBGL_debug_renderer_info');
return {
webgl: true,
version: g.getParameter(g.VERSION),
renderer: debugExt ? g.getParameter(debugExt.UNMASKED_RENDERER_WEBGL) : 'unknown',
vendor: debugExt ? g.getParameter(debugExt.UNMASKED_VENDOR_WEBGL) : 'unknown',
};
});
return info;
} finally {
await releasePage(page);
}

View file

@ -1,6 +1,6 @@
import express from 'express';
import { ScreenshotCache } from './cache.js';
import { takeScreenshot, closeBrowser } from './screenshot.js';
import { takeScreenshot, checkWebGL, closeBrowser } from './screenshot.js';
const PORT = parseInt(process.env.PORT || '8002', 10);
const NARROWIT_URL = process.env.NARROWIT_URL || 'http://localhost:8001';
@ -13,10 +13,19 @@ app.get('/health', (_req, res) => {
res.status(200).send('ok');
});
app.get('/debug', async (_req, res) => {
try {
const info = await checkWebGL();
res.json(info);
} catch (err) {
res.status(500).json({ error: String(err) });
}
});
app.get('/screenshot', async (req, res) => {
try {
const params: Record<string, string> = {};
for (const key of ['v', 'f', 'poi', 'tab']) {
for (const key of ['v', 'f', 'poi', 'tab', 'og']) {
const val = req.query[key];
if (typeof val === 'string' && val) {
params[key] = val;
@ -57,7 +66,7 @@ app.get('/screenshot', async (req, res) => {
});
const server = app.listen(PORT, () => {
console.log(`OG screenshot service listening on port ${PORT}`);
console.log(`Screenshot service listening on port ${PORT}`);
console.log(` NARROWIT_URL: ${NARROWIT_URL}`);
console.log(` CACHE_DIR: ${CACHE_DIR}`);
});