Generate dynamic video thumbnails with title and duration information
Replaces placeholder thumbnail service with dynamic SVG generation in /thumbnail/:videoId endpoint. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 50814a1e-92e4-4968-856f-7bc7eedf5e8f Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/50814a1e-92e4-4968-856f-7bc7eedf5e8f/ziBt2Ne
This commit is contained in:
parent
bbae36fb83
commit
dae60951f4
@ -118,29 +118,64 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Public thumbnail endpoint for social media sharing
|
// Public thumbnail endpoint that generates SVG thumbnails
|
||||||
app.get("/thumbnail/:videoId", async (req, res) => {
|
app.get("/thumbnail/:videoId", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { videoId } = req.params;
|
const { videoId } = req.params;
|
||||||
|
|
||||||
// Get video info for generating proper thumbnail
|
// Get video info for generating proper thumbnail
|
||||||
const video = await storage.getVideo(videoId);
|
const video = await storage.getVideo(videoId);
|
||||||
|
|
||||||
|
let title = "Video";
|
||||||
|
let duration = "0:00";
|
||||||
|
|
||||||
if (video) {
|
if (video) {
|
||||||
// Clean up title for display
|
title = video.title.replace('.mp4', '').substring(0, 35);
|
||||||
const title = video.title.replace('.mp4', '').substring(0, 45);
|
const minutes = Math.floor(video.duration / 60);
|
||||||
|
const seconds = video.duration % 60;
|
||||||
// Generate a high-quality video thumbnail using a more reliable service
|
duration = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
// Use a video-themed background with proper social media dimensions
|
|
||||||
const thumbnailUrl = `https://via.placeholder.com/400x225/1a1a1a/ffffff.png?text=${encodeURIComponent(title)}`;
|
|
||||||
|
|
||||||
res.redirect(thumbnailUrl);
|
|
||||||
} else {
|
|
||||||
// Video not found - use generic placeholder
|
|
||||||
res.redirect(`https://via.placeholder.com/400x225/1a1a1a/ffffff.png?text=Video+Not+Found`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate SVG thumbnail with video title and duration
|
||||||
|
const svg = `
|
||||||
|
<svg width="400" height="225" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#1e40af;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#0f172a;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="400" height="225" fill="url(#grad)"/>
|
||||||
|
<circle cx="200" cy="112.5" r="30" fill="rgba(255,255,255,0.8)"/>
|
||||||
|
<polygon points="190,98 190,127 215,112.5" fill="#000"/>
|
||||||
|
<text x="200" y="170" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="14" font-weight="bold">
|
||||||
|
${title.split(' ').map((word, i) => `<tspan x="200" dy="${i === 0 ? 0 : 18}">${word}</tspan>`).join('')}
|
||||||
|
</text>
|
||||||
|
<text x="350" y="20" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="12" font-weight="bold">
|
||||||
|
${duration}
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'image/svg+xml');
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||||
|
res.send(svg);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error serving thumbnail:", error);
|
console.error("Error generating thumbnail:", error);
|
||||||
res.redirect(`https://via.placeholder.com/400x225/1a1a1a/ffffff.png?text=Error+Loading+Video`);
|
|
||||||
|
// Fallback SVG
|
||||||
|
const fallbackSvg = `
|
||||||
|
<svg width="400" height="225" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="400" height="225" fill="#1a1a1a"/>
|
||||||
|
<circle cx="200" cy="112.5" r="30" fill="rgba(255,255,255,0.8)"/>
|
||||||
|
<polygon points="190,98 190,127 215,112.5" fill="#000"/>
|
||||||
|
<text x="200" y="170" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="16">Video Thumbnail</text>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'image/svg+xml');
|
||||||
|
res.send(fallbackSvg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user