Migrate all videos from Bunny.net to the database
Introduces a migration script to import all videos from Bunny.net into the PostgreSQL database, populating the `videos` table with relevant metadata and handling both new and existing video entries. Updates schema to include Bunny.net specific fields. 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/t16ASiD
This commit is contained in:
parent
ffb635eb82
commit
443d92804e
156
server/bunnyMigration.ts
Normal file
156
server/bunnyMigration.ts
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import { db } from "./db";
|
||||||
|
import { videos } from "@shared/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { bunnyService } from "./bunny";
|
||||||
|
|
||||||
|
export class BunnyMigrationService {
|
||||||
|
private migrationInProgress = false;
|
||||||
|
|
||||||
|
async migrateAllVideosToDatabase(): Promise<void> {
|
||||||
|
if (this.migrationInProgress) {
|
||||||
|
console.log("🔄 Migration already in progress, skipping...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.migrationInProgress = true;
|
||||||
|
console.log("🔄 Starting migration of all Bunny.net videos to database...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch all videos from Bunny.net
|
||||||
|
const bunnyVideos = await bunnyService.getAllVideos();
|
||||||
|
console.log(`📥 Found ${bunnyVideos.length} videos in Bunny.net`);
|
||||||
|
|
||||||
|
let insertedCount = 0;
|
||||||
|
let updatedCount = 0;
|
||||||
|
let skippedCount = 0;
|
||||||
|
|
||||||
|
for (const bunnyVideo of bunnyVideos) {
|
||||||
|
try {
|
||||||
|
// Check if video already exists in database
|
||||||
|
const existingVideo = await db
|
||||||
|
.select()
|
||||||
|
.from(videos)
|
||||||
|
.where(eq(videos.id, bunnyVideo.id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const videoData = {
|
||||||
|
id: bunnyVideo.id,
|
||||||
|
title: bunnyVideo.title,
|
||||||
|
description: bunnyVideo.description || "",
|
||||||
|
thumbnailUrl: bunnyVideo.thumbnailUrl,
|
||||||
|
videoUrl: bunnyVideo.videoUrl,
|
||||||
|
videoUrlMp4: bunnyVideo.videoUrlMp4,
|
||||||
|
videoUrlIframe: bunnyVideo.videoUrlIframe,
|
||||||
|
duration: bunnyVideo.duration,
|
||||||
|
views: bunnyVideo.views,
|
||||||
|
category: bunnyVideo.category || "",
|
||||||
|
tags: bunnyVideo.tags || [],
|
||||||
|
isPublic: true,
|
||||||
|
uploadStatus: "completed",
|
||||||
|
originalFileName: bunnyVideo.title,
|
||||||
|
bunnyId: bunnyVideo.id,
|
||||||
|
bunnyLibraryId: process.env.BUNNY_LIBRARY_ID || "476412",
|
||||||
|
source: "bunny",
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existingVideo.length === 0) {
|
||||||
|
// Insert new video
|
||||||
|
await db.insert(videos).values({
|
||||||
|
...videoData,
|
||||||
|
createdAt: bunnyVideo.createdAt ? new Date(bunnyVideo.createdAt) : new Date(),
|
||||||
|
});
|
||||||
|
insertedCount++;
|
||||||
|
} else {
|
||||||
|
// Update existing video
|
||||||
|
await db
|
||||||
|
.update(videos)
|
||||||
|
.set(videoData)
|
||||||
|
.where(eq(videos.id, bunnyVideo.id));
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Error processing video ${bunnyVideo.title}:`, error);
|
||||||
|
skippedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Migration completed:`);
|
||||||
|
console.log(` 📥 Inserted: ${insertedCount} videos`);
|
||||||
|
console.log(` 🔄 Updated: ${updatedCount} videos`);
|
||||||
|
console.log(` ⚠️ Skipped: ${skippedCount} videos`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Migration failed:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.migrationInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncVideoFromBunny(bunnyVideoId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const bunnyVideo = await bunnyService.getVideo(bunnyVideoId);
|
||||||
|
if (!bunnyVideo) {
|
||||||
|
console.warn(`⚠️ Video ${bunnyVideoId} not found in Bunny.net`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoData = {
|
||||||
|
id: bunnyVideo.id,
|
||||||
|
title: bunnyVideo.title,
|
||||||
|
description: bunnyVideo.description || "",
|
||||||
|
thumbnailUrl: bunnyVideo.thumbnailUrl,
|
||||||
|
videoUrl: bunnyVideo.videoUrl,
|
||||||
|
videoUrlMp4: bunnyVideo.videoUrlMp4,
|
||||||
|
videoUrlIframe: bunnyVideo.videoUrlIframe,
|
||||||
|
duration: bunnyVideo.duration,
|
||||||
|
views: bunnyVideo.views,
|
||||||
|
category: bunnyVideo.category || "",
|
||||||
|
tags: bunnyVideo.tags || [],
|
||||||
|
isPublic: true,
|
||||||
|
uploadStatus: "completed",
|
||||||
|
originalFileName: bunnyVideo.title,
|
||||||
|
bunnyId: bunnyVideo.id,
|
||||||
|
bunnyLibraryId: process.env.BUNNY_LIBRARY_ID || "476412",
|
||||||
|
source: "bunny",
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Upsert the video
|
||||||
|
await db
|
||||||
|
.insert(videos)
|
||||||
|
.values({
|
||||||
|
...videoData,
|
||||||
|
createdAt: bunnyVideo.createdAt ? new Date(bunnyVideo.createdAt) : new Date(),
|
||||||
|
})
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: videos.id,
|
||||||
|
set: videoData,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ Synced video: ${bunnyVideo.title}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Error syncing video ${bunnyVideoId}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoCount(): Promise<{ database: number; bunny: number }> {
|
||||||
|
try {
|
||||||
|
const [dbCount, bunnyVideos] = await Promise.all([
|
||||||
|
db.select().from(videos).then(result => result.length),
|
||||||
|
bunnyService.getAllVideos()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
database: dbCount,
|
||||||
|
bunny: bunnyVideos.length
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Error getting video count:", error);
|
||||||
|
return { database: 0, bunny: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bunnyMigration = new BunnyMigrationService();
|
||||||
@ -4,7 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const videos = pgTable("videos", {
|
export const videos = pgTable("videos", {
|
||||||
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
id: varchar("id").primaryKey(),
|
||||||
title: text("title").notNull(),
|
title: text("title").notNull(),
|
||||||
description: text("description").default("").notNull(),
|
description: text("description").default("").notNull(),
|
||||||
thumbnailUrl: text("thumbnail_url").notNull(),
|
thumbnailUrl: text("thumbnail_url").notNull(),
|
||||||
@ -17,13 +17,16 @@ export const videos = pgTable("videos", {
|
|||||||
category: text("category").default("").notNull(),
|
category: text("category").default("").notNull(),
|
||||||
tags: text("tags").array().default([]).notNull(),
|
tags: text("tags").array().default([]).notNull(),
|
||||||
isPublic: boolean("is_public").default(true).notNull(),
|
isPublic: boolean("is_public").default(true).notNull(),
|
||||||
uploadStatus: text("upload_status").default("pending").notNull(), // pending, processing, completed, failed
|
uploadStatus: text("upload_status").default("completed").notNull(), // pending, processing, completed, failed
|
||||||
originalFileName: text("original_file_name"),
|
originalFileName: text("original_file_name"),
|
||||||
fileSize: integer("file_size"), // in bytes
|
fileSize: integer("file_size"), // in bytes
|
||||||
bitrate: integer("bitrate"), // in kbps
|
bitrate: integer("bitrate"), // in kbps
|
||||||
resolution: text("resolution"), // e.g., "1920x1080"
|
resolution: text("resolution"), // e.g., "1920x1080"
|
||||||
format: text("format"), // e.g., "mp4", "avi", "mov"
|
format: text("format"), // e.g., "mp4", "avi", "mov"
|
||||||
encoding: text("encoding"), // e.g., "h264", "h265"
|
encoding: text("encoding"), // e.g., "h264", "h265"
|
||||||
|
bunnyId: varchar("bunny_id"), // Original Bunny.net video ID
|
||||||
|
bunnyLibraryId: varchar("bunny_library_id"), // Bunny.net library ID
|
||||||
|
source: text("source").default("bunny").notNull(), // bunny, upload, manual
|
||||||
createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`),
|
createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`),
|
||||||
updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`),
|
updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user