openvibers/openvibe.live Commits
Channel clips: creator names; thumbnail fallback redirects resolve real thumbs
Changes
@@@ -65,9 +65,19 @@ async function generateFor(kind, id, req, res) {}router.post('/generate/vod/:id', optionalAuth, (req, res) => generateFor('vod', req.params.id, req, res));router.post('/generate/clip/:id', optionalAuth, (req, res) => generateFor('clip', req.params.id, req, res));--// GET variants: some legacy <img> fallbacks point here — bounce to Media's public thumb.--router.get('/generate/vod/:id', (req, res) => res.redirect(302, media.thumbUrl(`vod-${req.params.id}`)));--router.get('/generate/clip/:id', (req, res) => res.redirect(302, media.thumbUrl(`clip-${req.params.id}`)));++// GET variants: <img> onerror fallbacks point here. Media's legacy++// /api/thumbnails/<kind>-<id>-<ts>.jpg route resolves the CURRENT thumbnail for++// that id (names drift on regeneration), so bounce through it with a fake ts.++function _generateRedirect(kind) {++ return (req, res) => {++ const id = parseInt(req.params.id, 10);++ if (!Number.isFinite(id)) return res.status(400).json({ error: 'Bad id' });++ res.set('Cache-Control', 'public, max-age=300');++ res.redirect(302, media.publicUrl(`/api/thumbnails/${kind}-${id}-0.jpg`));++ };++}++router.get('/generate/vod/:id', _generateRedirect('vod'));++router.get('/generate/clip/:id', _generateRedirect('clip'));// ── Serve a thumbnail ────────────────────────────────────────// Live thumbnails come from local disk; everything else 302s to openvibe.media.@@@ -319,6 +319,17 @@ router.get('/channel/:username', optionalAuth, async (req, res) => {vods = _rows(vr, 'vods'); vodTotal = vr?.total ?? vods.length;clips = _rows(cr, 'clips'); clipTotal = cr?.total ?? clips.length;clipsOfStreams = _rows(cor, 'clips'); clipsOfTotal = cor?.total ?? clipsOfStreams.length;++ // Media only stores our numeric user ids — resolve clip creator names++ // locally so cards don't render "by Unknown".++ const nameClip = (c) => {++ if (c && c.user_id != null) {++ const u = db.getUserById(c.user_id);++ if (u) { c.clip_creator_username = u.username; c.clip_creator_display_name = u.display_name; }++ }++ return c;++ };++ clips.forEach(nameClip);++ clipsOfStreams.forEach(nameClip);}const followerCount = db.getFollowerCount(channel.user_id);const isFollowing = req.user ? db.isFollowing(req.user.id, channel.user_id) : false;
@@ -65,9 +65,19 @@ async function generateFor(kind, id, req, res) {
@@ -65,9 +65,19 @@ async function generateFor(kind, id, req, res) {
}
}
router.post('/generate/vod/:id', optionalAuth, (req, res) => generateFor('vod', req.params.id, req, res));
router.post('/generate/vod/:id', optionalAuth, (req, res) => generateFor('vod', req.params.id, req, res));
router.post('/generate/clip/:id', optionalAuth, (req, res) => generateFor('clip', req.params.id, req, res));
router.post('/generate/clip/:id', optionalAuth, (req, res) => generateFor('clip', req.params.id, req, res));
-// GET variants: some legacy <img> fallbacks point here — bounce to Media's public thumb.
-router.get('/generate/vod/:id', (req, res) => res.redirect(302, media.thumbUrl(`vod-${req.params.id}`)));
-router.get('/generate/clip/:id', (req, res) => res.redirect(302, media.thumbUrl(`clip-${req.params.id}`)));
+// GET variants: <img> onerror fallbacks point here. Media's legacy
+// /api/thumbnails/<kind>-<id>-<ts>.jpg route resolves the CURRENT thumbnail for
+// that id (names drift on regeneration), so bounce through it with a fake ts.
+function _generateRedirect(kind) {
+ return (req, res) => {
+ const id = parseInt(req.params.id, 10);
+ if (!Number.isFinite(id)) return res.status(400).json({ error: 'Bad id' });
+ res.set('Cache-Control', 'public, max-age=300');
+ res.redirect(302, media.publicUrl(`/api/thumbnails/${kind}-${id}-0.jpg`));
+ };
+}
+router.get('/generate/vod/:id', _generateRedirect('vod'));
+router.get('/generate/clip/:id', _generateRedirect('clip'));
// ── Serve a thumbnail ────────────────────────────────────────
// ── Serve a thumbnail ────────────────────────────────────────
// Live thumbnails come from local disk; everything else 302s to openvibe.media.
// Live thumbnails come from local disk; everything else 302s to openvibe.media.
@@ -319,6 +319,17 @@ router.get('/channel/:username', optionalAuth, async (req, res) => {
@@ -319,6 +319,17 @@ router.get('/channel/:username', optionalAuth, async (req, res) => {
vods = _rows(vr, 'vods'); vodTotal = vr?.total ?? vods.length;
vods = _rows(vr, 'vods'); vodTotal = vr?.total ?? vods.length;
clips = _rows(cr, 'clips'); clipTotal = cr?.total ?? clips.length;
clips = _rows(cr, 'clips'); clipTotal = cr?.total ?? clips.length;
clipsOfStreams = _rows(cor, 'clips'); clipsOfTotal = cor?.total ?? clipsOfStreams.length;
clipsOfStreams = _rows(cor, 'clips'); clipsOfTotal = cor?.total ?? clipsOfStreams.length;
+ // Media only stores our numeric user ids — resolve clip creator names
+ // locally so cards don't render "by Unknown".
+ const nameClip = (c) => {
+ if (c && c.user_id != null) {
+ const u = db.getUserById(c.user_id);
+ if (u) { c.clip_creator_username = u.username; c.clip_creator_display_name = u.display_name; }
+ }
+ return c;
+ };
+ clips.forEach(nameClip);
+ clipsOfStreams.forEach(nameClip);
}
}
const followerCount = db.getFollowerCount(channel.user_id);
const followerCount = db.getFollowerCount(channel.user_id);
const isFollowing = req.user ? db.isFollowing(req.user.id, channel.user_id) : false;
const isFollowing = req.user ? db.isFollowing(req.user.id, channel.user_id) : false;