Spaces:
Runtime error
Runtime error
File size: 7,540 Bytes
ceb943f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | /**
* Seed script for local testing without real YouTube videos.
* Inserts fake videos, detected objects, and marketplace matches
* tied to your connected YouTube channel.
*
* Usage: npx tsx scripts/seed-test-data.ts
*/
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as dotenv from "dotenv";
import * as schema from "../src/lib/db/schema";
const { youtubeVideos, detectedObjects, marketplaceMatches } = schema;
dotenv.config({ path: ".env.local" });
const run = async () => {
const connectionString = process.env.DIRECT_URL;
if (!connectionString) throw new Error("DIRECT_URL not set in .env.local");
const sql = postgres(connectionString, { max: 1 });
const db = drizzle(sql, { schema, casing: "snake_case" });
// 1. Find the connected channel
const channel = await db.query.youtubeChannels.findFirst();
if (!channel) {
console.error("❌ No YouTube channel found. Connect one on the dashboard first.");
await sql.end();
process.exit(1);
}
console.log(`✅ Found channel: ${channel.channelName} (${channel.id})`);
// 2. Insert fake videos
const videos = [
{ videoId: "seed-vid-001", title: "Tech Gadget Unboxing", duration: "PT12M30S", viewCount: 4200 },
{ videoId: "seed-vid-002", title: "Home Office Setup Tour", duration: "PT8M15S", viewCount: 1800 },
{ videoId: "seed-vid-003", title: "Fashion Haul 2024", duration: "PT15M00S", viewCount: 9100 },
{ videoId: "seed-vid-004", title: "Audio Equipment Review", duration: "PT20M45S", viewCount: 3300 },
{ videoId: "seed-vid-005", title: "Living Room Makeover", duration: "PT18M10S", viewCount: 5600 },
];
const insertedVideos = await db
.insert(youtubeVideos)
.values(
videos.map((v) => ({
channelId: channel.id,
videoId: v.videoId,
title: v.title,
description: `Seed data: ${v.title}`,
thumbnailUrl: `https://i.ytimg.com/vi/${v.videoId}/mqdefault.jpg`,
duration: v.duration,
viewCount: v.viewCount,
publishedAt: new Date("2024-11-15T10:00:00Z"),
}))
)
.onConflictDoNothing()
.returning();
console.log(`✅ Inserted ${insertedVideos.length} videos`);
// Re-fetch all seed videos (in case they already existed)
const allVideos = await db.query.youtubeVideos.findMany({
where: (t, { inArray }) => inArray(t.videoId, videos.map((v) => v.videoId)),
});
// 3. Insert detected objects (mix of statuses for testing moderation)
const objects: Array<{
videoId: string;
objectName: string;
category: "Tech" | "Fashion" | "Furniture" | "Audio" | "Other";
moderationStatus: "PENDING" | "APPROVED" | "REJECTED";
confidenceScore: number;
frameTimestamp: number;
}> = [
{ videoId: "seed-vid-001", objectName: "Wireless Headphones", category: "Audio", moderationStatus: "APPROVED", confidenceScore: 0.94, frameTimestamp: 45 },
{ videoId: "seed-vid-001", objectName: "USB-C Hub", category: "Tech", moderationStatus: "APPROVED", confidenceScore: 0.88, frameTimestamp: 120 },
{ videoId: "seed-vid-001", objectName: "Mechanical Keyboard", category: "Tech", moderationStatus: "PENDING", confidenceScore: 0.76, frameTimestamp: 200 },
{ videoId: "seed-vid-002", objectName: "Standing Desk", category: "Furniture", moderationStatus: "APPROVED", confidenceScore: 0.91, frameTimestamp: 30 },
{ videoId: "seed-vid-002", objectName: "Ergonomic Chair", category: "Furniture", moderationStatus: "APPROVED", confidenceScore: 0.89, frameTimestamp: 60 },
{ videoId: "seed-vid-002", objectName: "Monitor Light Bar", category: "Tech", moderationStatus: "REJECTED", confidenceScore: 0.62, frameTimestamp: 90 },
{ videoId: "seed-vid-003", objectName: "Leather Jacket", category: "Fashion", moderationStatus: "APPROVED", confidenceScore: 0.95, frameTimestamp: 15 },
{ videoId: "seed-vid-003", objectName: "Running Shoes", category: "Fashion", moderationStatus: "PENDING", confidenceScore: 0.71, frameTimestamp: 180 },
{ videoId: "seed-vid-004", objectName: "Studio Microphone", category: "Audio", moderationStatus: "APPROVED", confidenceScore: 0.93, frameTimestamp: 55 },
{ videoId: "seed-vid-004", objectName: "Audio Interface", category: "Audio", moderationStatus: "APPROVED", confidenceScore: 0.87, frameTimestamp: 110 },
{ videoId: "seed-vid-005", objectName: "Throw Pillows", category: "Furniture", moderationStatus: "APPROVED", confidenceScore: 0.82, frameTimestamp: 25 },
{ videoId: "seed-vid-005", objectName: "Floor Lamp", category: "Furniture", moderationStatus: "PENDING", confidenceScore: 0.74, frameTimestamp: 95 },
];
const videoMap = new Map(allVideos.map((v) => [v.videoId, v.id]));
const insertedObjects = await db
.insert(detectedObjects)
.values(
objects.map((o) => ({
videoId: videoMap.get(o.videoId)!,
objectName: o.objectName,
category: o.category,
confidenceScore: o.confidenceScore,
frameTimestamp: o.frameTimestamp,
status: "approved" as const,
moderationStatus: o.moderationStatus,
detectionMetadata: { source: "seed", model: "test" },
}))
)
.onConflictDoNothing()
.returning();
console.log(`✅ Inserted ${insertedObjects.length} detected objects`);
// Re-fetch approved objects for marketplace match seeding
const approvedObjects = await db.query.detectedObjects.findMany({
where: (t, { and, eq: eq_ }) =>
and(eq_(t.moderationStatus, "APPROVED")),
});
// 4. Insert marketplace matches for approved objects
const matchTemplates: Array<{
marketplace: "amazon" | "ebay" | "etsy";
productName: string;
price: number;
}> = [
{ marketplace: "amazon", productName: "Sony WH-1000XM5 Headphones", price: 349.99 },
{ marketplace: "amazon", productName: "Anker USB-C 7-in-1 Hub", price: 39.99 },
{ marketplace: "amazon", productName: "FlexiSpot E7 Standing Desk", price: 449.0 },
{ marketplace: "amazon", productName: "Herman Miller Aeron Chair", price: 1199.0 },
{ marketplace: "amazon", productName: "Shure SM7B Microphone", price: 249.0 },
{ marketplace: "ebay", productName: "Audio-Technica AT2020 Mic", price: 89.99 },
{ marketplace: "ebay", productName: "Focusrite Solo Audio Interface", price: 119.99 },
{ marketplace: "etsy", productName: "Handmade Leather Jacket", price: 289.0 },
{ marketplace: "amazon", productName: "IKEA Lack Floor Lamp", price: 24.99 },
];
let matchCount = 0;
for (let i = 0; i < approvedObjects.length && i < matchTemplates.length; i++) {
const obj = approvedObjects[i];
const tmpl = matchTemplates[i];
const inserted = await db
.insert(marketplaceMatches)
.values({
objectId: obj.id,
marketplace: tmpl.marketplace,
productId: `seed-prod-${String(i + 1).padStart(3, "0")}`,
productName: tmpl.productName,
price: tmpl.price,
availabilityStatus: "IN_STOCK",
affiliateUrl: `https://www.amazon.com/dp/seed-${i + 1}`,
})
.onConflictDoNothing()
.returning();
matchCount += inserted.length;
}
console.log(`✅ Inserted ${matchCount} marketplace matches`);
console.log("\n🎉 Seed complete. Test pages to try:");
console.log(` • Dashboard: http://localhost:3000/dashboard`);
console.log(` • Public Vault: http://localhost:3000/vault/${channel.creatorSlug}`);
await sql.end();
};
run().catch((err) => {
console.error("❌ Seed failed:", err);
process.exit(1);
});
|