Add social media sharing previews for videos

Implement a server-side API to generate dynamic social media shareable image thumbnails for videos and update the client to utilize this new API for open graph and Twitter meta tags.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ab9cd02a-d0b2-4288-9ceb-1964d0059648
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/ab9cd02a-d0b2-4288-9ceb-1964d0059648/mbX83Mu
This commit is contained in:
sebastjanartic
2025-08-31 20:44:31 +00:00
parent 87e07be40f
commit 0425cb899a
3 changed files with 172 additions and 4 deletions
+71
View File
@@ -12,6 +12,7 @@ import {
import multer from "multer";
import { randomUUID } from "crypto";
import path from "path";
import fs from "fs";
import session from "express-session";
import sharp from "sharp";
@@ -755,6 +756,76 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Social media thumbnail generation endpoint
app.get('/api/video-thumbnail/:videoId', async (req, res) => {
try {
const { videoId } = req.params;
const video = await storage.getVideo(videoId);
if (!video) {
return res.status(404).send('Video not found');
}
// Ustvarimo social media thumbnail sliko z video informacijami
const width = 1200;
const height = 630; // Facebook/Twitter optimalne dimenzije
const svg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2D1B69;stop-opacity:1" />
<stop offset="50%" style="stop-color:#6366f1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
</linearGradient>
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#ffffff;stop-opacity:0.9" />
<stop offset="100%" style="stop-color:#ffffff;stop-opacity:0.7" />
</linearGradient>
</defs>
<!-- Ozadje -->
<rect width="${width}" height="${height}" fill="url(#bgGradient)"/>
<!-- Dekorativni elementi -->
<polygon points="100,100 180,180 70,180" fill="#ffffff" opacity="0.1"/>
<polygon points="1000,200 1120,280 880,280" fill="#ffffff" opacity="0.08"/>
<polygon points="200,450 260,510 140,510" fill="#ffffff" opacity="0.12"/>
<polygon points="950,500 1040,590 860,590" fill="#ffffff" opacity="0.06"/>
<!-- Glavni play button v levi strani -->
<circle cx="300" cy="315" r="80" fill="url(#logoGradient)" opacity="0.9"/>
<polygon points="270,285 270,345 330,315" fill="#6366f1"/>
<!-- Video naslov -->
<text x="450" y="250" font-family="Arial, sans-serif" font-size="42" font-weight="bold" fill="white" text-anchor="start">${video.title.length > 35 ? video.title.substring(0, 32) + '...' : video.title}</text>
<!-- Video opis -->
<text x="450" y="300" font-family="Arial, sans-serif" font-size="24" fill="rgba(255,255,255,0.8)" text-anchor="start">${video.description && video.description.length > 60 ? video.description.substring(0, 57) + '...' : video.description || 'Watch this video on go4.video'}</text>
<!-- go4.video logo -->
<text x="450" y="400" font-family="Arial, sans-serif" font-size="32" font-weight="bold" fill="white" text-anchor="start">go4.video</text>
<text x="450" y="430" font-family="Arial, sans-serif" font-size="18" fill="rgba(255,255,255,0.7)" text-anchor="start">Professional Video Streaming Platform</text>
<!-- Video dolžina -->
<rect x="950" y="500" width="200" height="40" rx="20" fill="rgba(0,0,0,0.7)"/>
<text x="1050" y="525" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="white" text-anchor="middle">${Math.floor(video.duration / 60)}:${(video.duration % 60).toString().padStart(2, '0')}</text>
</svg>`;
// Pretvorimo SVG v PNG
const pngBuffer = await sharp(Buffer.from(svg))
.png({ quality: 90, compressionLevel: 6 })
.toBuffer();
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'public, max-age=3600'); // Cache za 1 uro
res.send(pngBuffer);
} catch (error) {
console.error('Error generating video thumbnail:', error);
res.status(500).send('Error generating thumbnail');
}
});
const httpServer = createServer(app);
return httpServer;
}