-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
87 lines (79 loc) · 3.27 KB
/
Copy pathindex.js
File metadata and controls
87 lines (79 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const https = require('https');
const http = require('http');
const DEFAULT_BASE = 'https://grabshot.dev';
class GrabShot {
/**
* @param {Object} options
* @param {string} options.apiKey - Your GrabShot API key
* @param {string} [options.baseUrl] - Custom API base URL
*/
constructor({ apiKey, baseUrl } = {}) {
if (!apiKey) throw new Error('GrabShot: apiKey is required. Get one at https://grabshot.dev');
this.apiKey = apiKey;
this.baseUrl = (baseUrl || DEFAULT_BASE).replace(/\/$/, '');
}
/**
* Capture a screenshot of a URL
* @param {Object} params
* @param {string} params.url - URL to screenshot
* @param {number} [params.width=1280] - Viewport width
* @param {number} [params.height=800] - Viewport height
* @param {boolean} [params.fullPage=false] - Capture full page
* @param {string} [params.format='png'] - Output format: png, jpeg, webp
* @param {number} [params.quality] - JPEG/WebP quality (1-100)
* @param {number} [params.deviceScaleFactor=1] - Device scale (2 for retina)
* @param {boolean} [params.darkMode=false] - Emulate dark mode
* @param {number} [params.delay] - Wait ms before capture
* @param {boolean} [params.aiCleanup=false] - AI-powered cleanup (paid plans)
* @returns {Promise<Buffer>} Screenshot image buffer
*/
async capture(params = {}) {
if (!params.url) throw new Error('GrabShot: url is required');
const query = new URLSearchParams();
query.set('url', params.url);
query.set('api_key', this.apiKey);
if (params.width) query.set('width', params.width);
if (params.height) query.set('height', params.height);
if (params.fullPage) query.set('full_page', 'true');
if (params.format) query.set('format', params.format);
if (params.quality) query.set('quality', params.quality);
if (params.deviceScaleFactor) query.set('device_scale_factor', params.deviceScaleFactor);
if (params.darkMode) query.set('dark_mode', 'true');
if (params.delay) query.set('delay', params.delay);
if (params.aiCleanup) query.set('ai_cleanup', 'true');
const url = `${this.baseUrl}/api/screenshot?${query.toString()}`;
return this._request(url);
}
/**
* Get account usage info
* @returns {Promise<Object>} Usage data
*/
async usage() {
const url = `${this.baseUrl}/api/usage?api_key=${this.apiKey}`;
const buf = await this._request(url);
return JSON.parse(buf.toString());
}
_request(url) {
return new Promise((resolve, reject) => {
const mod = url.startsWith('https') ? https : http;
mod.get(url, { timeout: 60000 }, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return this._request(res.headers.location).then(resolve, reject);
}
const chunks = [];
res.on('data', (c) => chunks.push(c));
res.on('end', () => {
const buf = Buffer.concat(chunks);
if (res.statusCode >= 400) {
let msg;
try { msg = JSON.parse(buf.toString()).error; } catch { msg = buf.toString().slice(0, 200); }
return reject(new Error(`GrabShot API error ${res.statusCode}: ${msg}`));
}
resolve(buf);
});
res.on('error', reject);
}).on('error', reject);
});
}
}
module.exports = GrabShot;