Use actual video thumbnails for social media sharing

Integrates node-fetch and sharp to download and overlay branding on actual video thumbnails from Bunny.net for social media sharing.

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/xVB32z7
This commit is contained in:
sebastjanartic 2025-08-31 21:06:44 +00:00
parent faed7a9b62
commit c43057ec77

View File

@ -15,6 +15,7 @@ import path from "path";
import fs from "fs";
import session from "express-session";
import sharp from "sharp";
import fetch from "node-fetch";
// Extend express session
declare module "express-session" {
@ -766,7 +767,49 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(404).send('Video not found');
}
// Ustvarimo social media thumbnail sliko z video informacijami
// Najprej poskušamo uporabiti dejanski video thumbnail iz Bunny.net
if (video.thumbnailUrl) {
try {
// Prenesemo dejanski thumbnail iz Bunny.net
const response = await fetch(video.thumbnailUrl);
if (response.ok) {
const thumbnailBuffer = await response.arrayBuffer();
// Povečamo thumbnail na social media velikost (1200x630) z dodano go4.video branding overlay
const overlayedBuffer = await sharp(Buffer.from(thumbnailBuffer))
.resize(1200, 630, { fit: 'cover', position: 'center' })
.composite([
// Dodamo temno overlay za boljši kontrast teksta
{
input: Buffer.from(
`<svg width="1200" height="630">
<rect width="1200" height="630" fill="rgba(0,0,0,0.3)"/>
<!-- go4.video branding v spodnjem desnem kotu -->
<rect x="900" y="480" width="280" height="130" rx="15" fill="rgba(0,0,0,0.7)"/>
<text x="1040" y="515" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="white" text-anchor="middle">go4.video</text>
<text x="1040" y="540" font-family="Arial, sans-serif" font-size="14" fill="rgba(255,255,255,0.8)" text-anchor="middle">Professional Video Platform</text>
<!-- Video trajanje -->
<rect x="970" y="555" width="140" height="30" rx="15" fill="rgba(255,255,255,0.9)"/>
<text x="1040" y="575" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="black" text-anchor="middle">${Math.floor(video.duration / 60)}:${(video.duration % 60).toString().padStart(2, '0')}</text>
</svg>`
),
top: 0,
left: 0,
}
])
.png({ quality: 90, compressionLevel: 6 })
.toBuffer();
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'public, max-age=3600'); // Cache za 1 uro
return res.send(overlayedBuffer);
}
} catch (fetchError) {
console.log('Failed to fetch real thumbnail, falling back to generated:', fetchError);
}
}
// Fallback: ustvarimo generirani thumbnail z video informacijami
const width = 1200;
const height = 630; // Facebook/Twitter optimalne dimenzije