Improve video sharing by displaying thumbnails correctly on social media
Implements signed thumbnail URLs using Bunny.net and updates og:image meta tags in video.tsx and creates a /thumbnail/:videoId route. 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/wwY5Klj
This commit is contained in:
parent
f286638b19
commit
e9c136a92a
@ -93,10 +93,13 @@ export default function VideoPage() {
|
|||||||
|
|
||||||
const shareUrl = `${window.location.origin}/video/${video.id}`;
|
const shareUrl = `${window.location.origin}/video/${video.id}`;
|
||||||
|
|
||||||
|
// Use server proxy endpoint for reliable thumbnail access in social sharing
|
||||||
|
const publicThumbnailUrl = `${window.location.origin}/thumbnail/${video.id}`;
|
||||||
|
|
||||||
// Open Graph tags for Facebook
|
// Open Graph tags for Facebook
|
||||||
updateMeta('og:title', video.title);
|
updateMeta('og:title', video.title);
|
||||||
updateMeta('og:description', video.description || `Oglej si ta video na VideoStream`);
|
updateMeta('og:description', video.description || `Oglej si ta video na VideoStream`);
|
||||||
updateMeta('og:image', video.thumbnailUrl);
|
updateMeta('og:image', publicThumbnailUrl);
|
||||||
updateMeta('og:image:width', '1200');
|
updateMeta('og:image:width', '1200');
|
||||||
updateMeta('og:image:height', '630');
|
updateMeta('og:image:height', '630');
|
||||||
updateMeta('og:image:type', 'image/jpeg');
|
updateMeta('og:image:type', 'image/jpeg');
|
||||||
@ -111,7 +114,7 @@ export default function VideoPage() {
|
|||||||
updateMeta('twitter:card', 'summary_large_image', false);
|
updateMeta('twitter:card', 'summary_large_image', false);
|
||||||
updateMeta('twitter:title', video.title, false);
|
updateMeta('twitter:title', video.title, false);
|
||||||
updateMeta('twitter:description', video.description || `Oglej si ta video na VideoStream`, false);
|
updateMeta('twitter:description', video.description || `Oglej si ta video na VideoStream`, false);
|
||||||
updateMeta('twitter:image', video.thumbnailUrl, false);
|
updateMeta('twitter:image', publicThumbnailUrl, false);
|
||||||
}
|
}
|
||||||
}, [video]);
|
}, [video]);
|
||||||
|
|
||||||
|
|||||||
@ -73,6 +73,11 @@ export class BunnyService {
|
|||||||
return `https://${this.hostname}${path}?token=${token}&expires=${expireTimestamp}`;
|
return `https://${this.hostname}${path}?token=${token}&expires=${expireTimestamp}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public method for generating signed URLs for sharing
|
||||||
|
generatePublicSignedUrl(path: string, expiryHours: number = 1): string {
|
||||||
|
return this.generateSignedUrl(path, expiryHours);
|
||||||
|
}
|
||||||
|
|
||||||
private bunnyVideoToVideo(bunnyVideo: BunnyVideo): Video {
|
private bunnyVideoToVideo(bunnyVideo: BunnyVideo): Video {
|
||||||
// Generate signed URLs for private video access
|
// Generate signed URLs for private video access
|
||||||
const videoPath = `/${bunnyVideo.guid}/playlist.m3u8`;
|
const videoPath = `/${bunnyVideo.guid}/playlist.m3u8`;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { Express } from "express";
|
|||||||
import { createServer, type Server } from "http";
|
import { createServer, type Server } from "http";
|
||||||
import { storage } from "./storage";
|
import { storage } from "./storage";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { BunnyService } from "./bunny";
|
||||||
|
|
||||||
export async function registerRoutes(app: Express): Promise<Server> {
|
export async function registerRoutes(app: Express): Promise<Server> {
|
||||||
// Get videos with pagination and filtering
|
// Get videos with pagination and filtering
|
||||||
@ -117,26 +118,35 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Proxy endpoint for thumbnail images from Bunny.net
|
// Public thumbnail endpoint for social media sharing
|
||||||
app.get("/thumbnail/:videoId", async (req, res) => {
|
app.get("/thumbnail/:videoId", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { videoId } = req.params;
|
const { videoId } = req.params;
|
||||||
const hostname = process.env.BUNNY_HOSTNAME!;
|
|
||||||
const thumbnailUrl = `https://${hostname}/${videoId}/thumbnail.jpg`;
|
|
||||||
|
|
||||||
// Attempt to fetch and proxy the thumbnail
|
// Generate signed thumbnail URL using Bunny service
|
||||||
const response = await fetch(thumbnailUrl);
|
const bunnyService = new BunnyService();
|
||||||
|
const thumbnailPath = `/${videoId}/thumbnail.jpg`;
|
||||||
|
const signedThumbnailUrl = bunnyService.generatePublicSignedUrl(thumbnailPath, 24); // 24 hour expiry for sharing
|
||||||
|
|
||||||
|
// Fetch and proxy the thumbnail with proper headers for social media
|
||||||
|
const response = await fetch(signedThumbnailUrl);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
res.set('Content-Type', response.headers.get('content-type') || 'image/jpeg');
|
// Set appropriate headers for social media crawlers
|
||||||
|
res.set({
|
||||||
|
'Content-Type': 'image/jpeg',
|
||||||
|
'Cache-Control': 'public, max-age=86400', // Cache for 24 hours
|
||||||
|
'Access-Control-Allow-Origin': '*', // Allow cross-origin for social media
|
||||||
|
});
|
||||||
|
|
||||||
const buffer = await response.arrayBuffer();
|
const buffer = await response.arrayBuffer();
|
||||||
res.send(Buffer.from(buffer));
|
res.send(Buffer.from(buffer));
|
||||||
} else {
|
} else {
|
||||||
// Fallback to a music-themed placeholder
|
// Fallback to a high-quality video placeholder
|
||||||
res.redirect(`https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450`);
|
res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=1200&h=630`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error proxying thumbnail:", error);
|
console.error("Error proxying thumbnail:", error);
|
||||||
res.redirect(`https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450`);
|
res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=1200&h=630`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user