evOo-local/packages/db/src/media-fixtures.ts
2026-10-03 17:55:49 +00:00

266 lines
8.0 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { MediaSourceItem } from "@rakazo/adapter-kit";
import type { BrandKit, TeaserWeights } from "@rakazo/contracts";
import { TEASER_FEATURES } from "@rakazo/contracts";
import { bootstrapUserSpace } from "./bootstrap-user.js";
import type { PrismaClient } from "./client.js";
import { createDb } from "./client.js";
import type { MediaRepos } from "./media.js";
/*
* Synthetic fixtures for the Media Studio Postgres tests. Nothing here describes a
* real person, place, or file.
*/
const signupEnv = { signupsEnabled: "true", signupAllowlist: "" };
export interface MediaTestDb {
prisma: PrismaClient;
newUser(): Promise<{ userId: string; spaceId: string }>;
cleanup(): Promise<void>;
}
export function openMediaTestDb(databaseUrl: string): MediaTestDb {
const db = createDb(databaseUrl, { poolMax: 8 });
const userIds: string[] = [];
return {
prisma: db.prisma,
async newUser() {
// bootstrapUserSpace derives the organization slug from the first 12 characters.
const id = `md${randomUUID().replaceAll("-", "")}`;
userIds.push(id);
await db.prisma.user.create({ data: { id, name: "Test", email: `${id}@rakazo.test` } });
const { spaceId } = await bootstrapUserSpace(db.prisma, { id }, signupEnv, {
claimDeploymentOwner: false,
});
return { userId: id, spaceId };
},
async cleanup() {
const memberships = await db.prisma.member.findMany({
where: { userId: { in: userIds } },
select: { organizationId: true },
});
await db.prisma.organization.deleteMany({
where: { id: { in: memberships.map((member) => member.organizationId) } },
});
await db.prisma.user.deleteMany({ where: { id: { in: userIds } } });
await db.prisma.$disconnect();
await db.pool.end();
},
};
}
export const BASE_TIME = Date.UTC(2026, 0, 1, 8, 0, 0);
export function sourceItem(
index: number,
overrides: Partial<MediaSourceItem> = {},
): MediaSourceItem {
return {
externalId: `clip-${String(index).padStart(5, "0")}.mp4`,
kind: index % 3 === 0 ? "photo" : "video",
capturedAt: new Date(BASE_TIME + index * 60_000),
durationMs: index % 3 === 0 ? null : 10_000,
width: 1920,
height: 1080,
fps: 30,
gpsLat: null,
gpsLon: null,
deviceModel: "Test Camera",
albumNames: [],
isFavorite: false,
fileName: `clip-${index}.mp4`,
sizeBytes: 1000 + index,
revision: "r1",
...overrides,
};
}
export function testEdl(assetId: string, profileId = "example-short-vertical") {
return {
edlVersion: 1,
output: {
profileId,
width: 1080,
height: 1920,
fps: 30,
durationTargetMs: 5000,
durationHardMinMs: 1000,
durationHardMaxMs: 60_000,
},
tracks: {
video: [{ id: "c1", assetId, inMs: 0, outMs: 5000, atMs: 0, reframe: "auto" }],
},
metadata: {},
};
}
export const testBrandKit: BrandKit = {
name: "Test Kit",
colors: {
primary: "#101010",
secondary: "#202020",
accent: "#303030",
text: "#f0f0f0",
background: "#000000",
},
fonts: [],
logoArtifactIds: [],
captionPresets: [],
musicBeds: [],
};
export const testTeaserWeights = Object.fromEntries(
TEASER_FEATURES.map((feature) => [feature, 1]),
) as TeaserWeights;
export function vectorBytes(values: number[]): Uint8Array {
return new Uint8Array(new Float32Array(values).buffer);
}
/** One of every media record in a space, linked the way the product links them. */
export async function seedMediaSpace(media: MediaRepos, spaceId: string, userId: string) {
const source = await media.createSource(spaceId, {
kind: "folder",
label: "Footage",
ownerUserId: userId,
config: { path: "/media/footage" },
});
const synced = await media.upsertSourceItems(spaceId, source.id, [
sourceItem(1, { albumNames: ["Holiday"] }),
sourceItem(2),
]);
const [assetId, secondAssetId] = synced.changedAssetIds as [string, string];
const analysis = await media.upsertAnalysis(spaceId, assetId, {
kind: "embeddings",
modelId: "test-embedder",
modelVersion: "1",
vectors: vectorBytes([1, 0, 0]),
});
await media.replaceSegments(spaceId, assetId, 1, [
{ kind: "shot", startMs: 0, endMs: 4000, labels: ["beach"], transcriptExcerpt: "hello" },
]);
const segments = await media.listSegments(spaceId, [assetId]);
const tracks = await media.replaceFaceTracks(spaceId, assetId, "faces-1", [
{
startMs: 0,
endMs: 3000,
confidenceMin: 0.9,
keyframesPath: `faces/${assetId}/t1.json`,
embedding: vectorBytes([0.1, 0.2, 0.3]),
},
]);
const [person] = await media.createPersonSuggestions(spaceId, [
{ centroid: vectorBytes([0.1, 0.2, 0.3]), trackIds: tracks.map((track) => track.id) },
]);
const mark = await media.createMark(spaceId, {
kind: "tattoo",
label: "arm",
personId: person?.id ?? null,
assetId,
prompt: { tMs: 1000, point: { x: 0.5, y: 0.5 } },
treatment: "blur",
});
await media.replaceClusters(spaceId, "session", [
{
key: "s1",
title: "Morning",
labelSource: "rule",
startAt: new Date(BASE_TIME),
endAt: new Date(BASE_TIME + 120_000),
centroid: null,
coverAssetId: assetId,
assetIds: [assetId, secondAssetId],
},
]);
const collection = (await media.listCollections(spaceId, {})).items[0];
const brandKit = await media.upsertBrandKit(spaceId, { kit: testBrandKit });
const project = await media.createProject(spaceId, userId, {
title: "Teaser",
goal: "teaser",
profileIds: ["example-short-vertical"],
collectionIds: collection ? [collection.id] : [],
brandKitId: brandKit.id,
});
const plan = await media.createPlanVersion(spaceId, {
projectId: project.id,
profileId: "example-short-vertical",
edl: testEdl(assetId),
createdBy: "agent",
rationale: null,
});
const render = await media.createRender(spaceId, {
planId: plan.id,
quality: "draft",
createdBy: userId,
});
await media.claimRender(spaceId, render.id);
await media.updateRender(spaceId, render.id, { status: "done", progress: 1 });
const queuedRender = await media.createRender(spaceId, {
planId: plan.id,
quality: "final",
createdBy: userId,
});
const publication = await media.createPublication(spaceId, userId, {
renderId: render.id,
destination: "bundle",
targetUrl: "https://example.com/offer",
trackingUrl: "https://example.com/offer?utm_content=a",
utm: { source: "shorts", medium: "video", campaign: "launch", content: "a" },
title: "Title",
caption: "Caption",
tags: ["test"],
thumbnailVariant: "t1",
scheduledAt: null,
});
const metric = await media.recordMetric(spaceId, {
publicationId: publication.id,
observedAt: new Date(BASE_TIME).toISOString(),
views: 100,
clicks: 10,
conversions: 1,
revenueCents: 999,
currency: "EUR",
source: "manual",
});
const mapping = await media.saveMetricMapping(spaceId, {
name: "Default",
delimiter: ",",
columns: { publication: "id", observedAt: "date", views: "views" },
publicationKey: "id",
revenueUnit: "major",
defaultCurrency: "EUR",
});
await media.putTeaserModel(spaceId, testTeaserWeights, 3);
const upload = await media.beginUpload(spaceId, {
assetId: secondAssetId,
userId,
sizeBytes: 10,
sha256: "a".repeat(64),
fileName: "clip.mp4",
chunkBytes: 4,
});
return {
sourceId: source.id,
assetId,
secondAssetId,
analysisId: analysis.id,
segmentId: segments[0]?.id as string,
trackId: tracks[0]?.id as string,
personId: person?.id as string,
markId: mark.id,
collectionId: collection?.id as string,
brandKitId: brandKit.id,
projectId: project.id,
planId: plan.id,
renderId: render.id,
queuedRenderId: queuedRender.id,
publicationId: publication.id,
metricId: metric.id,
mappingId: mapping.id as string,
uploadId: upload.id,
};
}
export type SeededMediaSpace = Awaited<ReturnType<typeof seedMediaSpace>>;