openvibers/openvibe.live Commits

Channel clips: creator names; thumbnail fallback redirects resolve real thumbs

OP OpenVibers committed 18 Aug 2026 · 1baeb3dfe2 · 2 files, +24 −3

- Channel route enriches clip rows with clip_creator_username/display_name
  (Media only stores numeric ids — cards rendered 'by Unknown').
- GET /api/thumbnails/generate/:kind/:id now bounces through Media's legacy
  id-resolving route (drift-proof) with a cacheable redirect, instead of a
  timestamp-less name that always fell through to the transparent pixel.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Xw6Sf5zzSALw8m93XNu5JE

Changes

ddiff --git a/server/media-proxy/thumbnails.js b/server/media-proxy/thumbnails.js
iindex feabc3a2..bd78f39d 100644
---- a/server/media-proxy/thumbnails.js
++++ b/server/media-proxy/thumbnails.js
@@@ -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.
ddiff --git a/server/streaming/routes.js b/server/streaming/routes.js
iindex 1f7a8aee..a6758f67 100644
---- a/server/streaming/routes.js
++++ b/server/streaming/routes.js
@@@ -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;