openvibers/openvibe.live Commits
VOD and clip API: their independent lookups (names, stream, AI state, Media's clip list, view count, Community's comment count) run together instead of one after another (#214)
Changes
@@@ -232,20 +232,25 @@ router.get('/:id', optionalAuth, async (req, res) => {}if (!clip || !await access.canView(req.user, clip)) return clipNotFound(res);if (access.isPrivate(clip)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));-- await withUserFields(clip);++ // Independent lookups, run together (one after another before, Community's comment count included).++ const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip || 'unknown';++ const [, inserted, stream, st, comments] = await Promise.all([++ withUserFields(clip),++ // Unique-view tracking (content_views stays in live.db).++ db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['clip', clip.id, ip]).catch(() => null),++ clip.stream_id ? db.getStreamById(clip.stream_id).catch(() => null) : null,++ db.getClipAiState(clip.id).catch(() => null),++ commentCount('clip', clip.id, clip.title, req.ip).catch(() => null), // null when Community is unreachable++ ]);-- // Unique-view tracking (content_views stays in live.db).try {-- const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip || 'unknown';-- const inserted = await db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['clip', clip.id, ip]);-- if (inserted.changes > 0) {++ if (inserted && inserted.changes > 0) {const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['clip', clip.id]);clip.view_count = Math.max(Number(clip.view_count) || 0, count.c);}} catch { /* */ }if (clip.stream_id) {-- const stream = await db.getStreamById(clip.stream_id);if (stream) {clip.stream_started_at = stream.started_at;clip.stream_ended_at = stream.ended_at;@@@ -261,7 +266,6 @@ router.get('/:id', optionalAuth, async (req, res) => {// Overlay Live-owned AI state (overview + transcript live in clip_ai_state).try {-- const st = await db.getClipAiState(clip.id);if (st) {if (st.ai_overview_short) { clip.ai_overview_short = st.ai_overview_short; clip.ai_overview = st.ai_overview || clip.ai_overview || st.ai_overview_short; }if (st.ai_transcript_json) {@@@ -272,7 +276,7 @@ router.get('/:id', optionalAuth, async (req, res) => {}} catch { /* */ }-- clip.comment_count = await commentCount('clip', clip.id, clip.title, req.ip); // null when Community is unreachable++ clip.comment_count = comments;clip.can_delete = await canActorDeleteClip(req.user, clip);clip.can_edit = await canActorModerateClip(req.user, clip);@@@ -479,11 +479,21 @@ router.get('/:id', optionalAuth, async (req, res) => {const vod = await loadVisibleVod(req, res, req.params.id);if (!vod) return;if (access.isPrivate(vod)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));-- await withUserFields(vod);++ // The lookups below do not depend on each other: they run together (they ran one after another, Media's clip++ // list and Community's comment count included).++ const ip = requesterIp(req);++ const [, stream, st, co, inserted, comments] = await Promise.all([++ withUserFields(vod),++ vod.stream_id ? db.getStreamById(vod.stream_id).catch(() => null) : null,++ db.getVodAiState(vod.id).catch(() => null),++ media.listClips(vod.stream_id ? { stream_id: vod.stream_id } : { vod_id: vod.id }).catch(() => null),++ // Unique-view tracking stays local (content_views is a Live table).++ db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['vod', vod.id, ip]).catch(() => null),++ commentCount('vod', vod.id, vod.title, req.ip).catch(() => null), // null when Community is unreachable++ ]);// Enrich with local stream details for chat replay.if (vod.stream_id) {-- const stream = await db.getStreamById(vod.stream_id);if (stream) {vod.stream_started_at = stream.started_at;vod.stream_ended_at = stream.ended_at;@@@ -496,7 +506,6 @@ router.get('/:id', optionalAuth, async (req, res) => {// Overlay Live-owned AI state (overview + transcript live in vod_ai_state).try {-- const st = await db.getVodAiState(vod.id);if (st) {if (st.ai_overview_short) { vod.ai_overview_short = st.ai_overview_short; vod.ai_overview = st.ai_overview || vod.ai_overview || st.ai_overview_short; }if (st.ai_transcript_json) {@@@ -507,24 +516,15 @@ router.get('/:id', optionalAuth, async (req, res) => {}} catch { /* */ }-- let clips = [];-- try {-- const q = vod.stream_id ? { stream_id: vod.stream_id } : { vod_id: vod.id };-- const co = await media.listClips(q);-- clips = co?.clips || (Array.isArray(co) ? co : []);-- } catch { /* */ }---- // Unique-view tracking stays local (content_views is a Live table).++ const clips = co?.clips || (Array.isArray(co) ? co : []);try {-- const ip = requesterIp(req);-- const inserted = await db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['vod', vod.id, ip]);-- if (inserted.changes > 0) {++ if (inserted && inserted.changes > 0) {const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['vod', vod.id]);vod.view_count = Math.max(Number(vod.view_count) || 0, count.c);}} catch { /* */ }-- vod.comment_count = await commentCount('vod', vod.id, vod.title, req.ip); // null when Community is unreachable++ vod.comment_count = comments;res.json({ vod, clips });} catch {res.status(500).json({ error: 'Failed to get VOD' });
@@ -232,20 +232,25 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -232,20 +232,25 @@ router.get('/:id', optionalAuth, async (req, res) => {
}
}
if (!clip || !await access.canView(req.user, clip)) return clipNotFound(res);
if (!clip || !await access.canView(req.user, clip)) return clipNotFound(res);
if (access.isPrivate(clip)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));
if (access.isPrivate(clip)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));
- await withUserFields(clip);
+ // Independent lookups, run together (one after another before, Community's comment count included).
+ const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip || 'unknown';
+ const [, inserted, stream, st, comments] = await Promise.all([
+ withUserFields(clip),
+ // Unique-view tracking (content_views stays in live.db).
+ db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['clip', clip.id, ip]).catch(() => null),
+ clip.stream_id ? db.getStreamById(clip.stream_id).catch(() => null) : null,
+ db.getClipAiState(clip.id).catch(() => null),
+ commentCount('clip', clip.id, clip.title, req.ip).catch(() => null), // null when Community is unreachable
+ ]);
- // Unique-view tracking (content_views stays in live.db).
try {
try {
- const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip || 'unknown';
- const inserted = await db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['clip', clip.id, ip]);
- if (inserted.changes > 0) {
+ if (inserted && inserted.changes > 0) {
const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['clip', clip.id]);
const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['clip', clip.id]);
clip.view_count = Math.max(Number(clip.view_count) || 0, count.c);
clip.view_count = Math.max(Number(clip.view_count) || 0, count.c);
}
}
} catch { /* */ }
} catch { /* */ }
if (clip.stream_id) {
if (clip.stream_id) {
- const stream = await db.getStreamById(clip.stream_id);
if (stream) {
if (stream) {
clip.stream_started_at = stream.started_at;
clip.stream_started_at = stream.started_at;
clip.stream_ended_at = stream.ended_at;
clip.stream_ended_at = stream.ended_at;
@@ -261,7 +266,6 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -261,7 +266,6 @@ router.get('/:id', optionalAuth, async (req, res) => {
// Overlay Live-owned AI state (overview + transcript live in clip_ai_state).
// Overlay Live-owned AI state (overview + transcript live in clip_ai_state).
try {
try {
- const st = await db.getClipAiState(clip.id);
if (st) {
if (st) {
if (st.ai_overview_short) { clip.ai_overview_short = st.ai_overview_short; clip.ai_overview = st.ai_overview || clip.ai_overview || st.ai_overview_short; }
if (st.ai_overview_short) { clip.ai_overview_short = st.ai_overview_short; clip.ai_overview = st.ai_overview || clip.ai_overview || st.ai_overview_short; }
if (st.ai_transcript_json) {
if (st.ai_transcript_json) {
@@ -272,7 +276,7 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -272,7 +276,7 @@ router.get('/:id', optionalAuth, async (req, res) => {
}
}
} catch { /* */ }
} catch { /* */ }
- clip.comment_count = await commentCount('clip', clip.id, clip.title, req.ip); // null when Community is unreachable
+ clip.comment_count = comments;
clip.can_delete = await canActorDeleteClip(req.user, clip);
clip.can_delete = await canActorDeleteClip(req.user, clip);
clip.can_edit = await canActorModerateClip(req.user, clip);
clip.can_edit = await canActorModerateClip(req.user, clip);
@@ -479,11 +479,21 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -479,11 +479,21 @@ router.get('/:id', optionalAuth, async (req, res) => {
const vod = await loadVisibleVod(req, res, req.params.id);
const vod = await loadVisibleVod(req, res, req.params.id);
if (!vod) return;
if (!vod) return;
if (access.isPrivate(vod)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));
if (access.isPrivate(vod)) res.set('Cache-Control', cache.htmlHeaders({ private: true }));
- await withUserFields(vod);
+ // The lookups below do not depend on each other: they run together (they ran one after another, Media's clip
+ // list and Community's comment count included).
+ const ip = requesterIp(req);
+ const [, stream, st, co, inserted, comments] = await Promise.all([
+ withUserFields(vod),
+ vod.stream_id ? db.getStreamById(vod.stream_id).catch(() => null) : null,
+ db.getVodAiState(vod.id).catch(() => null),
+ media.listClips(vod.stream_id ? { stream_id: vod.stream_id } : { vod_id: vod.id }).catch(() => null),
+ // Unique-view tracking stays local (content_views is a Live table).
+ db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['vod', vod.id, ip]).catch(() => null),
+ commentCount('vod', vod.id, vod.title, req.ip).catch(() => null), // null when Community is unreachable
+ ]);
// Enrich with local stream details for chat replay.
// Enrich with local stream details for chat replay.
if (vod.stream_id) {
if (vod.stream_id) {
- const stream = await db.getStreamById(vod.stream_id);
if (stream) {
if (stream) {
vod.stream_started_at = stream.started_at;
vod.stream_started_at = stream.started_at;
vod.stream_ended_at = stream.ended_at;
vod.stream_ended_at = stream.ended_at;
@@ -496,7 +506,6 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -496,7 +506,6 @@ router.get('/:id', optionalAuth, async (req, res) => {
// Overlay Live-owned AI state (overview + transcript live in vod_ai_state).
// Overlay Live-owned AI state (overview + transcript live in vod_ai_state).
try {
try {
- const st = await db.getVodAiState(vod.id);
if (st) {
if (st) {
if (st.ai_overview_short) { vod.ai_overview_short = st.ai_overview_short; vod.ai_overview = st.ai_overview || vod.ai_overview || st.ai_overview_short; }
if (st.ai_overview_short) { vod.ai_overview_short = st.ai_overview_short; vod.ai_overview = st.ai_overview || vod.ai_overview || st.ai_overview_short; }
if (st.ai_transcript_json) {
if (st.ai_transcript_json) {
@@ -507,24 +516,15 @@ router.get('/:id', optionalAuth, async (req, res) => {
@@ -507,24 +516,15 @@ router.get('/:id', optionalAuth, async (req, res) => {
}
}
} catch { /* */ }
} catch { /* */ }
- let clips = [];
- try {
- const q = vod.stream_id ? { stream_id: vod.stream_id } : { vod_id: vod.id };
- const co = await media.listClips(q);
- clips = co?.clips || (Array.isArray(co) ? co : []);
- } catch { /* */ }
-
- // Unique-view tracking stays local (content_views is a Live table).
+ const clips = co?.clips || (Array.isArray(co) ? co : []);
try {
try {
- const ip = requesterIp(req);
- const inserted = await db.run('INSERT INTO content_views (content_type, content_id, ip) VALUES (?, ?, ?) ON CONFLICT DO NOTHING', ['vod', vod.id, ip]);
- if (inserted.changes > 0) {
+ if (inserted && inserted.changes > 0) {
const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['vod', vod.id]);
const count = await db.get('SELECT COUNT(*) as c FROM content_views WHERE content_type = ? AND content_id = ?', ['vod', vod.id]);
vod.view_count = Math.max(Number(vod.view_count) || 0, count.c);
vod.view_count = Math.max(Number(vod.view_count) || 0, count.c);
}
}
} catch { /* */ }
} catch { /* */ }
- vod.comment_count = await commentCount('vod', vod.id, vod.title, req.ip); // null when Community is unreachable
+ vod.comment_count = comments;
res.json({ vod, clips });
res.json({ vod, clips });
} catch {
} catch {
res.status(500).json({ error: 'Failed to get VOD' });
res.status(500).json({ error: 'Failed to get VOD' });