calmlens 0.23.0 → 0.23.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/lib/calmlens/assets.rb +17 -10
- data/lib/calmlens/testing/fake_api.rb +17 -11
- data/lib/calmlens/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0707eda417af2a9021104b813fecc82ca3a4cddbcc420d92fcd22642f844d61f
|
|
4
|
+
data.tar.gz: c834ec6d48c5815e93d1af413efbd2c8cab0b4fb67071d2da17ac9d97ffab018
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cfa54ba17a0a73abea37f1b34a76df43a2172ffa93e6801f0ae1cd671b40970516675fdcca7014fed69a8acf686a542f78e101fc7000580cc1c11f31c07ac095
|
|
7
|
+
data.tar.gz: 71a628e67b7887807d5c241913b4125836bf5493b6bb54558d9321bb47a4331ea54079dd94d4161dcb02dcc79c798d5653bd891f9c1b3ff3d8848f7cebac9bbd
|
data/lib/calmlens/assets.rb
CHANGED
|
@@ -359,24 +359,31 @@ module CalmLens
|
|
|
359
359
|
def poll_for_decision(asset_id, request_options, timeout)
|
|
360
360
|
deadline = @session.monotonic + timeout
|
|
361
361
|
delay = DECISION_POLL_FIRST_DELAY
|
|
362
|
+
processing = moderation = nil
|
|
362
363
|
loop do
|
|
363
|
-
|
|
364
|
+
remaining = deadline - @session.monotonic
|
|
365
|
+
break if timeout.positive? && !remaining.positive?
|
|
366
|
+
|
|
367
|
+
# The outer deadline covers transport retries and the entire response read.
|
|
368
|
+
asset = ::Timeout.timeout(timeout.zero? ? nil : remaining) do
|
|
369
|
+
retrieve('assetId' => asset_id, 'request_options' => request_options)
|
|
370
|
+
end
|
|
371
|
+
remaining = deadline - @session.monotonic
|
|
372
|
+
break if timeout.positive? && !remaining.positive?
|
|
373
|
+
|
|
364
374
|
processing = asset.dig('classification', 'status')
|
|
365
375
|
moderation = asset.dig('classification', 'moderation', 'status')
|
|
366
376
|
return asset if SETTLED_STATUSES.include?(processing)
|
|
367
|
-
|
|
368
|
-
remaining = deadline - @session.monotonic
|
|
369
|
-
unless remaining.positive?
|
|
370
|
-
raise TimeoutError.new(SdkErrorCodes::TIMEOUT,
|
|
371
|
-
"Asset #{asset['id']} was not settled after #{timeout}s " \
|
|
372
|
-
"(processing #{processing}, moderation #{moderation})",
|
|
373
|
-
details: { 'assetId' => asset['id'], 'classificationStatus' => processing,
|
|
374
|
-
'moderationStatus' => moderation })
|
|
375
|
-
end
|
|
377
|
+
break unless remaining.positive?
|
|
376
378
|
|
|
377
379
|
sleep([delay, remaining].min)
|
|
378
380
|
delay = [delay * DECISION_POLL_FACTOR, DECISION_POLL_MAX_DELAY].min
|
|
379
381
|
end
|
|
382
|
+
raise ::Timeout::Error
|
|
383
|
+
rescue ::Timeout::Error
|
|
384
|
+
raise TimeoutError.new(SdkErrorCodes::TIMEOUT, "Asset #{asset_id} was not settled after #{timeout}s",
|
|
385
|
+
details: { 'assetId' => asset_id, 'classificationStatus' => processing,
|
|
386
|
+
'moderationStatus' => moderation })
|
|
380
387
|
end
|
|
381
388
|
|
|
382
389
|
def submit(options, request_options, wait)
|
|
@@ -1782,8 +1782,8 @@ module CalmLens
|
|
|
1782
1782
|
# Answers like the CDN (serveAssetResource.ts): `original` is the upload; `rendered` the image,
|
|
1783
1783
|
# PDF, video or audio file `content.rendered` describes; `preview` the still `content.preview`
|
|
1784
1784
|
# describes; `hls` the adaptive stream's files, which the mock never prepares; `embed` a small
|
|
1785
|
-
# page standing in for the embed page of any asset; and `placeholder` the preview's ThumbHash as a PNG
|
|
1786
|
-
#
|
|
1785
|
+
# page standing in for the embed page of any asset; and `placeholder` the preview's ThumbHash as a PNG.
|
|
1786
|
+
# A safety hold refuses every resource. Other resources pass delivery checks first,
|
|
1787
1787
|
# which refuse everyone (content still uploading, blocked or purged, failed processing, removed
|
|
1788
1788
|
# content), then an age-restricted asset needs `viewerClaims.ageConfirmed=true` whatever the key,
|
|
1789
1789
|
# and a private asset needs an API key with `assets:download` for its folder. The mock converts
|
|
@@ -1809,21 +1809,21 @@ module CalmLens
|
|
|
1809
1809
|
authorised = !grant.nil? || can_download?(item['folderId'], headers)
|
|
1810
1810
|
return info(method, item, authorised, params) if resource == 'info'
|
|
1811
1811
|
return not_available if item['content']['safetyHold']
|
|
1812
|
-
return placeholder(method, item) if resource == 'placeholder'
|
|
1812
|
+
return placeholder(method, item, authorised) if resource == 'placeholder'
|
|
1813
1813
|
|
|
1814
1814
|
deliver(method, item, resource, params, selected ? options || {} : nil, authorised)
|
|
1815
1815
|
end
|
|
1816
1816
|
|
|
1817
1817
|
# What a grant doesn't cover, or, like the CDN, a private asset for anyone without a grant or the
|
|
1818
1818
|
# project's API key: it answers as an unknown asset does, whatever its state, except for its
|
|
1819
|
-
#
|
|
1819
|
+
# info, which has its own rule. An error response, or nil.
|
|
1820
1820
|
def refuse_viewer(item, resource, grant, headers)
|
|
1821
1821
|
if resource == 'original' && grant && !grant[:original]
|
|
1822
1822
|
return error(ErrorCodes::SIGNED_URL_ORIGINAL_NOT_GRANTED, "This signed asset URL doesn't grant the original file", status: 403)
|
|
1823
1823
|
end
|
|
1824
1824
|
return if grant || item['visibility'] == 'public' || header(headers, 'X-Api-Key') == @api_key
|
|
1825
1825
|
|
|
1826
|
-
asset_not_found unless
|
|
1826
|
+
asset_not_found unless resource == 'info'
|
|
1827
1827
|
end
|
|
1828
1828
|
|
|
1829
1829
|
# Whether the path after the resource selects a lens or transform, and its options; nil when the
|
|
@@ -1866,12 +1866,15 @@ module CalmLens
|
|
|
1866
1866
|
restricted = item['visibility'] != 'public'
|
|
1867
1867
|
return asset_not_found if restricted && !authorised
|
|
1868
1868
|
|
|
1869
|
+
restricted ||= %w[blocked purged].include?(item['classification']['moderation']['status'])
|
|
1870
|
+
return not_available if restricted && !authorised
|
|
1871
|
+
|
|
1869
1872
|
claims = JSON.parse(params.fetch('viewerClaims', '{}'))
|
|
1870
1873
|
body = { 'id' => item['id'], 'projectId' => item['projectId'], 'type' => item['type'], 'externalUrl' => item['externalUrl'],
|
|
1871
1874
|
'content' => item['content'].slice(*%w[format width height durationSeconds pageCount rendered preview hls]),
|
|
1872
1875
|
'availability' => availability(item, claims.is_a?(Hash) && claims['ageConfirmed'] == true) }
|
|
1873
1876
|
cdn_response(method, JSON.generate(body), { 'content-type' => 'application/json',
|
|
1874
|
-
'cache-control' =>
|
|
1877
|
+
'cache-control' => restricted ? 'private, no-store' : 'public, no-cache',
|
|
1875
1878
|
'cache-tag' => "asset-#{item['id']}" })
|
|
1876
1879
|
rescue JSON::ParserError
|
|
1877
1880
|
error(ErrorCodes::INVALID_VIEWER_CLAIMS, 'Invalid viewer claims', status: 400)
|
|
@@ -1980,15 +1983,18 @@ module CalmLens
|
|
|
1980
1983
|
|
|
1981
1984
|
def not_available = error('asset/not-available', 'Asset is not available', status: 403)
|
|
1982
1985
|
|
|
1983
|
-
#
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1986
|
+
# A preview requires access for private or blocked assets and disappears with its content.
|
|
1987
|
+
def placeholder(method, item, authorised)
|
|
1988
|
+
private_asset = item['visibility'] != 'public'
|
|
1989
|
+
blocked = item['classification']['moderation']['status'] == 'blocked'
|
|
1990
|
+
return asset_not_found if private_asset && !authorised
|
|
1991
|
+
return not_available if blocked && !authorised
|
|
1992
|
+
|
|
1987
1993
|
thumbhash = item.dig('content', 'preview', 'thumbhash')
|
|
1988
1994
|
return not_available if thumbhash.nil? || item['classification']['moderation']['status'] == 'purged'
|
|
1989
1995
|
|
|
1990
1996
|
cdn_response(method, Placeholder.png(thumbhash),
|
|
1991
|
-
'content-type' => 'image/png', 'cache-control' => 'public,
|
|
1997
|
+
'content-type' => 'image/png', 'cache-control' => private_asset || blocked ? 'private, no-store' : 'public, no-cache',
|
|
1992
1998
|
'cache-tag' => "asset-#{item['id']}", 'etag' => "\"#{thumbhash}\"")
|
|
1993
1999
|
end
|
|
1994
2000
|
|
data/lib/calmlens/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: calmlens
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.23.
|
|
4
|
+
version: 0.23.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- CalmLens
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'Server-side client for the CalmLens API: moderation, uploads, events,
|
|
14
14
|
webhooks and CDN delivery.'
|