Update video playback to use direct iframe links

Replaced signed URLs with direct iframe URLs for video playback and updated the token generation method in the Bunny service.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: d7424866-83d1-4486-a212-ac12b4c7becf
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/d7424866-83d1-4486-a212-ac12b4c7becf/tr3vokF
This commit is contained in:
sebastjanartic 2025-08-28 17:32:26 +00:00
parent 3481a92615
commit 146e5fd25c
2 changed files with 29 additions and 13 deletions

View File

@ -1,4 +1,5 @@
import { type Video, type InsertVideo } from "@shared/schema"; import { type Video, type InsertVideo } from "@shared/schema";
import crypto from 'crypto';
interface BunnyVideo { interface BunnyVideo {
guid: string; guid: string;
@ -153,7 +154,23 @@ export class BunnyService {
console.log(`Fetching video with description from Bunny: ${guid}`); console.log(`Fetching video with description from Bunny: ${guid}`);
const bunnyVideo: BunnyVideoDetails = await this.makeRequest(`videos/${guid}`); const bunnyVideo: BunnyVideoDetails = await this.makeRequest(`videos/${guid}`);
console.log(`Fetching video: ${bunnyVideo.title} - Description available: ${!!bunnyVideo.description}`); console.log(`Fetching video: ${bunnyVideo.title} - Description available: ${!!bunnyVideo.description}`);
return this.bunnyVideoToVideo(bunnyVideo); // Use direct iframe URL instead of signed URL to avoid complexity
return {
id: bunnyVideo.guid,
title: bunnyVideo.title,
description: bunnyVideo.description || "",
thumbnailUrl: this.getThumbnailUrl(bunnyVideo.guid, bunnyVideo.thumbnailFileName),
videoUrl: `https://iframe.mediadelivery.net/embed/384105/${bunnyVideo.guid}`,
duration: bunnyVideo.length,
views: bunnyVideo.views,
category: bunnyVideo.category || "",
tags: bunnyVideo.metaTags?.map(tag => tag.value) || [],
isPublic: bunnyVideo.status === 4,
uploadStatus: bunnyVideo.status === 4 ? "completed" : "processing",
originalFileName: bunnyVideo.title,
createdAt: new Date(bunnyVideo.dateUploaded),
updatedAt: new Date(bunnyVideo.dateUploaded),
};
} catch (error) { } catch (error) {
console.error(`Error fetching video ${guid} from Bunny:`, error); console.error(`Error fetching video ${guid} from Bunny:`, error);
return null; return null;
@ -220,13 +237,18 @@ export class BunnyService {
// Generate signed URL for private video access // Generate signed URL for private video access
generateSignedUrl(videoId: string, expirationTime: number = 3600): string { generateSignedUrl(videoId: string, expirationTime: number = 3600): string {
// Use the pull zone hostname for video streaming
const baseUrl = `https://${this.hostname}/${videoId}/playlist.m3u8`; const baseUrl = `https://${this.hostname}/${videoId}/playlist.m3u8`;
const expires = Math.floor(Date.now() / 1000) + expirationTime; const expires = Math.floor(Date.now() / 1000) + expirationTime;
// Simple token generation (in production, use proper HMAC signing) // Generate security token using library ID and API key
const token = Buffer.from(`${videoId}:${expires}:${this.apiKey.substring(0, 8)}`).toString('base64'); const securityKey = this.apiKey;
const hashableBase = securityKey + videoId + expires.toString();
return `${baseUrl}?token=${token}&expires=${expires}`; // Create a simple hash for the token
const hash = crypto.createHash('md5').update(hashableBase).digest('hex');
return `${baseUrl}?token=${hash}&expires=${expires}`;
} }
} }

View File

@ -73,16 +73,13 @@ export class DatabaseStorage implements IStorage {
const result = await db.execute(sqlQuery); const result = await db.execute(sqlQuery);
console.log(`📊 DatabaseStorage: Found ${result.rows.length} videos (search: "${search || 'none'}")`); console.log(`📊 DatabaseStorage: Found ${result.rows.length} videos (search: "${search || 'none'}")`);
// Import Bunny service for signed URLs // Transform database rows to Video objects with direct iframe URLs
const { bunnyService } = await import('./bunny');
// Transform database rows to Video objects with signed URLs
return result.rows.map((row: any) => ({ return result.rows.map((row: any) => ({
id: row.id, id: row.id,
title: row.title, title: row.title,
description: row.description, description: row.description,
thumbnailUrl: row.thumbnail_url, thumbnailUrl: row.thumbnail_url,
videoUrl: bunnyService.generateSignedUrl(row.id, 7200), // 2 hour expiration videoUrl: `https://iframe.mediadelivery.net/embed/384105/${row.id}`, // Direct Bunny.net iframe
duration: row.duration, duration: row.duration,
views: row.views, views: row.views,
category: row.category, category: row.category,
@ -107,16 +104,13 @@ export class DatabaseStorage implements IStorage {
if (result.rows.length === 0) return undefined; if (result.rows.length === 0) return undefined;
// Import Bunny service for signed URLs
const { bunnyService } = await import('./bunny');
const row = result.rows[0] as any; const row = result.rows[0] as any;
return { return {
id: row.id, id: row.id,
title: row.title, title: row.title,
description: row.description, description: row.description,
thumbnailUrl: row.thumbnail_url, thumbnailUrl: row.thumbnail_url,
videoUrl: bunnyService.generateSignedUrl(row.id, 7200), // 2 hour expiration videoUrl: `https://iframe.mediadelivery.net/embed/384105/${row.id}`, // Direct Bunny.net iframe
duration: row.duration, duration: row.duration,
views: row.views, views: row.views,
category: row.category, category: row.category,