screentake 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +519 -0
- data/Rakefile +12 -0
- data/lib/generators/screentake/install/install_generator.rb +15 -0
- data/lib/generators/screentake/install/templates/screentake.rb +25 -0
- data/lib/screentake/configuration.rb +27 -0
- data/lib/screentake/drivers/base.rb +19 -0
- data/lib/screentake/drivers/cloudflare.rb +188 -0
- data/lib/screentake/drivers/ferrum.rb +176 -0
- data/lib/screentake/errors.rb +54 -0
- data/lib/screentake/railtie.rb +18 -0
- data/lib/screentake/screenshot.rb +250 -0
- data/lib/screentake/version.rb +5 -0
- data/lib/screentake.rb +80 -0
- metadata +74 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d6fe6b26493d9070ad6db10fd5accd18b1ada36cd60eccc157c0378cc24d28f1
|
|
4
|
+
data.tar.gz: 94b23b9f579ad1d7cbc4244d5d54260d692bf94e88a15384c1573bfb8602b62f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: aecb55173a821838fe18c495b38c0f2caa34415d8b0685925403ccbdd96fb891d3905887d206256e6dc632b506f5fcf342f8de2f73249d9a2265f63a105fea63
|
|
7
|
+
data.tar.gz: 4d34057305663ca48d3e520ed25d213b12271f13e35ebb28dc541ebf0f9382ce364d52730148adf6d816f76c6c811e65627cf034a7194d15f1d198b4543394a1
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maful Prayoga
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# Screentake
|
|
2
|
+
|
|
3
|
+
A Ruby gem for capturing web page screenshots with a clean, keyword-driven API. Supports both the Cloudflare Browser Rendering API and a local Ferrum (headless Chrome) driver.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Screentake provides a simple interface for turning URLs or raw HTML into PNG, JPEG, or WebP screenshots. It handles viewport configuration, device scale factors, full-page captures, element-specific screenshots, and various wait strategies out of the box.
|
|
8
|
+
|
|
9
|
+
The gem ships with two interchangeable drivers. The **Cloudflare** driver sends rendering requests to the [Cloudflare Browser Rendering API](https://developers.cloudflare.com/browser-rendering/) -- no local browser required. The **Ferrum** driver uses headless Chrome on your own machine via the [Ferrum](https://github.com/rubycdp/ferrum) gem. Both drivers share the same options interface, so switching between them is a one-line configuration change.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **URL and HTML rendering** -- pass a URL or an HTML string
|
|
14
|
+
- **Multiple output formats** -- PNG, JPEG, WebP
|
|
15
|
+
- **Viewport control** -- width, height, and device scale factor (1-4x)
|
|
16
|
+
- **Full-page capture** -- screenshot the entire scrollable page
|
|
17
|
+
- **Element targeting** -- capture a specific CSS selector
|
|
18
|
+
- **Clip regions** -- crop to an arbitrary rectangle
|
|
19
|
+
- **Transparent backgrounds** -- omit the default white background
|
|
20
|
+
- **Wait strategies** -- wait for load, DOMContentLoaded, network idle, a selector, or a fixed timeout
|
|
21
|
+
- **Automatic retries** -- Cloudflare driver retries on 429 rate limits with exponential backoff
|
|
22
|
+
- **Three output methods** -- raw binary, Base64, or save directly to a file
|
|
23
|
+
- **Rails auto-integration** -- Railtie loaded automatically when Rails is present
|
|
24
|
+
- **Driver pattern** -- swap between Cloudflare and Ferrum without changing application code
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Add to your Gemfile:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "screentake"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bundle install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or install directly:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gem install screentake
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you plan to use the Ferrum driver, also add:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
gem "ferrum", "~> 0.15"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Rails Installation
|
|
53
|
+
|
|
54
|
+
After adding screentake to your Gemfile, run the install generator:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bundle install
|
|
58
|
+
rails generate screentake:install
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This creates `config/initializers/screentake.rb` with default configuration. Set your Cloudflare credentials via environment variables (`CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`) or edit the initializer directly.
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
require "screentake"
|
|
67
|
+
|
|
68
|
+
Screentake.configure do |config|
|
|
69
|
+
config.cloudflare_account_id = ENV["CLOUDFLARE_ACCOUNT_ID"]
|
|
70
|
+
config.cloudflare_api_token = ENV["CLOUDFLARE_API_TOKEN"]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Take a screenshot and save it
|
|
74
|
+
screenshot = Screentake.take(url: "https://example.com")
|
|
75
|
+
screenshot.save("example.png")
|
|
76
|
+
|
|
77
|
+
# Or get the binary data directly
|
|
78
|
+
binary = screenshot.capture
|
|
79
|
+
|
|
80
|
+
# Or get Base64 for embedding in HTML/JSON
|
|
81
|
+
encoded = screenshot.base64
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
Configure Screentake globally with `Screentake.configure`:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
Screentake.configure do |config|
|
|
90
|
+
# Driver selection (:cloudflare or :ferrum)
|
|
91
|
+
config.driver = :cloudflare
|
|
92
|
+
|
|
93
|
+
# Cloudflare credentials (required for :cloudflare driver)
|
|
94
|
+
config.cloudflare_account_id = ENV["CLOUDFLARE_ACCOUNT_ID"]
|
|
95
|
+
config.cloudflare_api_token = ENV["CLOUDFLARE_API_TOKEN"]
|
|
96
|
+
|
|
97
|
+
# Default viewport dimensions
|
|
98
|
+
config.default_viewport = { width: 1280, height: 800 }
|
|
99
|
+
|
|
100
|
+
# Device scale factor (1-4, default: 2)
|
|
101
|
+
config.default_scale = 2
|
|
102
|
+
|
|
103
|
+
# Default image format (:png, :jpeg, :webp)
|
|
104
|
+
config.default_format = :png
|
|
105
|
+
|
|
106
|
+
# Navigation timeout in milliseconds (default: 30000)
|
|
107
|
+
config.timeout = 30_000
|
|
108
|
+
|
|
109
|
+
# Logger instance for debug/retry output (default: nil)
|
|
110
|
+
config.logger = Rails.logger
|
|
111
|
+
|
|
112
|
+
# Ferrum browser options (only used with :ferrum driver)
|
|
113
|
+
config.ferrum_options = { headless: true }
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Configuration Defaults
|
|
118
|
+
|
|
119
|
+
| Option | Default | Description |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `driver` | `:cloudflare` | Which rendering backend to use |
|
|
122
|
+
| `cloudflare_account_id` | `nil` | Your Cloudflare account ID |
|
|
123
|
+
| `cloudflare_api_token` | `nil` | Your Cloudflare API token |
|
|
124
|
+
| `default_viewport` | `{ width: 1280, height: 800 }` | Browser viewport size |
|
|
125
|
+
| `default_scale` | `2` | Device scale factor (Retina) |
|
|
126
|
+
| `default_format` | `:png` | Output image format |
|
|
127
|
+
| `timeout` | `30000` | Navigation timeout (ms) |
|
|
128
|
+
| `logger` | `nil` | Logger for debug output |
|
|
129
|
+
| `ferrum_options` | `{ headless: true }` | Options passed to `Ferrum::Browser.new` |
|
|
130
|
+
|
|
131
|
+
## Usage Examples
|
|
132
|
+
|
|
133
|
+
### Basic URL Screenshot
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
screenshot = Screentake.take(url: "https://example.com")
|
|
137
|
+
screenshot.save("output.png")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### HTML Content Screenshot
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
html = <<~HTML
|
|
144
|
+
<html>
|
|
145
|
+
<body style="background: #1a1a2e; color: #eee; padding: 40px;">
|
|
146
|
+
<h1>Hello from Screentake</h1>
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|
|
149
|
+
HTML
|
|
150
|
+
|
|
151
|
+
screenshot = Screentake.take(html: html)
|
|
152
|
+
screenshot.save("rendered.png")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Note: `url:` and `html:` are mutually exclusive. Passing both raises `ArgumentError`.
|
|
156
|
+
|
|
157
|
+
### Full-Page Capture
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
screenshot = Screentake.take(
|
|
161
|
+
url: "https://example.com/long-page",
|
|
162
|
+
full_page: true,
|
|
163
|
+
)
|
|
164
|
+
screenshot.save("full_page.png")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Custom Viewport and Scale
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# Using width: and height:
|
|
171
|
+
screenshot = Screentake.take(
|
|
172
|
+
url: "https://example.com",
|
|
173
|
+
width: 1920,
|
|
174
|
+
height: 1080,
|
|
175
|
+
scale: 3,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Using size: shorthand
|
|
179
|
+
screenshot = Screentake.take(
|
|
180
|
+
url: "https://example.com",
|
|
181
|
+
size: [375, 812], # iPhone viewport
|
|
182
|
+
scale: 3,
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Selector-Based Screenshot
|
|
187
|
+
|
|
188
|
+
Capture only a specific element:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
screenshot = Screentake.take(
|
|
192
|
+
url: "https://example.com",
|
|
193
|
+
selector: "#hero-section",
|
|
194
|
+
)
|
|
195
|
+
screenshot.save("hero.png")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Clip Region
|
|
199
|
+
|
|
200
|
+
Crop to an arbitrary rectangle:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
screenshot = Screentake.take(
|
|
204
|
+
url: "https://example.com",
|
|
205
|
+
clip: { x: 0, y: 0, width: 600, height: 400 },
|
|
206
|
+
)
|
|
207
|
+
screenshot.save("cropped.png")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Different Formats
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
# JPEG with quality setting
|
|
214
|
+
screenshot = Screentake.take(
|
|
215
|
+
url: "https://example.com",
|
|
216
|
+
format: :jpeg,
|
|
217
|
+
quality: 85,
|
|
218
|
+
)
|
|
219
|
+
screenshot.save("output.jpg")
|
|
220
|
+
|
|
221
|
+
# WebP with quality setting
|
|
222
|
+
screenshot = Screentake.take(
|
|
223
|
+
url: "https://example.com",
|
|
224
|
+
format: :webp,
|
|
225
|
+
quality: 90,
|
|
226
|
+
)
|
|
227
|
+
screenshot.save("output.webp")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Note: `quality:` is only valid for JPEG and WebP. Passing it with PNG format raises `InvalidOptionError`.
|
|
231
|
+
|
|
232
|
+
The `.save` method also infers format from the file extension. Saving to `output.jpg` will produce a JPEG regardless of the `format:` option.
|
|
233
|
+
|
|
234
|
+
### Transparent Background
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
screenshot = Screentake.take(
|
|
238
|
+
url: "https://example.com",
|
|
239
|
+
transparent: true,
|
|
240
|
+
format: :png,
|
|
241
|
+
)
|
|
242
|
+
screenshot.save("transparent.png")
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Wait Strategies
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
# Wait until there are zero network connections for 500ms
|
|
249
|
+
screenshot = Screentake.take(
|
|
250
|
+
url: "https://example.com",
|
|
251
|
+
wait_until: :networkidle0,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Wait for a specific element to appear
|
|
255
|
+
screenshot = Screentake.take(
|
|
256
|
+
url: "https://example.com",
|
|
257
|
+
wait_for_selector: ".chart-loaded",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Add a fixed delay (in milliseconds, 1-30000)
|
|
261
|
+
screenshot = Screentake.take(
|
|
262
|
+
url: "https://example.com",
|
|
263
|
+
wait_for_timeout: 2000,
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Available `wait_until` values:
|
|
268
|
+
|
|
269
|
+
| Value | Description |
|
|
270
|
+
|---|---|
|
|
271
|
+
| `:load` | Wait for the `load` event |
|
|
272
|
+
| `:domcontentloaded` | Wait for `DOMContentLoaded` event |
|
|
273
|
+
| `:networkidle0` | Wait until zero network connections for 500ms (default) |
|
|
274
|
+
| `:networkidle2` | Wait until two or fewer network connections for 500ms |
|
|
275
|
+
|
|
276
|
+
### Output Methods
|
|
277
|
+
|
|
278
|
+
Every `Screentake.take` call returns a `Screenshot` object with three output methods:
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
screenshot = Screentake.take(url: "https://example.com")
|
|
282
|
+
|
|
283
|
+
# Raw binary data
|
|
284
|
+
binary = screenshot.capture
|
|
285
|
+
|
|
286
|
+
# Base64-encoded string
|
|
287
|
+
encoded = screenshot.base64
|
|
288
|
+
|
|
289
|
+
# Save to file (creates parent directories automatically, returns the path)
|
|
290
|
+
path = screenshot.save("screenshots/example.png")
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Debugging
|
|
294
|
+
|
|
295
|
+
Inspect the resolved options for a screenshot:
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
screenshot = Screentake.take(url: "https://example.com", full_page: true)
|
|
299
|
+
pp screenshot.debug
|
|
300
|
+
# {
|
|
301
|
+
# source_type: :url,
|
|
302
|
+
# source: "https://example.com",
|
|
303
|
+
# width: 1280,
|
|
304
|
+
# height: 800,
|
|
305
|
+
# scale: 2,
|
|
306
|
+
# format: :png,
|
|
307
|
+
# quality: nil,
|
|
308
|
+
# full_page: true,
|
|
309
|
+
# selector: nil,
|
|
310
|
+
# clip: nil,
|
|
311
|
+
# transparent: false,
|
|
312
|
+
# wait_until: :networkidle0,
|
|
313
|
+
# wait_for_selector: nil,
|
|
314
|
+
# wait_for_timeout: nil,
|
|
315
|
+
# driver: :cloudflare,
|
|
316
|
+
# }
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Drivers
|
|
320
|
+
|
|
321
|
+
### Cloudflare (Default)
|
|
322
|
+
|
|
323
|
+
The Cloudflare driver sends requests to the [Cloudflare Browser Rendering API](https://developers.cloudflare.com/browser-rendering/). No local browser installation needed.
|
|
324
|
+
|
|
325
|
+
```ruby
|
|
326
|
+
Screentake.configure do |config|
|
|
327
|
+
config.driver = :cloudflare
|
|
328
|
+
config.cloudflare_account_id = ENV["CLOUDFLARE_ACCOUNT_ID"]
|
|
329
|
+
config.cloudflare_api_token = ENV["CLOUDFLARE_API_TOKEN"]
|
|
330
|
+
end
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Rate limits:** Cloudflare enforces 2 browsers per minute and 2 concurrent requests. The driver automatically retries up to 3 times on HTTP 429 responses with exponential backoff (1s, 2s, 4s).
|
|
334
|
+
|
|
335
|
+
### Ferrum (Local Chrome)
|
|
336
|
+
|
|
337
|
+
The Ferrum driver runs a local headless Chrome instance via the [Ferrum](https://github.com/rubycdp/ferrum) gem. It requires Chrome or Chromium installed on the machine.
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
# Gemfile
|
|
341
|
+
gem "ferrum", "~> 0.15"
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
```ruby
|
|
345
|
+
Screentake.configure do |config|
|
|
346
|
+
config.driver = :ferrum
|
|
347
|
+
config.ferrum_options = {
|
|
348
|
+
headless: true,
|
|
349
|
+
# browser_path: "/usr/bin/chromium", # custom Chrome path
|
|
350
|
+
# timeout: 60, # Ferrum process timeout
|
|
351
|
+
}
|
|
352
|
+
end
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The Ferrum driver lazily starts the browser on the first screenshot, reuses it for subsequent requests (each screenshot gets its own browser context), and automatically shuts it down on process exit.
|
|
356
|
+
|
|
357
|
+
### Choosing a Driver
|
|
358
|
+
|
|
359
|
+
| | Cloudflare | Ferrum |
|
|
360
|
+
|---|---|---|
|
|
361
|
+
| **Setup** | API credentials only | Chrome/Chromium installed locally |
|
|
362
|
+
| **Infrastructure** | No local browser needed | Runs on your server |
|
|
363
|
+
| **Rate limits** | 2/min, 2 concurrent | Limited by your hardware |
|
|
364
|
+
| **Cost** | Cloudflare pricing applies | Free (your compute) |
|
|
365
|
+
| **Best for** | Production, serverless | Development, CI, high volume |
|
|
366
|
+
|
|
367
|
+
## API Reference
|
|
368
|
+
|
|
369
|
+
### `Screentake.take(**options)`
|
|
370
|
+
|
|
371
|
+
Creates a `Screenshot` instance. Validates all options eagerly -- invalid options raise immediately, before any network request.
|
|
372
|
+
|
|
373
|
+
**Source options** (exactly one required):
|
|
374
|
+
|
|
375
|
+
| Option | Type | Description |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| `url:` | `String` | URL to capture (http/https only) |
|
|
378
|
+
| `html:` | `String` | Raw HTML content to render |
|
|
379
|
+
|
|
380
|
+
**Viewport options:**
|
|
381
|
+
|
|
382
|
+
| Option | Type | Default | Description |
|
|
383
|
+
|---|---|---|---|
|
|
384
|
+
| `width:` | `Integer` | `1280` | Viewport width (1-10000) |
|
|
385
|
+
| `height:` | `Integer` | `800` | Viewport height (1-10000) |
|
|
386
|
+
| `size:` | `Array` | -- | Shorthand `[width, height]` |
|
|
387
|
+
| `scale:` | `Numeric` | `2` | Device scale factor (1-4) |
|
|
388
|
+
|
|
389
|
+
**Capture options:**
|
|
390
|
+
|
|
391
|
+
| Option | Type | Default | Description |
|
|
392
|
+
|---|---|---|---|
|
|
393
|
+
| `format:` | `Symbol` | `:png` | `:png`, `:jpeg`, or `:webp` |
|
|
394
|
+
| `quality:` | `Integer` | `nil` | 1-100, JPEG/WebP only |
|
|
395
|
+
| `full_page:` | `Boolean` | `false` | Capture full scrollable page |
|
|
396
|
+
| `selector:` | `String` | `nil` | CSS selector to capture |
|
|
397
|
+
| `clip:` | `Hash` | `nil` | `{ x:, y:, width:, height: }` |
|
|
398
|
+
| `transparent:` | `Boolean` | `false` | Transparent background |
|
|
399
|
+
|
|
400
|
+
**Wait options:**
|
|
401
|
+
|
|
402
|
+
| Option | Type | Default | Description |
|
|
403
|
+
|---|---|---|---|
|
|
404
|
+
| `wait_until:` | `Symbol` | `:networkidle0` | Page load strategy |
|
|
405
|
+
| `wait_for_selector:` | `String` | `nil` | Wait for this CSS selector |
|
|
406
|
+
| `wait_for_timeout:` | `Numeric` | `nil` | Fixed delay in ms (1-30000) |
|
|
407
|
+
|
|
408
|
+
### `Screenshot#capture`
|
|
409
|
+
|
|
410
|
+
Returns raw binary image data as a `String`.
|
|
411
|
+
|
|
412
|
+
### `Screenshot#base64`
|
|
413
|
+
|
|
414
|
+
Returns the screenshot as a Base64-encoded `String` (strict encoding, no newlines).
|
|
415
|
+
|
|
416
|
+
### `Screenshot#save(path)`
|
|
417
|
+
|
|
418
|
+
Writes the screenshot to `path`, creating parent directories as needed. Infers format from the file extension (`.jpg`/`.jpeg` to JPEG, `.webp` to WebP, `.png` to PNG). Returns the path as a `String`.
|
|
419
|
+
|
|
420
|
+
### `Screenshot#debug`
|
|
421
|
+
|
|
422
|
+
Returns a `Hash` of all resolved options for inspection.
|
|
423
|
+
|
|
424
|
+
### `Screentake.configure { |config| ... }`
|
|
425
|
+
|
|
426
|
+
Yields the global `Configuration` object for setting defaults.
|
|
427
|
+
|
|
428
|
+
### `Screentake.reset_configuration!`
|
|
429
|
+
|
|
430
|
+
Resets all configuration to defaults and shuts down the current driver. Useful in tests.
|
|
431
|
+
|
|
432
|
+
## Error Handling
|
|
433
|
+
|
|
434
|
+
All Screentake errors inherit from `Screentake::Error`, so you can rescue broadly or handle specific cases:
|
|
435
|
+
|
|
436
|
+
```ruby
|
|
437
|
+
begin
|
|
438
|
+
screenshot = Screentake.take(url: "https://example.com")
|
|
439
|
+
screenshot.save("output.png")
|
|
440
|
+
rescue Screentake::RateLimitError
|
|
441
|
+
# Cloudflare 429 after all retries exhausted
|
|
442
|
+
retry_later
|
|
443
|
+
rescue Screentake::AuthenticationError => e
|
|
444
|
+
# Invalid Cloudflare credentials
|
|
445
|
+
Rails.logger.error("Screentake auth failed: #{e.message}")
|
|
446
|
+
rescue Screentake::TimeoutError
|
|
447
|
+
# Page took too long to load
|
|
448
|
+
handle_timeout
|
|
449
|
+
rescue Screentake::ElementNotFoundError
|
|
450
|
+
# Selector matched no elements
|
|
451
|
+
use_fallback_image
|
|
452
|
+
rescue Screentake::Error => e
|
|
453
|
+
# Catch-all for any Screentake error
|
|
454
|
+
Rails.logger.error("Screenshot failed: #{e.message}")
|
|
455
|
+
end
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Error Hierarchy
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
Screentake::Error
|
|
462
|
+
Screentake::NotImplementedError
|
|
463
|
+
Screentake::InvalidUrlError
|
|
464
|
+
Screentake::InvalidHtmlError
|
|
465
|
+
Screentake::InvalidDimensionError
|
|
466
|
+
Screentake::InvalidOptionError
|
|
467
|
+
Screentake::InvalidClipError
|
|
468
|
+
Screentake::ElementNotFoundError
|
|
469
|
+
Screentake::TimeoutError
|
|
470
|
+
Screentake::RenderError
|
|
471
|
+
Screentake::DriverError
|
|
472
|
+
Screentake::CloudflareError
|
|
473
|
+
Screentake::RateLimitError
|
|
474
|
+
Screentake::AuthenticationError
|
|
475
|
+
Screentake::FerrumError
|
|
476
|
+
Screentake::BrowserNotFoundError
|
|
477
|
+
Screentake::BrowserCrashedError
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Development
|
|
481
|
+
|
|
482
|
+
### Setup
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
git clone https://github.com/maful/screentake.git
|
|
486
|
+
cd screentake
|
|
487
|
+
bin/setup
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Running Tests
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
rake test # Run all tests
|
|
494
|
+
rake rubocop # Run linter
|
|
495
|
+
rake # Run tests + rubocop (default)
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Interactive Console
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
bin/console
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Requirements
|
|
505
|
+
|
|
506
|
+
- Ruby >= 3.1
|
|
507
|
+
- Chrome or Chromium (for Ferrum driver only)
|
|
508
|
+
|
|
509
|
+
### Contributing
|
|
510
|
+
|
|
511
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/maful/screentake](https://github.com/maful/screentake). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/maful/screentake/blob/main/CODE_OF_CONDUCT.md).
|
|
512
|
+
|
|
513
|
+
## License
|
|
514
|
+
|
|
515
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
516
|
+
|
|
517
|
+
## Code of Conduct
|
|
518
|
+
|
|
519
|
+
Everyone interacting in the Screentake project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/maful/screentake/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Screentake
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Creates a Screentake initializer file."
|
|
9
|
+
|
|
10
|
+
def copy_initializer
|
|
11
|
+
template("screentake.rb", "config/initializers/screentake.rb")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Screentake.configure do |config|
|
|
4
|
+
# Driver to use for capturing screenshots.
|
|
5
|
+
# :cloudflare and :ferrum is supported.
|
|
6
|
+
config.driver = :cloudflare
|
|
7
|
+
|
|
8
|
+
# Cloudflare Browser Rendering API credentials.
|
|
9
|
+
# You can also set these via environment variables:
|
|
10
|
+
# CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN
|
|
11
|
+
config.cloudflare_account_id = ENV.fetch("CLOUDFLARE_ACCOUNT_ID", nil)
|
|
12
|
+
config.cloudflare_api_token = ENV.fetch("CLOUDFLARE_API_TOKEN", nil)
|
|
13
|
+
|
|
14
|
+
# Default viewport dimensions (pixels).
|
|
15
|
+
# config.default_viewport = { width: 1280, height: 800 }
|
|
16
|
+
|
|
17
|
+
# Default device scale factor (1-4). 2 produces retina-quality output.
|
|
18
|
+
# config.default_scale = 2
|
|
19
|
+
|
|
20
|
+
# Default image format (:png, :jpeg, :webp).
|
|
21
|
+
# config.default_format = :png
|
|
22
|
+
|
|
23
|
+
# Request timeout in milliseconds.
|
|
24
|
+
# config.timeout = 30_000
|
|
25
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Screentake
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :driver,
|
|
6
|
+
:cloudflare_account_id,
|
|
7
|
+
:cloudflare_api_token,
|
|
8
|
+
:default_viewport,
|
|
9
|
+
:default_scale,
|
|
10
|
+
:default_format,
|
|
11
|
+
:timeout,
|
|
12
|
+
:logger,
|
|
13
|
+
:ferrum_options
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@driver = :cloudflare
|
|
17
|
+
@cloudflare_account_id = nil
|
|
18
|
+
@cloudflare_api_token = nil
|
|
19
|
+
@default_viewport = { width: 1280, height: 800 }
|
|
20
|
+
@default_scale = 2
|
|
21
|
+
@default_format = :png
|
|
22
|
+
@timeout = 30_000
|
|
23
|
+
@logger = nil
|
|
24
|
+
@ferrum_options = { headless: true }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Screentake
|
|
4
|
+
module Drivers
|
|
5
|
+
# Abstract base class for screenshot drivers.
|
|
6
|
+
# All drivers must inherit from this class and implement #screenshot.
|
|
7
|
+
class Base
|
|
8
|
+
# Capture a screenshot and return binary image data.
|
|
9
|
+
#
|
|
10
|
+
# @param source [String] the URL or HTML content
|
|
11
|
+
# @param type [Symbol] :url or :html
|
|
12
|
+
# @param options [Hash] screenshot options (viewport, format, etc.)
|
|
13
|
+
# @return [String] binary image data
|
|
14
|
+
def screenshot(source:, type:, options:)
|
|
15
|
+
raise NotImplementedError, "#{self.class}#screenshot must be implemented"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|