mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-06-21 11:00:54 -04:00
187 lines
7.9 KiB
JavaScript
Executable File
187 lines
7.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const puppeteer = require('puppeteer');
|
|
|
|
const DEFAULT_URL = 'http://web.archivebox.localhost:8000/add/';
|
|
const DEFAULT_OUTPUT = 'tmp/add-page.png';
|
|
const DEFAULT_CHROME = '/Users/squash/Library/Application Support/abx/lib/puppeteer/bin/chromium';
|
|
|
|
function usage() {
|
|
console.log(`
|
|
Usage:
|
|
node bin/take_screenshot.js [url] [output.png]
|
|
|
|
Environment:
|
|
SESSIONID Django session cookie value for admin.archivebox.localhost
|
|
SCREENSHOT_COOKIE_NAME Cookie name, defaults to sessionid
|
|
SCREENSHOT_COOKIE_DOMAIN Cookie domain, defaults to admin.archivebox.localhost
|
|
CHROME_BINARY Chromium/Chrome executable path
|
|
PUPPETEER_EXECUTABLE_PATH Chromium/Chrome executable path
|
|
SCREENSHOT_WIDTH Viewport width, defaults to 1600
|
|
SCREENSHOT_HEIGHT Viewport height, defaults to 1400
|
|
SCREENSHOT_FULL_PAGE Set to 1 to capture the full page, defaults to viewport only
|
|
SCREENSHOT_SCROLL_SELECTOR Scroll this selector into view before capture
|
|
SCREENSHOT_WAIT_SELECTOR Wait for this selector before capture
|
|
SCREENSHOT_CLICK_SELECTOR Click this selector before capture
|
|
SCREENSHOT_AFTER_CLICK_WAIT_SELECTOR Wait for this selector after clicking
|
|
SCREENSHOT_HOST_RESOLVER_RULES Chrome host resolver rules
|
|
SCREENSHOT_SNAPSHOT_VIEW Set to list or grid before loading the page
|
|
SCREENSHOT_RESET_FILTERS Set to 1 to clear the admin filter collapsed preference
|
|
`);
|
|
}
|
|
|
|
function argValue(name) {
|
|
const idx = process.argv.indexOf(name);
|
|
return idx === -1 ? null : process.argv[idx + 1];
|
|
}
|
|
|
|
function firstPositionalArg(index) {
|
|
return process.argv.slice(2).filter((arg) => !arg.startsWith('--'))[index] || null;
|
|
}
|
|
|
|
function chromePath() {
|
|
const configured = process.env.CHROME_BINARY || process.env.PUPPETEER_EXECUTABLE_PATH || DEFAULT_CHROME;
|
|
return fs.existsSync(configured) ? configured : undefined;
|
|
}
|
|
|
|
async function main() {
|
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
const url = argValue('--url') || firstPositionalArg(0) || DEFAULT_URL;
|
|
const output = path.resolve(argValue('--output') || firstPositionalArg(1) || DEFAULT_OUTPUT);
|
|
const width = Number(process.env.SCREENSHOT_WIDTH || 1600);
|
|
const height = Number(process.env.SCREENSHOT_HEIGHT || 1400);
|
|
const fullPage = process.env.SCREENSHOT_FULL_PAGE === '1';
|
|
|
|
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
|
|
const launchOptions = {
|
|
headless: true,
|
|
defaultViewport: { width, height },
|
|
// Default protocolTimeout is 30s; admin pages with many DB-backed
|
|
// partials (snapshot grid, progress monitor, etc.) routinely take
|
|
// longer to capture on a busy server. Lift to 5min — failures should
|
|
// be the server actually being slow, not the CDP heartbeat.
|
|
protocolTimeout: 300_000,
|
|
};
|
|
if (process.env.SCREENSHOT_HOST_RESOLVER_RULES) {
|
|
launchOptions.args = [`--host-resolver-rules=${process.env.SCREENSHOT_HOST_RESOLVER_RULES}`];
|
|
}
|
|
const executablePath = chromePath();
|
|
if (executablePath) {
|
|
launchOptions.executablePath = executablePath;
|
|
}
|
|
|
|
const browser = await puppeteer.launch(launchOptions);
|
|
try {
|
|
const page = await browser.newPage();
|
|
page.setDefaultTimeout(45000);
|
|
|
|
if (process.env.SCREENSHOT_SNAPSHOT_VIEW || process.env.SCREENSHOT_RESET_FILTERS === '1') {
|
|
await page.evaluateOnNewDocument((snapshotView, resetFilters) => {
|
|
if (snapshotView) localStorage.setItem('preferred_snapshot_view_mode', snapshotView);
|
|
if (resetFilters) localStorage.removeItem('admin-filters-collapsed');
|
|
}, process.env.SCREENSHOT_SNAPSHOT_VIEW || '', process.env.SCREENSHOT_RESET_FILTERS === '1');
|
|
}
|
|
|
|
if (process.env.SESSIONID) {
|
|
const cookie = {
|
|
name: process.env.SCREENSHOT_COOKIE_NAME || 'sessionid',
|
|
value: process.env.SESSIONID,
|
|
path: '/',
|
|
};
|
|
if (process.env.SCREENSHOT_COOKIE_DOMAIN) {
|
|
cookie.domain = process.env.SCREENSHOT_COOKIE_DOMAIN;
|
|
} else {
|
|
cookie.url = new URL(url).origin;
|
|
}
|
|
await page.setCookie(cookie);
|
|
}
|
|
|
|
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
|
|
await page.waitForSelector('body');
|
|
await page.waitForSelector('#progress-monitor, #add-form', { timeout: 5000 }).catch(() => {});
|
|
if (process.env.SCREENSHOT_WAIT_SELECTOR) {
|
|
await page.waitForSelector(process.env.SCREENSHOT_WAIT_SELECTOR, { timeout: 45000 });
|
|
}
|
|
if (process.env.SCREENSHOT_CLICK_SELECTOR) {
|
|
await page.waitForSelector(process.env.SCREENSHOT_CLICK_SELECTOR, { timeout: 45000 });
|
|
await page.click(process.env.SCREENSHOT_CLICK_SELECTOR);
|
|
}
|
|
if (process.env.SCREENSHOT_AFTER_CLICK_WAIT_SELECTOR) {
|
|
await page.waitForSelector(process.env.SCREENSHOT_AFTER_CLICK_WAIT_SELECTOR, { timeout: 45000 });
|
|
}
|
|
if (process.env.SCREENSHOT_SCROLL_SELECTOR) {
|
|
await page.waitForSelector(process.env.SCREENSHOT_SCROLL_SELECTOR, { timeout: 45000 }).catch(() => {});
|
|
await page.evaluate((selector) => {
|
|
document.querySelector(selector)?.scrollIntoView({ block: 'start', inline: 'nearest' });
|
|
}, process.env.SCREENSHOT_SCROLL_SELECTOR);
|
|
}
|
|
|
|
const frameHandle = await page.$('.crawl-snapshots-embed iframe');
|
|
if (frameHandle) {
|
|
const frame = await frameHandle.contentFrame();
|
|
if (frame) {
|
|
await frame.waitForSelector('#changelist-form', { timeout: 45000 }).catch(() => {});
|
|
await frame.waitForSelector('#result_list', { timeout: 45000 }).catch(() => {});
|
|
}
|
|
}
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1200));
|
|
|
|
const checks = await page.evaluate(() => ({
|
|
url: location.href,
|
|
title: document.title,
|
|
progressMonitorDisplay: document.querySelector('#progress-monitor')
|
|
? getComputedStyle(document.querySelector('#progress-monitor')).display
|
|
: 'missing',
|
|
progressCrawls: document.querySelectorAll('#progress-monitor .crawl-item').length,
|
|
progressSnapshots: document.querySelectorAll('#progress-monitor .snapshot-item').length,
|
|
screencastVisible: Boolean(document.querySelector('#progress-monitor .screencast-panel.visible')),
|
|
screencastImageLoaded: (() => {
|
|
const img = document.querySelector('#progress-monitor .screencast-panel.visible img');
|
|
return Boolean(img && img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
|
|
})(),
|
|
screencastImageSize: (() => {
|
|
const img = document.querySelector('#progress-monitor .screencast-panel.visible img');
|
|
return img ? `${img.naturalWidth}x${img.naturalHeight}` : '';
|
|
})(),
|
|
snapshotEmbed: Boolean(document.querySelector('.crawl-snapshots-embed iframe')),
|
|
addForm: Boolean(document.querySelector('#add-form')),
|
|
limitFields: Array.from(document.querySelectorAll('.crawl-limit-field label')).map((el) => el.textContent.trim()),
|
|
}));
|
|
|
|
let frameChecks = null;
|
|
const embeddedFrameHandle = await page.$('.crawl-snapshots-embed iframe');
|
|
if (embeddedFrameHandle) {
|
|
const frame = await embeddedFrameHandle.contentFrame();
|
|
if (frame) {
|
|
frameChecks = await frame.evaluate(() => ({
|
|
rows: document.querySelectorAll('#result_list tbody tr').length,
|
|
actionCheckboxes: document.querySelectorAll('#result_list input.action-select').length,
|
|
searchModeRadios: document.querySelectorAll('#changelist-search input[type="radio"][name="search_mode"]').length,
|
|
progressMonitorDisplay: document.querySelector('#progress-monitor')
|
|
? getComputedStyle(document.querySelector('#progress-monitor')).display
|
|
: 'missing',
|
|
}));
|
|
}
|
|
}
|
|
|
|
await page.screenshot({ path: output, fullPage });
|
|
console.log(JSON.stringify({ screenshotPath: output, checks, frameChecks }, null, 2));
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|