videofolxtv/server/videoMigrator.ts
sebastjanartic f658b64b56 Migrate video metadata to PostgreSQL for improved data management
Introduces a new video migration service to transfer video metadata from Bunny.net to PostgreSQL. Updates storage logic to prioritize PostgreSQL, removes redundant fields from the video schema, and adds database migration functionality to the server startup.

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/i1Fg8VZ
2025-08-28 17:23:05 +00:00

88 lines
2.9 KiB
TypeScript

import { db } from "./db";
import { sql } from "drizzle-orm";
import { videoSyncService } from "./videoSync";
export class VideoMigrator {
private isRunning = false;
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);
}
}
console.log(`✅ Migration completed:`);
console.log(` 📥 Inserted: ${inserted} new videos`);
console.log(` 🔄 Updated: ${updated} existing videos`);
} 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();