fp/services/our/scripts/2025-08-12-migrate-from-v1.ts
2025-08-12 21:43:26 -08:00

118 lines
3.2 KiB
TypeScript

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();