videofolxtv/server/videoMigrator.ts
sebastjanartic 6a20747a32 Improve video metadata synchronization with automatic database updates
Implement periodic synchronization for video metadata to ensure the database remains up-to-date with Bunny.net, including initial migration and recurring checks every 5 minutes.

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/yjIG1wi
2025-08-28 17:27:43 +00:00

121 lines
4.2 KiB
TypeScript

import { db } from "./db";
import { sql } from "drizzle-orm";
import { videoSyncService } from "./videoSync";
export class VideoMigrator {
private isRunning = false;
private syncInterval: NodeJS.Timeout | null = null;
async initialize(): Promise<void> {
console.log("🔄 Initializing video metadata migrator...");
// Run initial migration
await this.migrateAllVideoMetadata();
// Set up periodic sync every 5 minutes to ensure database is always complete
this.syncInterval = setInterval(async () => {
console.log("⏰ Starting periodic video metadata sync...");
try {
await this.migrateAllVideoMetadata();
const count = await this.getVideoCount();
console.log(`✅ Periodic sync completed - Database: ${count.database}, Bunny: ${count.bunny}`);
} catch (error) {
console.error("❌ Periodic sync failed:", error);
}
}, 5 * 60 * 1000); // Every 5 minutes
console.log("✅ Video metadata migrator initialized with periodic sync");
}
async migrateAllVideoMetadata(): Promise<void> {
if (this.isRunning) {
console.log("🔄 Migration already running...");
return;
}
this.isRunning = true;
console.log("🔄 Migrating all video metadata from Bunny.net to PostgreSQL...");
try {
// Get all videos from cache
const videoList = videoSyncService.getVideos(1000, 0).videos;
console.log(`📥 Found ${videoList.length} videos in cache`);
let inserted = 0;
let updated = 0;
for (const video of videoList) {
try {
// Upsert video metadata with simple SQL
await db.execute(sql`
INSERT INTO video_metadata (
id, title, description, thumbnail_url, video_url,
duration, views, category, created_at, updated_at
) VALUES (
${video.id}, ${video.title}, ${video.description || ""},
${video.thumbnailUrl}, ${video.videoUrl},
${video.duration}, ${video.views}, ${video.category || ""},
${video.createdAt ? new Date(video.createdAt) : new Date()},
${new Date()}
) ON CONFLICT (id) DO UPDATE SET
title = EXCLUDED.title,
description = EXCLUDED.description,
thumbnail_url = EXCLUDED.thumbnail_url,
video_url = EXCLUDED.video_url,
duration = EXCLUDED.duration,
views = EXCLUDED.views,
category = EXCLUDED.category,
updated_at = EXCLUDED.updated_at
`);
inserted++;
// Log progress every 10 videos
if ((inserted + updated) % 10 === 0) {
console.log(`📊 Progress: ${inserted + updated}/${videoList.length} videos processed`);
}
} catch (error) {
console.error(`❌ Error processing ${video.title}:`, error);
}
}
const dbCount = await this.getVideoCount();
console.log(`✅ Migration completed:`);
console.log(` 📥 Inserted: ${inserted} new videos`);
console.log(` 🔄 Updated: ${updated} existing videos`);
console.log(` 📊 Database total: ${dbCount.database} videos`);
console.log(` 🎥 Bunny.net total: ${dbCount.bunny} videos`);
// Verify all Bunny videos are in database
if (dbCount.database < dbCount.bunny) {
console.warn(`⚠️ Warning: Database has ${dbCount.database} videos but Bunny.net has ${dbCount.bunny}. Some videos may be missing!`);
} else {
console.log("✅ All Bunny.net videos are synchronized with database");
}
} catch (error) {
console.error("❌ Migration failed:", error);
} finally {
this.isRunning = false;
}
}
async getVideoCount(): Promise<{ database: number; bunny: number }> {
try {
const dbResult = await db.execute(sql`SELECT COUNT(*) FROM video_metadata`);
const cachedVideos = videoSyncService.getVideos(1000, 0).videos;
return {
database: Number(dbResult.rows[0]?.count || 0),
bunny: cachedVideos.length
};
} catch (error) {
console.error("❌ Error counting videos:", error);
return { database: 0, bunny: 0 };
}
}
}
export const videoMigrator = new VideoMigrator();