openvibers/openvibe.live Commits
Live: VOD and clip pages link the same item on OpenVibe.Video (#104)
Changes
@@@ -51,6 +51,7 @@<span class="muted"><i class="fa-solid fa-clock"></i> <span id="clp-duration">0:00</span></span><span class="muted"><i class="fa-solid fa-eye"></i> <span id="clp-views">0</span></span><span id="clp-protocol"></span>++ <a id="clp-video-link" class="muted vod-video-link" href="#" style="display:none" title="This clip on OpenVibe.Video"><i class="fa-solid fa-film"></i> OpenVibe.Video</a></div><!-- Who made it, right under the title: "Clipped by <clipper>", or "AI clip · from <streamer>'s stream". --><div id="clp-clipped-by" class="muted" style="margin-top:4px"></div>@@@ -60,6 +60,7 @@<span class="muted"><i class="fa-solid fa-clock"></i> <span id="vp-duration">0:00</span></span><span class="muted"><i class="fa-solid fa-eye"></i> <span id="vp-views">0</span></span><span id="vp-protocol"></span>++ <a id="vp-video-link" class="muted vod-video-link" href="#" style="display:none" title="The same recording on OpenVibe.Video, with AI chapters and captions"><i class="fa-solid fa-film"></i> OpenVibe.Video</a></div><div id="vp-extra-details" class="vod-extra-details" style="display:none"></div><p id="vp-description" class="vod-player-description"></p>@@@ -104,6 +104,17 @@ function downloadMediaTranscript(btn) {} // streamer's user id — the stable chat room key// The site-wide VOD and clip lists are the Content feed now (public/js/content-feed.js).// AI "memory" timeline on the VOD player — clickable timestamps that seek the video.++// OpenVibe.Video (openvibe.video) serves public watch pages for recordings and clips that are public or unlisted++// and finished; the link appears only for those. Private items and recordings in progress keep Live's page alone.++function _showVideoLink(elId, kind, item) {++ const a = document.getElementById(elId);++ if (!a) return;++ const vis = item.visibility || (item.is_public ? 'public' : 'private');++ const ok = (vis === 'public' || vis === 'unlisted') && !item.is_recording && Number.isSafeInteger(Number(item.id));++ a.style.display = ok ? '' : 'none';++ if (ok) a.href = `https://openvibe.video/${kind === 'clip' ? 'c' : 'v'}/${Number(item.id)}`;++}++async function loadVodAiTimeline(vodId) {const el = document.getElementById('vp-ai-timeline');if (!el) return;@@@ -288,6 +299,7 @@ async function loadVodPlayer(vodId, seekTo) {document.getElementById('vp-date').textContent = formatDateTime(v.created_at);document.getElementById('vp-duration').textContent = formatDuration(v.duration_seconds || v.duration);document.getElementById('vp-views').textContent = `${v.view_count || 0} views${v.unique_views != null ? ` · ${v.unique_views} unique` : ''}`;++ _showVideoLink('vp-video-link', 'vod', v);document.getElementById('vp-description').textContent = v.description || '';_renderMediaAiOverview('vp-description', v.ai_overview);_renderMediaTranscript('vp-description', 'vod', v);@@@ -1032,6 +1044,7 @@ async function loadClipPlayer(clipId) {// View countconst clpViews = document.getElementById('clp-views');if (clpViews) clpViews.textContent = `${cl.view_count || 0} views${cl.unique_views != null ? ` · ${cl.unique_views} unique` : ''}`;++ _showVideoLink('clp-video-link', 'clip', cl);// Protocol badgeconst clpProto = document.getElementById('clp-protocol');
@@ -51,6 +51,7 @@
@@ -51,6 +51,7 @@
<span class="muted"><i class="fa-solid fa-clock"></i> <span id="clp-duration">0:00</span></span>
<span class="muted"><i class="fa-solid fa-clock"></i> <span id="clp-duration">0:00</span></span>
<span class="muted"><i class="fa-solid fa-eye"></i> <span id="clp-views">0</span></span>
<span class="muted"><i class="fa-solid fa-eye"></i> <span id="clp-views">0</span></span>
<span id="clp-protocol"></span>
<span id="clp-protocol"></span>
+ <a id="clp-video-link" class="muted vod-video-link" href="#" style="display:none" title="This clip on OpenVibe.Video"><i class="fa-solid fa-film"></i> OpenVibe.Video</a>
</div>
</div>
<!-- Who made it, right under the title: "Clipped by <clipper>", or "AI clip · from <streamer>'s stream". -->
<!-- Who made it, right under the title: "Clipped by <clipper>", or "AI clip · from <streamer>'s stream". -->
<div id="clp-clipped-by" class="muted" style="margin-top:4px"></div>
<div id="clp-clipped-by" class="muted" style="margin-top:4px"></div>
@@ -60,6 +60,7 @@
@@ -60,6 +60,7 @@
<span class="muted"><i class="fa-solid fa-clock"></i> <span id="vp-duration">0:00</span></span>
<span class="muted"><i class="fa-solid fa-clock"></i> <span id="vp-duration">0:00</span></span>
<span class="muted"><i class="fa-solid fa-eye"></i> <span id="vp-views">0</span></span>
<span class="muted"><i class="fa-solid fa-eye"></i> <span id="vp-views">0</span></span>
<span id="vp-protocol"></span>
<span id="vp-protocol"></span>
+ <a id="vp-video-link" class="muted vod-video-link" href="#" style="display:none" title="The same recording on OpenVibe.Video, with AI chapters and captions"><i class="fa-solid fa-film"></i> OpenVibe.Video</a>
</div>
</div>
<div id="vp-extra-details" class="vod-extra-details" style="display:none"></div>
<div id="vp-extra-details" class="vod-extra-details" style="display:none"></div>
<p id="vp-description" class="vod-player-description"></p>
<p id="vp-description" class="vod-player-description"></p>
@@ -104,6 +104,17 @@ function downloadMediaTranscript(btn) {
@@ -104,6 +104,17 @@ function downloadMediaTranscript(btn) {
} // streamer's user id — the stable chat room key
} // streamer's user id — the stable chat room key
// The site-wide VOD and clip lists are the Content feed now (public/js/content-feed.js).
// The site-wide VOD and clip lists are the Content feed now (public/js/content-feed.js).
// AI "memory" timeline on the VOD player — clickable timestamps that seek the video.
// AI "memory" timeline on the VOD player — clickable timestamps that seek the video.
+// OpenVibe.Video (openvibe.video) serves public watch pages for recordings and clips that are public or unlisted
+// and finished; the link appears only for those. Private items and recordings in progress keep Live's page alone.
+function _showVideoLink(elId, kind, item) {
+ const a = document.getElementById(elId);
+ if (!a) return;
+ const vis = item.visibility || (item.is_public ? 'public' : 'private');
+ const ok = (vis === 'public' || vis === 'unlisted') && !item.is_recording && Number.isSafeInteger(Number(item.id));
+ a.style.display = ok ? '' : 'none';
+ if (ok) a.href = `https://openvibe.video/${kind === 'clip' ? 'c' : 'v'}/${Number(item.id)}`;
+}
+
async function loadVodAiTimeline(vodId) {
async function loadVodAiTimeline(vodId) {
const el = document.getElementById('vp-ai-timeline');
const el = document.getElementById('vp-ai-timeline');
if (!el) return;
if (!el) return;
@@ -288,6 +299,7 @@ async function loadVodPlayer(vodId, seekTo) {
@@ -288,6 +299,7 @@ async function loadVodPlayer(vodId, seekTo) {
document.getElementById('vp-date').textContent = formatDateTime(v.created_at);
document.getElementById('vp-date').textContent = formatDateTime(v.created_at);
document.getElementById('vp-duration').textContent = formatDuration(v.duration_seconds || v.duration);
document.getElementById('vp-duration').textContent = formatDuration(v.duration_seconds || v.duration);
document.getElementById('vp-views').textContent = `${v.view_count || 0} views${v.unique_views != null ? ` · ${v.unique_views} unique` : ''}`;
document.getElementById('vp-views').textContent = `${v.view_count || 0} views${v.unique_views != null ? ` · ${v.unique_views} unique` : ''}`;
+ _showVideoLink('vp-video-link', 'vod', v);
document.getElementById('vp-description').textContent = v.description || '';
document.getElementById('vp-description').textContent = v.description || '';
_renderMediaAiOverview('vp-description', v.ai_overview);
_renderMediaAiOverview('vp-description', v.ai_overview);
_renderMediaTranscript('vp-description', 'vod', v);
_renderMediaTranscript('vp-description', 'vod', v);
@@ -1032,6 +1044,7 @@ async function loadClipPlayer(clipId) {
@@ -1032,6 +1044,7 @@ async function loadClipPlayer(clipId) {
// View count
// View count
const clpViews = document.getElementById('clp-views');
const clpViews = document.getElementById('clp-views');
if (clpViews) clpViews.textContent = `${cl.view_count || 0} views${cl.unique_views != null ? ` · ${cl.unique_views} unique` : ''}`;
if (clpViews) clpViews.textContent = `${cl.view_count || 0} views${cl.unique_views != null ? ` · ${cl.unique_views} unique` : ''}`;
+ _showVideoLink('clp-video-link', 'clip', cl);
// Protocol badge
// Protocol badge
const clpProto = document.getElementById('clp-protocol');
const clpProto = document.getElementById('clp-protocol');