Skip to content

Instantly share code, notes, and snippets.

@signalwerk
Last active October 18, 2024 06:46
Show Gist options
  • Save signalwerk/546cad0a985c526f47ce3264e1055a67 to your computer and use it in GitHub Desktop.
Save signalwerk/546cad0a985c526f47ce3264e1055a67 to your computer and use it in GitHub Desktop.
CLI screenshot helper
#!/usr/bin/env node
import puppeteer from "puppeteer";
import { URL } from "url";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import fs from "fs";
import path from "path";
const INDENT = " ";
(async () => {
// Parse arguments using yargs for better named argument support
const argv = yargs(hideBin(process.argv))
.usage("Usage: $0 <url> [options]")
.demandCommand(1)
.option("resolution", {
alias: "r",
describe: "Screen resolution in format WIDTHxHEIGHT",
default: "1920x1080",
type: "string",
})
.option("clickSelector", {
alias: "c",
describe:
"CSS selector to click before taking the screenshot (e.g., to dismiss a cookie warning)",
type: "string",
})
.option("folder", {
alias: "f",
describe: "Subfolder where the screenshot will be saved",
default: "captures",
type: "string",
})
.option("wait", {
alias: "w",
describe:
"Time to wait in milliseconds after page load before taking the screenshot",
default: 0,
type: "number",
})
.option("username", {
alias: "u",
describe: "Username for Basic Authentication",
type: "string",
})
.option("password", {
alias: "p",
describe: "Password for Basic Authentication",
type: "string",
})
.help().argv;
const url = argv._[0];
const resolution = argv.resolution;
const clickSelector = argv.clickSelector;
const folder = argv.folder;
const waitTime = argv.wait;
const username = argv.username;
const password = argv.password;
const [width, height] = resolution.replace("×", "x").split("x").map(Number);
if (!width || !height) {
console.error("Invalid resolution format. Use the format: 1920x1080");
process.exit(1);
}
console.log(`Starting screenshot process for URL: ${url}`);
console.log(
`${INDENT}Using resolution: ${width}x${height} and device scale factor: 2`,
);
console.log(`${INDENT}Screenshots will be saved in folder: ${folder}`);
console.log(
`${INDENT}Waiting for ${waitTime}ms after page load before taking the screenshot`,
);
try {
// Ensure the folder exists, or create it
const folderPath = path.resolve(folder);
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath, { recursive: true });
console.log(`${INDENT}Created folder: ${folderPath}`);
}
const browser = await puppeteer.launch();
console.log(`${INDENT}Browser launched`);
const page = await browser.newPage();
console.log(`${INDENT}New page created`);
// Set Basic Authentication if username and password are provided
if (username && password) {
console.log(`${INDENT}Setting up Basic Authentication`);
await page.authenticate({
username: username,
password: password,
});
console.log(`${INDENT}Basic Authentication configured`);
}
await page.setViewport({
width,
height,
deviceScaleFactor: 2,
});
console.log(
`${INDENT}Viewport set to: ${width}x${height} with device scale factor 2`,
);
await page.goto(url, { waitUntil: "networkidle2" });
console.log(`${INDENT}Navigated to: ${url}`);
// Optionally click the provided selector to remove cookie warnings or popups
if (clickSelector) {
console.log(
`${INDENT}Attempting to click element with selector: ${clickSelector}`,
);
try {
const selection = await page.waitForSelector(clickSelector, {
timeout: 5000,
});
if (!selection) {
console.warn(
`${INDENT}Warning: Could not find the element with selector: ${clickSelector}. Proceeding without clicking.`,
);
} else {
await page.click(clickSelector);
console.log(
`${INDENT}Successfully clicked element: ${clickSelector}`,
);
// Wait for 500ms after the click to ensure the page updates
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(`${INDENT}Waited 500ms after clicking the element`);
}
} catch (error) {
console.error(
`${INDENT}Error clicking element with selector: ${clickSelector}`,
error,
);
}
}
// Wait for the specified time after the page has loaded
if (waitTime > 0) {
console.log(
`${INDENT}Waiting for ${waitTime}ms before taking the screenshot`,
);
await new Promise((resolve) => setTimeout(resolve, waitTime));
}
// Parse the URL and construct the output file name
const parsedUrl = new URL(url);
const domain = parsedUrl.hostname.replace(/^www\./, "").replace(/\./g, "-");
const pathName = parsedUrl.pathname
.replace(/\//g, "--")
.replace(/^-+|-+$/g, "");
const outputFileName = `${domain}${pathName ? "--" + pathName : ""}.png`;
const outputPath = path.join(folderPath, outputFileName);
console.log(`${INDENT}Taking screenshot`);
await page.screenshot({ path: outputPath });
console.log(`${INDENT}Screenshot saved successfully as ${outputPath}`);
await browser.close();
console.log(`${INDENT}Browser closed`);
} catch (error) {
console.error(`${INDENT}Error during the screenshot process:`, error);
}
})();
{
"name": "capture-screenshot",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"puppeteer": "^23.5.2",
"yargs": "^17.7.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment