Restored to '551aaee106c3dc6c34e10193d8683fa67e7f9bea'
Replit-Restored-To: 551aaee106
This commit is contained in:
+7
-37
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user