import { PrismaClient } from '../generated/prisma'; import pg from 'pg'; const prisma = new PrismaClient(); const v1 = new pg.Pool({ host: process.env.V1_DB_HOST || 'localhost', port: +(process.env.V1_DB_PORT || '5444'), user: process.env.V1_DB_USER || 'postgres', password: process.env.V1_DB_PASS || 'password', database: process.env.V1_DB_NAME || 'restoredb' }); // Set this to an existing user ID in v2 const DEFAULT_UPLOADER_ID = process.env.DEFAULT_UPLOADER_ID || 'REPLACE_WITH_V2_USER_ID'; async function migrateVtubers() { console.log('Migrating vtubers...'); const res = await v1.query(`SELECT * FROM vtubers`); for (const vt of res.rows) { await prisma.vtuber.create({ data: { slug: vt.slug, image: vt.image, displayName: vt.display_name, chaturbate: vt.chaturbate, twitter: vt.twitter, patreon: vt.patreon, twitch: vt.twitch, tiktok: vt.tiktok, onlyfans: vt.onlyfans, youtube: vt.youtube, linktree: vt.linktree, carrd: vt.carrd, fansly: vt.fansly, pornhub: vt.pornhub, discord: vt.discord, reddit: vt.reddit, throne: vt.throne, instagram: vt.instagram, facebook: vt.facebook, merch: vt.merch, description: `${vt.description_1 ?? ''}\n${vt.description_2 ?? ''}`.trim() || null, themeColor: vt.theme_color, uploaderId: DEFAULT_UPLOADER_ID } }); } console.log(`Migrated ${res.rows.length} vtubers`); } async function migrateVods() { console.log('Migrating vods...'); const vods = await v1.query(`SELECT * FROM vods`); for (const vod of vods.rows) { // Get linked vtubers const vtuberLinks = await v1.query( `SELECT vtuber_id FROM vods_vtuber_links WHERE vod_id = $1`, [vod.id] ); let vtuberSlugs: string[] = []; if (vtuberLinks.rows.length > 0) { const vtuberRes = await v1.query( `SELECT slug FROM vtubers WHERE id = ANY($1)`, [vtuberLinks.rows.map(r => r.vtuber_id)] ); vtuberSlugs = vtuberRes.rows.map(v => v.slug).filter(Boolean); } // Get thumbnail const thumbLink = await v1.query( `SELECT b2.cdn_url, b2.url FROM vods_thumbnail_links vtl JOIN b2_files b2 ON vtl.b_2_file_id = b2.id WHERE vtl.vod_id = $1 LIMIT 1`, [vod.id] ); // Get source video const videoSrcLink = await v1.query( `SELECT b2.cdn_url, b2.url FROM vods_video_src_b_2_links vsl JOIN b2_files b2 ON vsl.b_2_file_id = b2.id WHERE vsl.vod_id = $1 LIMIT 1`, [vod.id] ); await prisma.vod.create({ data: { uploaderId: DEFAULT_UPLOADER_ID, streamDate: vod.date ?? new Date(), notes: vod.note, sourceVideo: videoSrcLink.rows[0]?.cdn_url || videoSrcLink.rows[0]?.url || null, thumbnail: thumbLink.rows[0]?.cdn_url || thumbLink.rows[0]?.url || null, vtubers: { connect: vtuberSlugs.map(slug => ({ slug })) } } }); } console.log(`Migrated ${vods.rows.length} vods`); } async function main() { try { await migrateVtubers(); await migrateVods(); } catch (err) { console.error(err); } finally { await v1.end(); await prisma.$disconnect(); } } main();