renderscreenshot 1.0.0 → 1.0.1
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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +35 -4
- data/lib/renderscreenshot/client.rb +5 -47
- data/lib/renderscreenshot/take_options.rb +50 -44
- data/lib/renderscreenshot/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31b7d11d24a55a5e7ffae6e8ba80c81433d20b81ae82d95866eb56acb3e8613f
|
|
4
|
+
data.tar.gz: d3a1dc0bb1a01470d761ff05873fdaa9d8ae402ac56c9f0349705a09d685a08b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 380f44cd4faee08b39073403dcff73167a02df5acc368b426402d412da18503dbe3b8a03c477bfa2053916f5d5820a019458aff83fd7598bb908aab453c22869
|
|
7
|
+
data.tar.gz: b756575692352f742ba194cfc653cdcd95c883e114bae180053fb3d4c983e67b3cd0a6f217d14dcb75e24030b1e0e511b8fcbed05342af95196e3ebb84f12ad7
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.1] - 2026-10-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `TakeOptions#cache_available` sets `cache.available` (GET: `cache_available`).
|
|
13
|
+
The API reuses a cached screenshot only when this is set.
|
|
14
|
+
- `TakeOptions#to_query_params` returns the flat GET parameters as a hash.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `pdf_print_background` now sends `pdf.print_background`. Before, it sent
|
|
19
|
+
`pdf.background`, which the API ignores.
|
|
20
|
+
- `pdf_margin_top`/`_right`/`_bottom`/`_left` now send flat `pdf.margin_top`,
|
|
21
|
+
`pdf.margin_right`, `pdf.margin_bottom` and `pdf.margin_left`. Before, they
|
|
22
|
+
sent a nested `pdf.margin` hash, which the API drops. `pdf_margin` still
|
|
23
|
+
sends `pdf.margin`, and the per-side values can be combined with it.
|
|
24
|
+
- `device` is now sent as the top-level `device` parameter, which POST and GET
|
|
25
|
+
both read. Before, it was sent as `viewport.device`.
|
|
26
|
+
- `generate_url` now uses the GET parameter names (`selector`, `timeout`,
|
|
27
|
+
`block_cookies`, `pdf_paper`, ...). Before, it flattened the builder's own
|
|
28
|
+
names (`element`, `wait_for`, `block_cookie_banners`, ...), which GET does
|
|
29
|
+
not read. Options that GET cannot express are left out of the URL.
|
|
30
|
+
- `to_query_string` covers every GET parameter, and its pairs are sorted and
|
|
31
|
+
encoded like Rails `to_query`.
|
|
32
|
+
- Docs: `wait_for_timeout` is in seconds (the API clamps it to 1-60), not
|
|
33
|
+
milliseconds. The devices example reads `viewport.width`/`viewport.height`.
|
|
34
|
+
|
|
8
35
|
## [1.0.0] - 2026-02-24
|
|
9
36
|
|
|
10
37
|
### Added
|
data/README.md
CHANGED
|
@@ -120,6 +120,7 @@ options = RenderScreenshot::TakeOptions
|
|
|
120
120
|
.wait_for('networkidle')
|
|
121
121
|
.delay(500)
|
|
122
122
|
.wait_for_selector('.loaded')
|
|
123
|
+
.wait_for_timeout(30) # seconds; the API clamps it to 1-60
|
|
123
124
|
# Blocking
|
|
124
125
|
.block_ads
|
|
125
126
|
.block_trackers
|
|
@@ -188,13 +189,40 @@ options = RenderScreenshot::TakeOptions
|
|
|
188
189
|
.url('https://example.com')
|
|
189
190
|
.preset('og_card')
|
|
190
191
|
|
|
191
|
-
# URL expires in 24 hours
|
|
192
|
+
# URL expires in 24 hours (the API accepts at most 30 days)
|
|
192
193
|
signed_url = client.generate_url(options, expires_at: Time.now + 86400)
|
|
193
194
|
|
|
194
195
|
# Use in HTML
|
|
195
196
|
# <img src="#{signed_url}" />
|
|
196
197
|
```
|
|
197
198
|
|
|
199
|
+
Signing needs your public key ID (`rs_pub_*`) and secret key (`rs_secret_*`):
|
|
200
|
+
pass `signing_key:` and `public_key_id:` to `Client.new` or to `generate_url`.
|
|
201
|
+
|
|
202
|
+
A signed URL is a `GET /v1/screenshot` request, so it carries only the options
|
|
203
|
+
that GET reads as query parameters: `url`, `html`, `preset`, `device`, `width`,
|
|
204
|
+
`height`, `scale`, `full_page`, `element` (sent as `selector`), `format`,
|
|
205
|
+
`quality`, `delay`, `wait_for_timeout` (sent as `timeout`), `click`, `hide`,
|
|
206
|
+
`remove`, `block_ads`, `block_cookie_banners` (sent as `block_cookies`),
|
|
207
|
+
`dark_mode`, `user_agent`, `locale`, `timezone`, `cache_ttl`, `cache_refresh`,
|
|
208
|
+
`cache_available` and the `pdf_*` options. Other options (for example `mobile`,
|
|
209
|
+
`wait_for`, `wait_for_selector`, `block_trackers`, network and storage options)
|
|
210
|
+
are left out of the URL. Use `take` or `take_json` (POST) for those.
|
|
211
|
+
`TakeOptions#to_query_params` and `#to_query_string` give the same mapping.
|
|
212
|
+
|
|
213
|
+
### Cache Reuse
|
|
214
|
+
|
|
215
|
+
The API serves a previous screenshot (0 credits) only when the request sets
|
|
216
|
+
`cache_available`. Otherwise every request renders a new screenshot.
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
options = RenderScreenshot::TakeOptions
|
|
220
|
+
.url('https://example.com')
|
|
221
|
+
.preset('og_card')
|
|
222
|
+
.cache_available # reuse a render with the same options
|
|
223
|
+
.cache_ttl(86_400) # look back up to 24 hours (seconds)
|
|
224
|
+
```
|
|
225
|
+
|
|
198
226
|
### Cache Management
|
|
199
227
|
|
|
200
228
|
```ruby
|
|
@@ -231,7 +259,7 @@ preset = client.preset('og_card')
|
|
|
231
259
|
|
|
232
260
|
# List all devices
|
|
233
261
|
devices = client.devices
|
|
234
|
-
devices.each { |d| puts "#{d['id']}: #{d['name']} (#{d['width']}x#{d['height']})" }
|
|
262
|
+
devices.each { |d| puts "#{d['id']}: #{d['name']} (#{d['viewport']['width']}x#{d['viewport']['height']})" }
|
|
235
263
|
```
|
|
236
264
|
|
|
237
265
|
### Webhook Verification
|
|
@@ -311,10 +339,13 @@ Error properties:
|
|
|
311
339
|
| **Page** | `inject_script`, `inject_style`, `click`, `hide`, `remove` |
|
|
312
340
|
| **Browser** | `dark_mode`, `reduced_motion`, `media_type`, `user_agent`, `timezone`, `locale`, `geolocation` |
|
|
313
341
|
| **Network** | `headers`, `cookies`, `auth_basic`, `auth_bearer`, `bypass_csp` |
|
|
314
|
-
| **Cache** | `cache_ttl`, `cache_refresh` |
|
|
342
|
+
| **Cache** | `cache_ttl`, `cache_refresh`, `cache_available` |
|
|
315
343
|
| **PDF** | `pdf_paper_size`, `pdf_width`, `pdf_height`, `pdf_landscape`, `pdf_margin`, `pdf_margin_top`, `pdf_margin_right`, `pdf_margin_bottom`, `pdf_margin_left`, `pdf_scale`, `pdf_print_background`, `pdf_page_ranges`, `pdf_header`, `pdf_footer`, `pdf_fit_one_page`, `pdf_prefer_css_page_size` |
|
|
316
344
|
| **Storage** | `storage_enabled`, `storage_path`, `storage_acl` |
|
|
317
345
|
|
|
346
|
+
Units: `delay` is in milliseconds (max 10000). `wait_for_timeout` is in seconds
|
|
347
|
+
(the API clamps it to 1-60). `cache_ttl` is in seconds (3600-2592000).
|
|
348
|
+
|
|
318
349
|
## Development
|
|
319
350
|
|
|
320
351
|
After checking out the repo:
|
|
@@ -349,7 +380,7 @@ The hooks include:
|
|
|
349
380
|
|
|
350
381
|
## Contributing
|
|
351
382
|
|
|
352
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
383
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Render-Screenshot/rs-ruby.
|
|
353
384
|
|
|
354
385
|
## License
|
|
355
386
|
|
|
@@ -9,7 +9,7 @@ module RenderScreenshot
|
|
|
9
9
|
attr_reader :http
|
|
10
10
|
|
|
11
11
|
# Initialize a new client
|
|
12
|
-
# @param api_key [String] API key (rs_live_
|
|
12
|
+
# @param api_key [String] API key (rs_live_*)
|
|
13
13
|
# @param base_url [String, nil] Custom base URL
|
|
14
14
|
# @param timeout [Integer, nil] Request timeout in seconds
|
|
15
15
|
# @param signing_key [String, nil] Secret key for signed URLs (rs_secret_*)
|
|
@@ -64,11 +64,12 @@ module RenderScreenshot
|
|
|
64
64
|
)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
take_options = options.is_a?(TakeOptions) ? options : TakeOptions.from(options || {})
|
|
68
68
|
expires = expires_at.is_a?(Time) ? expires_at.to_i : expires_at
|
|
69
69
|
|
|
70
|
-
#
|
|
71
|
-
|
|
70
|
+
# Signed URLs carry only the parameters GET /v1/screenshot reads.
|
|
71
|
+
# Pairs are sorted and encoded like Rails `to_query` (required for signature verification).
|
|
72
|
+
params = take_options.to_query_params.merge('expires' => expires.to_s, 'key_id' => key_id)
|
|
72
73
|
query_string = params.sort.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
|
|
73
74
|
|
|
74
75
|
# Sign with HMAC-SHA256 using the secret key
|
|
@@ -154,48 +155,5 @@ module RenderScreenshot
|
|
|
154
155
|
raise ValidationError.invalid_request('Options must be a TakeOptions or Hash')
|
|
155
156
|
end
|
|
156
157
|
end
|
|
157
|
-
|
|
158
|
-
def build_signed_params(config, key_id, expires)
|
|
159
|
-
params = {}
|
|
160
|
-
|
|
161
|
-
# Add expires first (alphabetically before key_id)
|
|
162
|
-
params['expires'] = expires
|
|
163
|
-
|
|
164
|
-
# Add key_id (public identifier)
|
|
165
|
-
params['key_id'] = key_id
|
|
166
|
-
|
|
167
|
-
# Add URL or html
|
|
168
|
-
params['url'] = config[:url] if config[:url]
|
|
169
|
-
params['html'] = config[:html] if config[:html]
|
|
170
|
-
|
|
171
|
-
# Add other params
|
|
172
|
-
flat_params = flatten_config(config)
|
|
173
|
-
flat_params.each do |key, value|
|
|
174
|
-
next if %w[url html].include?(key)
|
|
175
|
-
|
|
176
|
-
params[key] = value
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
params
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def flatten_config(config)
|
|
183
|
-
result = {}
|
|
184
|
-
config.each do |key, value|
|
|
185
|
-
next if value.nil?
|
|
186
|
-
|
|
187
|
-
str_key = key.to_s
|
|
188
|
-
if value.is_a?(Hash)
|
|
189
|
-
value.each do |sub_key, sub_value|
|
|
190
|
-
result["#{str_key}_#{sub_key}"] = sub_value unless sub_value.nil?
|
|
191
|
-
end
|
|
192
|
-
elsif value.is_a?(Array)
|
|
193
|
-
result[str_key] = value.join(',')
|
|
194
|
-
else
|
|
195
|
-
result[str_key] = value
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
result
|
|
199
|
-
end
|
|
200
158
|
end
|
|
201
159
|
end
|
|
@@ -72,6 +72,7 @@ module RenderScreenshot
|
|
|
72
72
|
with(wait_for_selector: selector)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# Maximum wait time in seconds (the API clamps it to 1-60)
|
|
75
76
|
def wait_for_timeout(value)
|
|
76
77
|
with(wait_for_timeout: value)
|
|
77
78
|
end
|
|
@@ -192,6 +193,12 @@ module RenderScreenshot
|
|
|
192
193
|
with(cache_refresh: value)
|
|
193
194
|
end
|
|
194
195
|
|
|
196
|
+
# Serve a previous screenshot with the same options when one exists.
|
|
197
|
+
# The API reuses a cached render only when this is set.
|
|
198
|
+
def cache_available(value = true)
|
|
199
|
+
with(cache_available: value)
|
|
200
|
+
end
|
|
201
|
+
|
|
195
202
|
# PDF methods
|
|
196
203
|
def pdf_paper_size(value)
|
|
197
204
|
with(pdf_paper_size: value)
|
|
@@ -280,13 +287,13 @@ module RenderScreenshot
|
|
|
280
287
|
result = {}
|
|
281
288
|
|
|
282
289
|
# Top-level params
|
|
283
|
-
%i[url html preset].each do |key|
|
|
290
|
+
%i[url html preset device].each do |key|
|
|
284
291
|
result[key] = config[key] if config.key?(key)
|
|
285
292
|
end
|
|
286
293
|
|
|
287
294
|
# Viewport group
|
|
288
295
|
viewport = {}
|
|
289
|
-
{ width: :width, height: :height, scale: :scale, mobile: :mobile
|
|
296
|
+
{ width: :width, height: :height, scale: :scale, mobile: :mobile }.each do |config_key, api_key|
|
|
290
297
|
viewport[api_key] = config[config_key] if config.key?(config_key)
|
|
291
298
|
end
|
|
292
299
|
result[:viewport] = viewport unless viewport.empty?
|
|
@@ -357,6 +364,7 @@ module RenderScreenshot
|
|
|
357
364
|
cache = {}
|
|
358
365
|
cache[:ttl] = config[:cache_ttl] if config[:cache_ttl]
|
|
359
366
|
cache[:refresh] = config[:cache_refresh] if config.key?(:cache_refresh)
|
|
367
|
+
cache[:available] = config[:cache_available] if config.key?(:cache_available)
|
|
360
368
|
result[:cache] = cache unless cache.empty?
|
|
361
369
|
|
|
362
370
|
# PDF group
|
|
@@ -366,25 +374,18 @@ module RenderScreenshot
|
|
|
366
374
|
pdf[:height] = config[:pdf_height] if config[:pdf_height]
|
|
367
375
|
pdf[:landscape] = config[:pdf_landscape] if config.key?(:pdf_landscape)
|
|
368
376
|
pdf[:scale] = config[:pdf_scale] if config[:pdf_scale]
|
|
369
|
-
pdf[:
|
|
377
|
+
pdf[:print_background] = config[:pdf_print_background] if config.key?(:pdf_print_background)
|
|
370
378
|
pdf[:page_ranges] = config[:pdf_page_ranges] if config[:pdf_page_ranges]
|
|
371
379
|
pdf[:header] = config[:pdf_header] if config[:pdf_header]
|
|
372
380
|
pdf[:footer] = config[:pdf_footer] if config[:pdf_footer]
|
|
373
381
|
pdf[:fit_one_page] = config[:pdf_fit_one_page] if config.key?(:pdf_fit_one_page)
|
|
374
382
|
pdf[:prefer_css_page_size] = config[:pdf_prefer_css_page_size] if config.key?(:pdf_prefer_css_page_size)
|
|
375
383
|
|
|
376
|
-
# PDF margins
|
|
377
|
-
if config[:pdf_margin]
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
# Build margin hash from individual margin settings
|
|
382
|
-
margin = {}
|
|
383
|
-
margin[:top] = config[:pdf_margin_top] if config[:pdf_margin_top]
|
|
384
|
-
margin[:right] = config[:pdf_margin_right] if config[:pdf_margin_right]
|
|
385
|
-
margin[:bottom] = config[:pdf_margin_bottom] if config[:pdf_margin_bottom]
|
|
386
|
-
margin[:left] = config[:pdf_margin_left] if config[:pdf_margin_left]
|
|
387
|
-
pdf[:margin] = margin unless margin.empty?
|
|
384
|
+
# PDF margins: a uniform `margin` plus optional per-side overrides
|
|
385
|
+
pdf[:margin] = config[:pdf_margin] if config[:pdf_margin]
|
|
386
|
+
%i[top right bottom left].each do |side|
|
|
387
|
+
key = :"pdf_margin_#{side}"
|
|
388
|
+
pdf[:"margin_#{side}"] = config[key] if config[key]
|
|
388
389
|
end
|
|
389
390
|
result[:pdf] = pdf unless pdf.empty?
|
|
390
391
|
|
|
@@ -398,39 +399,44 @@ module RenderScreenshot
|
|
|
398
399
|
result
|
|
399
400
|
end
|
|
400
401
|
|
|
401
|
-
#
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
402
|
+
# GET /v1/screenshot query parameter names, keyed by config key
|
|
403
|
+
QUERY_PARAM_NAMES = {
|
|
404
|
+
url: 'url', html: 'html', preset: 'preset', device: 'device',
|
|
405
|
+
width: 'width', height: 'height', scale: 'scale',
|
|
406
|
+
full_page: 'full_page', element: 'selector', format: 'format', quality: 'quality',
|
|
407
|
+
delay: 'delay', wait_for_timeout: 'timeout',
|
|
408
|
+
click: 'click', hide: 'hide', remove: 'remove',
|
|
409
|
+
block_ads: 'block_ads', block_cookie_banners: 'block_cookies',
|
|
410
|
+
dark_mode: 'dark_mode', user_agent: 'user_agent', locale: 'locale', timezone: 'timezone',
|
|
411
|
+
cache_ttl: 'cache_ttl', cache_refresh: 'cache_refresh', cache_available: 'cache_available',
|
|
412
|
+
pdf_paper_size: 'pdf_paper', pdf_width: 'pdf_width', pdf_height: 'pdf_height',
|
|
413
|
+
pdf_landscape: 'pdf_landscape', pdf_scale: 'pdf_scale', pdf_fit_one_page: 'pdf_fit_one_page',
|
|
414
|
+
pdf_margin: 'pdf_margin', pdf_margin_top: 'pdf_margin_top', pdf_margin_right: 'pdf_margin_right',
|
|
415
|
+
pdf_margin_bottom: 'pdf_margin_bottom', pdf_margin_left: 'pdf_margin_left',
|
|
416
|
+
pdf_print_background: 'pdf_print_background', pdf_prefer_css_page_size: 'pdf_prefer_css_page_size',
|
|
417
|
+
pdf_page_ranges: 'pdf_page_ranges', pdf_header: 'pdf_header', pdf_footer: 'pdf_footer'
|
|
418
|
+
}.freeze
|
|
419
|
+
|
|
420
|
+
# Convert to flat query parameters for GET requests and signed URLs.
|
|
421
|
+
#
|
|
422
|
+
# Uses the parameter names that GET /v1/screenshot reads. Options with no
|
|
423
|
+
# GET equivalent (for example mobile, wait_for, wait_for_selector,
|
|
424
|
+
# block_trackers, block_urls, media_type, network, storage and page
|
|
425
|
+
# injection options) are left out. Use take / take_json (POST) for those.
|
|
426
|
+
# @return [Hash{String => String}]
|
|
427
|
+
def to_query_params
|
|
428
|
+
QUERY_PARAM_NAMES.each_with_object({}) do |(config_key, param_name), params|
|
|
429
|
+
next unless config.key?(config_key) && !config[config_key].nil?
|
|
428
430
|
|
|
429
431
|
value = config[config_key]
|
|
430
|
-
params
|
|
432
|
+
params[param_name] = value.is_a?(Array) ? value.join(',') : value.to_s
|
|
431
433
|
end
|
|
434
|
+
end
|
|
432
435
|
|
|
433
|
-
|
|
436
|
+
# Convert to a flat query string for GET requests.
|
|
437
|
+
# Pairs are sorted and values are form-encoded like Rails `to_query`.
|
|
438
|
+
def to_query_string
|
|
439
|
+
to_query_params.sort.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}" }.join('&')
|
|
434
440
|
end
|
|
435
441
|
|
|
436
442
|
private
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: renderscreenshot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Simon Chiu
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -32,7 +32,7 @@ dependencies:
|
|
|
32
32
|
description: A developer-friendly screenshot API for capturing web pages programmatically.
|
|
33
33
|
Create social cards, link previews, documentation screenshots, and more.
|
|
34
34
|
email:
|
|
35
|
-
-
|
|
35
|
+
- simon@renderscreenshot.com
|
|
36
36
|
executables: []
|
|
37
37
|
extensions: []
|
|
38
38
|
extra_rdoc_files: []
|
|
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
79
|
version: '0'
|
|
80
80
|
requirements: []
|
|
81
|
-
rubygems_version:
|
|
81
|
+
rubygems_version: 4.0.20
|
|
82
82
|
specification_version: 4
|
|
83
83
|
summary: Official Ruby SDK for RenderScreenshot API
|
|
84
84
|
test_files: []
|