openvibers/openvibe.live Commits

A private VOD or clip is transcribed and analysed too (roadmap WS-O task 2; Media 9aac60f signed playback URLs). The recording source for AI work is now a signed URL from Media (media-client signedMediaUrl, six hours), which works whatever the visibility. So OpenVibe.AI, which holds no key, reads a private recording window by window, and Live's own frame capture reads it as well. Before, the public URL 404'd for the 34 private VODs. It falls back to the public URL if Media does not sign. npm test passes.

OP OpenVibers committed 28 Sept 2026 · 695202d8a9 · 2 files, +15 −0


Changes

ddiff --git a/server/ai/ai-analysis.js b/server/ai/ai-analysis.js
iindex 96eed927..03c380e1 100644
---- a/server/ai/ai-analysis.js
++++ b/server/ai/ai-analysis.js
@@@ -139,6 +139,12 @@ async function _mediaSource(row, kind = 'vod') {
const media = require('../media-client');
const id = row && row.id;
if (!id) return null;
++ // A signed URL reads the recording whatever its visibility: a private VOD is transcribed too (by OpenVibe.AI,
++ // which holds no key) and its frames captured. Six hours covers a long transcript pass, window by window.
++ try {
++ const signed = await media.signedMediaUrl(kind, id, 6 * 3600);
++ if (signed && signed.url) return signed.url;
++ } catch { /* fall back to the public URL */ }
const meta = kind === 'clip' ? await media.getClip(id) : await media.getVod(id);
if (meta && meta.playback_url) return media.publicUrl(meta.playback_url);
return kind === 'clip' ? media.clipUrl(id) : media.vodPlaybackUrl(id);
ddiff --git a/server/media-client.js b/server/media-client.js
iindex f02dfbff..f83b29d6 100644
---- a/server/media-client.js
++++ b/server/media-client.js
@@@ -177,6 +177,14 @@ function getVod(vodId, opts = {}) {
return request('GET', `/vods/${vodId}`, opts);
}
++/**
++ * GET /vods/:id/signed-url or /clips/:id/signed-url → { url, expires_at }: a short-lived URL of the recording's bytes
++ * that works whatever its visibility, for a reader with no key (OpenVibe.AI transcribing it). ttl in seconds (≤ 6 h).
++ */
++function signedMediaUrl(kind, id, ttlS = 3600, opts = {}) {
++ return request('GET', `/${kind === 'clip' ? 'clips' : 'vods'}/${id}/signed-url`, { query: { ttl: ttlS }, ...opts });
++}
++
/** GET /vods?limit&offset (+ pass-through filters like username/user_id/stream_id) */
function listVods(query = {}, opts = {}) {
return request('GET', '/vods', { query, ...opts });
@@@ -398,6 +406,7 @@ function actingUserFrom(req) {
}
module.exports = {
++ signedMediaUrl,
MEDIA_URL, MEDIA_PUBLIC_URL, MEDIA_APP_ID,
MediaApiError,
request, proxy, actingUserFrom, _formData,