Improve video sharing by displaying thumbnails correctly on social media

Implements signed thumbnail URLs using Bunny.net and updates og:image meta tags in video.tsx and creates a /thumbnail/:videoId route.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 50814a1e-92e4-4968-856f-7bc7eedf5e8f
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/50814a1e-92e4-4968-856f-7bc7eedf5e8f/wwY5Klj
This commit is contained in:
sebastjanartic
2025-08-04 19:16:54 +00:00
parent f286638b19
commit e9c136a92a
3 changed files with 29 additions and 11 deletions
+19 -9
View File
@@ -2,6 +2,7 @@ import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { z } from "zod";
import { BunnyService } from "./bunny";
export async function registerRoutes(app: Express): Promise<Server> {
// Get videos with pagination and filtering
@@ -117,26 +118,35 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Proxy endpoint for thumbnail images from Bunny.net
// Public thumbnail endpoint for social media sharing
app.get("/thumbnail/:videoId", async (req, res) => {
try {
const { videoId } = req.params;
const hostname = process.env.BUNNY_HOSTNAME!;
const thumbnailUrl = `https://${hostname}/${videoId}/thumbnail.jpg`;
// Attempt to fetch and proxy the thumbnail
const response = await fetch(thumbnailUrl);
// Generate signed thumbnail URL using Bunny service
const bunnyService = new BunnyService();
const thumbnailPath = `/${videoId}/thumbnail.jpg`;
const signedThumbnailUrl = bunnyService.generatePublicSignedUrl(thumbnailPath, 24); // 24 hour expiry for sharing
// Fetch and proxy the thumbnail with proper headers for social media
const response = await fetch(signedThumbnailUrl);
if (response.ok) {
res.set('Content-Type', response.headers.get('content-type') || 'image/jpeg');
// Set appropriate headers for social media crawlers
res.set({
'Content-Type': 'image/jpeg',
'Cache-Control': 'public, max-age=86400', // Cache for 24 hours
'Access-Control-Allow-Origin': '*', // Allow cross-origin for social media
});
const buffer = await response.arrayBuffer();
res.send(Buffer.from(buffer));
} else {
// Fallback to a music-themed placeholder
res.redirect(`https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450`);
// Fallback to a high-quality video placeholder
res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=1200&h=630`);
}
} catch (error) {
console.error("Error proxying thumbnail:", error);
res.redirect(`https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450`);
res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=1200&h=630`);
}
});