Add support for displaying video advertisement metadata

Integrate Bunny.net API to fetch and display video ad spots, including type, duration, and priority, on the video page. Define new database schema for video ads and create API endpoints for retrieving ad information.

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/jsdCVZt
This commit is contained in:
sebastjanartic
2025-08-28 16:47:09 +00:00
parent ba6cf17bfc
commit 4e4fe369c1
5 changed files with 254 additions and 0 deletions
+63
View File
@@ -9,6 +9,23 @@ interface BunnyVideo {
views: number;
thumbnailFileName?: string;
category?: string;
metaTags?: Array<{
property: string;
value: string;
}>;
moments?: Array<{
type: string; // "preroll", "midroll", "postroll"
position?: number; // seconds for midroll
vastTag?: string;
duration?: number;
network?: string;
title?: string;
}>;
}
interface BunnyVideoDetails extends BunnyVideo {
description?: string;
customMetadata?: Record<string, any>;
}
interface BunnyLibraryResponse {
@@ -124,6 +141,52 @@ export class BunnyService {
}
}
async getVideoDetails(videoId: string): Promise<BunnyVideoDetails | null> {
try {
console.log(`Fetching detailed video data for: ${videoId}`);
const videoDetails: BunnyVideoDetails = await this.makeRequest(`videos/${videoId}`);
console.log(`Retrieved video details with ${videoDetails.moments?.length || 0} ad spots`);
return videoDetails;
} catch (error) {
console.error(`Error fetching video details for ${videoId}:`, error);
return null;
}
}
async getVideoAds(videoId: string): Promise<Array<{
adType: string;
adUrl?: string;
adTitle?: string;
adDuration?: number;
position?: number;
vastTag?: string;
adNetwork?: string;
priority: number;
}>> {
try {
const videoDetails = await this.getVideoDetails(videoId);
if (!videoDetails || !videoDetails.moments) {
console.log(`No ad moments found for video ${videoId}`);
return [];
}
console.log(`Found ${videoDetails.moments.length} ad moments for video ${videoId}`);
return videoDetails.moments.map((moment, index) => ({
adType: moment.type,
adUrl: moment.vastTag || '',
adTitle: moment.title || `${moment.type} Ad`,
adDuration: moment.duration || 30,
position: moment.position || 0,
vastTag: moment.vastTag,
adNetwork: moment.network || 'Unknown',
priority: index + 1,
}));
} catch (error) {
console.error(`Error fetching video ads for ${videoId}:`, error);
return [];
}
}
async getCategories(): Promise<string[]> {
try {
// Get all videos and extract unique categories
+35
View File
@@ -233,6 +233,41 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Get video ads/spots metadata from Bunny.net
app.get("/api/videos/:id/ads", async (req, res) => {
try {
const { id } = req.params;
// Check if video exists first
const video = await storage.getVideo(id);
if (!video) {
return res.status(404).json({ message: "Video not found" });
}
// Get ads from Bunny.net API
let ads = [];
try {
const { BunnyService } = await import("./bunny");
const bunnyService = new BunnyService();
ads = await bunnyService.getVideoAds(id);
console.log(`Retrieved ${ads.length} ad spots for video ${id}`);
} catch (error) {
console.error(`Failed to get ads from Bunny.net for video ${id}:`, error);
// Return empty array if Bunny service fails
ads = [];
}
res.json({
videoId: id,
ads: ads,
totalAds: ads.length
});
} catch (error) {
console.error(`Error fetching ads for video ${id}:`, error);
res.status(500).json({ message: "Failed to fetch video ads" });
}
});
// Update video views
app.post("/api/videos/:id/view", async (req, res) => {
try {