Restored to '551aaee106c3dc6c34e10193d8683fa67e7f9bea'

Replit-Restored-To: 551aaee106
This commit is contained in:
sebastjanartic
2025-08-04 20:10:59 +00:00
parent bb26fd3cdc
commit 9b192c6e1b
27 changed files with 107 additions and 2599 deletions
+7 -37
View File
@@ -1,5 +1,4 @@
import { type Video, type InsertVideo } from "@shared/schema";
import crypto from 'crypto';
interface BunnyVideo {
guid: string;
@@ -23,13 +22,11 @@ export class BunnyService {
private apiKey: string;
private libraryId: string;
private hostname: string;
private securityKey: string;
constructor() {
this.apiKey = process.env.BUNNY_API_KEY!;
this.libraryId = process.env.BUNNY_LIBRARY_ID!;
this.hostname = process.env.BUNNY_HOSTNAME!;
this.securityKey = process.env.BUNNY_SECURITY_KEY || ''; // CDN security key for signing URLs
if (!this.apiKey || !this.libraryId || !this.hostname) {
throw new Error("Missing Bunny.net configuration");
@@ -54,41 +51,14 @@ export class BunnyService {
return response.json();
}
private generateSignedUrl(path: string, expiryHours: number = 1): string {
if (!this.securityKey) {
// If no security key, return iframe embed as fallback
const videoId = path.split('/')[1];
return `https://iframe.mediadelivery.net/embed/${this.libraryId}/${videoId}?controls=true&autoplay=false`;
}
const expireTimestamp = Math.floor(Date.now() / 1000) + (expiryHours * 3600);
// Bunny.net uses SHA256 for token generation according to docs
// hashableBase = securityKey + signaturePath + expires + userIp + parameterData
const hashableBase = `${this.securityKey}${path}${expireTimestamp}`;
const hash = crypto.createHash('sha256').update(hashableBase).digest();
const token = Buffer.from(hash).toString('base64')
.replace(/\n/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
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 {
// Since Bunny.net private library token authentication is not working,
// use iframe embed approach for reliable video playback
const videoUrl = `https://iframe.mediadelivery.net/embed/${this.libraryId}/${bunnyVideo.guid}?controls=true&autoplay=false`;
// Use proxy endpoint that will try multiple thumbnail sources
const thumbnailUrl = `/thumbnail/${bunnyVideo.guid}`;
// For private videos, we'll generate a video poster frame from the iframe
// This is the best approach for private Bunny.net videos
const thumbnailUrl = `https://iframe.mediadelivery.net/embed/${this.libraryId}/${bunnyVideo.guid}?poster=true`;
// For private videos, we'll use an iframe embed URL which handles authentication
// Enable controls, allow fullscreen, and ensure player functionality
const videoUrl = `https://iframe.mediadelivery.net/embed/${this.libraryId}/${bunnyVideo.guid}?controls=true&autoplay=false&preload=metadata`;
return {
id: bunnyVideo.guid,