Update video sharing to display correct images and text

Introduce a crawler detection middleware to serve Open Graph and Twitter Card metadata for social media sharing, and update the client-side sharing logic to utilize a dedicated share endpoint.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 401e2ec0-e00d-4f10-9d0e-60f3d479f9a5
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 1a172d71-eb2c-471b-871f-cbf561747bbf
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/401e2ec0-e00d-4f10-9d0e-60f3d479f9a5/lDvepVp
This commit is contained in:
sebastjanartic
2026-01-10 16:29:52 +00:00
parent ff0cc42fa2
commit 9ceb8d3fc6
2 changed files with 122 additions and 12 deletions
+109
View File
@@ -83,6 +83,115 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Setup Replit Auth first
await setupAuth(app);
// Social media crawler detection middleware for /video/:id routes
// This serves proper OG meta tags to crawlers while letting regular users get the SPA
app.get('/video/:id', async (req, res, next) => {
const userAgent = req.headers['user-agent']?.toLowerCase() || '';
// List of social media crawler user agents
const crawlers = [
'facebookexternalhit',
'facebot',
'twitterbot',
'whatsapp',
'telegrambot',
'linkedinbot',
'pinterest',
'slackbot',
'viberbot',
'discordbot',
'applebot',
'googlebot',
'bingbot',
'yandex',
'baiduspider',
'duckduckbot'
];
const isCrawler = crawlers.some(crawler => userAgent.includes(crawler));
if (!isCrawler) {
// Not a crawler, let the SPA handle it
return next();
}
try {
const { id } = req.params;
// Find video from cache
const allVideos = await storage.getVideos(600, 0);
let video;
if (id.length === 36 && id.includes('-')) {
video = allVideos.find(v => v.id === id);
} else if (id.length === 8) {
video = allVideos.find(v => v.id.replace(/-/g, '').substring(0, 8) === id);
} else {
video = allVideos.find(v => v.id.includes(id));
}
if (!video) {
return next(); // Let SPA handle 404
}
const baseUrl = 'https://video.folx.tv';
const videoUrl = `${baseUrl}/video/${video.id}`;
// Get high-quality thumbnail for sharing (1200x630 is ideal for Facebook)
const thumbnailUrl = video.thumbnailUrl
? video.thumbnailUrl.replace('width=400&height=225', 'width=1200&height=630').replace('format=webp', 'format=jpg')
: `${baseUrl}/api/social-image`;
// Clean description for meta tags
const description = video.description
? video.description.substring(0, 200).replace(/[<>"']/g, '')
: `Schauen Sie ${video.title} auf video.folx.tv - Die beste Musik`;
const title = video.title || 'video.folx.tv';
// Return HTML page with OG tags for crawlers
const html = `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title} - video.folx.tv</title>
<!-- Open Graph meta tags for Facebook/Social Media -->
<meta property="og:type" content="video.other">
<meta property="og:title" content="${title}">
<meta property="og:description" content="${description}">
<meta property="og:image" content="${thumbnailUrl}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:url" content="${videoUrl}">
<meta property="og:site_name" content="video.folx.tv">
<meta property="og:locale" content="de_DE">
<!-- Twitter Card meta tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${title}">
<meta name="twitter:description" content="${description}">
<meta name="twitter:image" content="${thumbnailUrl}">
<link rel="canonical" href="${videoUrl}">
</head>
<body>
<h1>${title}</h1>
<p>${description}</p>
<p><a href="${videoUrl}">Watch on video.folx.tv</a></p>
</body>
</html>`;
res.set('Content-Type', 'text/html');
res.send(html);
} catch (error) {
console.error('Error generating OG tags for crawler:', error);
next(); // Let SPA handle errors
}
});
// Add compression middleware for better performance
app.use(compression({
level: 6,