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
88 lines
2.9 KiB
TypeScript
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(); |