From 9ceb8d3fc6453d6c2ded755d5faa3410ccf2a5cc Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Sat, 10 Jan 2026 16:29:52 +0000 Subject: [PATCH] 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 --- client/src/pages/VideoPage.tsx | 25 ++++---- server/routes.ts | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 12 deletions(-) diff --git a/client/src/pages/VideoPage.tsx b/client/src/pages/VideoPage.tsx index ce3ccc8..be986b0 100644 --- a/client/src/pages/VideoPage.tsx +++ b/client/src/pages/VideoPage.tsx @@ -308,23 +308,24 @@ export default function VideoPage() { }; + // Share URL uses special endpoint with proper OG meta tags for social media previews + // This URL shows thumbnail, title, and description when shared on Facebook, Viber, WhatsApp, etc. const getShareUrl = () => { - if (!currentVideo?.id) return window.location.origin; - // Use custom domain if set, otherwise current domain - const baseUrl = import.meta.env.VITE_SHARE_DOMAIN || window.location.origin; - return `${baseUrl}/video/${currentVideo.id}`; - }; - - // Facebook share URL uses special endpoint with proper OG meta tags - const getFacebookShareUrl = () => { if (!currentVideo?.id) return window.location.origin; const baseUrl = 'https://video.folx.tv'; return `${baseUrl}/share/video/${currentVideo.id}`; }; + // Direct video URL (for display purposes) + const getVideoUrl = () => { + if (!currentVideo?.id) return window.location.origin; + const baseUrl = 'https://video.folx.tv'; + return `${baseUrl}/video/${currentVideo.id}`; + }; + const copyToClipboard = async () => { try { - await navigator.clipboard.writeText(getFacebookShareUrl()); + await navigator.clipboard.writeText(getShareUrl()); const notification = document.createElement('div'); notification.textContent = 'Link kopiert!'; notification.className = 'fixed top-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg z-50 transition-opacity duration-300'; @@ -739,19 +740,19 @@ export default function VideoPage() { {showShareMenu && (
- +
Facebook
- +
Twitter
- +
WhatsApp diff --git a/server/routes.ts b/server/routes.ts index 1c69c94..26a31a2 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -83,6 +83,115 @@ export async function registerRoutes(app: Express): Promise { // 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 = ` + + + + + ${title} - video.folx.tv + + + + + + + + + + + + + + + + + + + + + + +

${title}

+

${description}

+

Watch on video.folx.tv

+ +`; + + 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,