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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user