openvibers/openvibe.ai Commits
AI: media.analyze follows Media's redirect to its shield host edge.openvibe.media (B2 recordings were unreadable) (#61)
Changes
@@@ -99,7 +99,7 @@ AI_MODEL=# downloaded, and speech windows seek with range requests. 0 downloads it first (the disk cap then applies).#AI_MEDIA_STREAM=1# Object storage Media redirects offloaded recordings to (presigned URLs), followed only from Media itself.--#AI_MEDIA_STORAGE_HOSTS=*.backblazeb2.com,*.r2.cloudflarestorage.com++#AI_MEDIA_STORAGE_HOSTS=*.backblazeb2.com,*.r2.cloudflarestorage.com,edge.openvibe.media#AI_LOCAL_LLM_MODEL=qwen2.5-1.5b-instruct#AI_LOCAL_LLM_TIMEOUT_MS=180000# media.analyze: file cap (also bounded by free temp disk minus the reserve), download timeout, scene threshold@@@ -188,9 +188,10 @@ function load(env = process.env) {internalUrl: trimUrl(env.OV_MEDIA_INTERNAL_URL || ''),// https hostnames (exact, or *.suffix) that media/image URLs may point at.allowHosts: list(env.AI_FETCH_ALLOW_HOSTS, ['openvibe.media', 'openvibe.live', 'openvibe.network', '*.openvibe.network', 'openvibe.community', 'openvibe.tools', 'openvibe.games']),-- // Object storage Media hands recordings off to (a 302 to a presigned URL): reachable ONLY as a redirect-- // from Media itself (its public host or the internal origin), never as a URL a caller names.-- storageHosts: list(env.AI_MEDIA_STORAGE_HOSTS, ['*.backblazeb2.com', '*.r2.cloudflarestorage.com']),++ // Object storage Media hands recordings off to (a 302 to a presigned URL), and Media's origin shield++ // edge.openvibe.media (where it sends a B2 copy's reads, through its slice cache): reachable ONLY as a++ // redirect from Media itself (its public host or the internal origin), never as a URL a caller names.++ storageHosts: list(env.AI_MEDIA_STORAGE_HOSTS, ['*.backblazeb2.com', '*.r2.cloudflarestorage.com', 'edge.openvibe.media']),maxBytes: int(env.AI_FETCH_MAX_BYTES, 512 * 1024 * 1024),maxImageBytes: int(env.AI_FETCH_MAX_IMAGE_BYTES, 12 * 1024 * 1024),timeoutMs: int(env.AI_FETCH_TIMEOUT_MS, 120000),@@@ -36,6 +36,8 @@ t.test('object storage is followed only as a redirect from Media', async () => {const b2 = 'https://s3.us-west-004.backblazeb2.com/bucket/vod-1.mp4?X-Amz-Signature=abc';assert.strictEqual(f.follow(media, b2).url.hostname, 's3.us-west-004.backblazeb2.com', 'Media -> B2');assert.strictEqual(f.follow(internal, 'https://acct.r2.cloudflarestorage.com/b/x').internal, false, 'internal Media -> R2, as a public target');++ assert.strictEqual(f.follow(media, 'https://edge.openvibe.media/v/1').url.hostname, 'edge.openvibe.media', 'Media -> its shield (a B2 copy)');++ await (async () => { try { f.judge('https://edge.openvibe.media/v/1'); assert.fail('named'); } catch (e) { assert.strictEqual(e.code, 'fetch.refused', 'the shield is never a URL a caller names'); } })();const refused = (fn, why) => { try { fn(); assert.fail(why); } catch (e) { assert.strictEqual(e.code, 'fetch.refused', why); } };await refused(() => f.follow(other, b2), 'another host may not send it to storage');await refused(() => f.judge(b2), 'storage is never a URL a caller names');
@@ -99,7 +99,7 @@ AI_MODEL=
@@ -99,7 +99,7 @@ AI_MODEL=
# downloaded, and speech windows seek with range requests. 0 downloads it first (the disk cap then applies).
# downloaded, and speech windows seek with range requests. 0 downloads it first (the disk cap then applies).
#AI_MEDIA_STREAM=1
#AI_MEDIA_STREAM=1
# Object storage Media redirects offloaded recordings to (presigned URLs), followed only from Media itself.
# Object storage Media redirects offloaded recordings to (presigned URLs), followed only from Media itself.
-#AI_MEDIA_STORAGE_HOSTS=*.backblazeb2.com,*.r2.cloudflarestorage.com
+#AI_MEDIA_STORAGE_HOSTS=*.backblazeb2.com,*.r2.cloudflarestorage.com,edge.openvibe.media
#AI_LOCAL_LLM_MODEL=qwen2.5-1.5b-instruct
#AI_LOCAL_LLM_MODEL=qwen2.5-1.5b-instruct
#AI_LOCAL_LLM_TIMEOUT_MS=180000
#AI_LOCAL_LLM_TIMEOUT_MS=180000
# media.analyze: file cap (also bounded by free temp disk minus the reserve), download timeout, scene threshold
# media.analyze: file cap (also bounded by free temp disk minus the reserve), download timeout, scene threshold
@@ -188,9 +188,10 @@ function load(env = process.env) {
@@ -188,9 +188,10 @@ function load(env = process.env) {
internalUrl: trimUrl(env.OV_MEDIA_INTERNAL_URL || ''),
internalUrl: trimUrl(env.OV_MEDIA_INTERNAL_URL || ''),
// https hostnames (exact, or *.suffix) that media/image URLs may point at.
// https hostnames (exact, or *.suffix) that media/image URLs may point at.
allowHosts: list(env.AI_FETCH_ALLOW_HOSTS, ['openvibe.media', 'openvibe.live', 'openvibe.network', '*.openvibe.network', 'openvibe.community', 'openvibe.tools', 'openvibe.games']),
allowHosts: list(env.AI_FETCH_ALLOW_HOSTS, ['openvibe.media', 'openvibe.live', 'openvibe.network', '*.openvibe.network', 'openvibe.community', 'openvibe.tools', 'openvibe.games']),
- // Object storage Media hands recordings off to (a 302 to a presigned URL): reachable ONLY as a redirect
- // from Media itself (its public host or the internal origin), never as a URL a caller names.
- storageHosts: list(env.AI_MEDIA_STORAGE_HOSTS, ['*.backblazeb2.com', '*.r2.cloudflarestorage.com']),
+ // Object storage Media hands recordings off to (a 302 to a presigned URL), and Media's origin shield
+ // edge.openvibe.media (where it sends a B2 copy's reads, through its slice cache): reachable ONLY as a
+ // redirect from Media itself (its public host or the internal origin), never as a URL a caller names.
+ storageHosts: list(env.AI_MEDIA_STORAGE_HOSTS, ['*.backblazeb2.com', '*.r2.cloudflarestorage.com', 'edge.openvibe.media']),
maxBytes: int(env.AI_FETCH_MAX_BYTES, 512 * 1024 * 1024),
maxBytes: int(env.AI_FETCH_MAX_BYTES, 512 * 1024 * 1024),
maxImageBytes: int(env.AI_FETCH_MAX_IMAGE_BYTES, 12 * 1024 * 1024),
maxImageBytes: int(env.AI_FETCH_MAX_IMAGE_BYTES, 12 * 1024 * 1024),
timeoutMs: int(env.AI_FETCH_TIMEOUT_MS, 120000),
timeoutMs: int(env.AI_FETCH_TIMEOUT_MS, 120000),
@@ -36,6 +36,8 @@ t.test('object storage is followed only as a redirect from Media', async () => {
@@ -36,6 +36,8 @@ t.test('object storage is followed only as a redirect from Media', async () => {
const b2 = 'https://s3.us-west-004.backblazeb2.com/bucket/vod-1.mp4?X-Amz-Signature=abc';
const b2 = 'https://s3.us-west-004.backblazeb2.com/bucket/vod-1.mp4?X-Amz-Signature=abc';
assert.strictEqual(f.follow(media, b2).url.hostname, 's3.us-west-004.backblazeb2.com', 'Media -> B2');
assert.strictEqual(f.follow(media, b2).url.hostname, 's3.us-west-004.backblazeb2.com', 'Media -> B2');
assert.strictEqual(f.follow(internal, 'https://acct.r2.cloudflarestorage.com/b/x').internal, false, 'internal Media -> R2, as a public target');
assert.strictEqual(f.follow(internal, 'https://acct.r2.cloudflarestorage.com/b/x').internal, false, 'internal Media -> R2, as a public target');
+ assert.strictEqual(f.follow(media, 'https://edge.openvibe.media/v/1').url.hostname, 'edge.openvibe.media', 'Media -> its shield (a B2 copy)');
+ await (async () => { try { f.judge('https://edge.openvibe.media/v/1'); assert.fail('named'); } catch (e) { assert.strictEqual(e.code, 'fetch.refused', 'the shield is never a URL a caller names'); } })();
const refused = (fn, why) => { try { fn(); assert.fail(why); } catch (e) { assert.strictEqual(e.code, 'fetch.refused', why); } };
const refused = (fn, why) => { try { fn(); assert.fail(why); } catch (e) { assert.strictEqual(e.code, 'fetch.refused', why); } };
await refused(() => f.follow(other, b2), 'another host may not send it to storage');
await refused(() => f.follow(other, b2), 'another host may not send it to storage');
await refused(() => f.judge(b2), 'storage is never a URL a caller names');
await refused(() => f.judge(b2), 'storage is never a URL a caller names');