Files
Trey t 807dfc539b feat: add asset preferences, video research, and Remotion ad assets
- Add thumbs-down feedback modal and preference API endpoint
- Add AI UGC video platforms research doc
- Add ReflectAd Remotion composition with public flow assets
- Add gemini-ad-designer and poster-ad-designer pipeline skills
- Add research_reflect_v1.1 pipeline script

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:28:07 -05:00

97 lines
3.1 KiB
TypeScript

import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { repurposeImage, retoneCaption, getAvailableFormats } from "@/lib/repurpose";
import { existsSync } from "fs";
import path from "path";
const PIPELINE_ROOT = process.env.PIPELINE_ROOT || path.join(process.cwd(), "pipeline");
export async function GET(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const session = await auth();
if (!session) return new Response("Unauthorized", { status: 401 });
const { id } = await params;
const asset = await prisma.asset.findUnique({ where: { id } });
if (!asset) return Response.json({ error: "Not found" }, { status: 404 });
const formats = getAvailableFormats(asset.dimensions);
return Response.json({ formats, currentDimensions: asset.dimensions });
}
export async function POST(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const session = await auth();
if (!session) return new Response("Unauthorized", { status: 401 });
const { id } = await params;
const body = await request.json();
const { formats: targetFormats } = body as { formats: string[] };
const asset = await prisma.asset.findUnique({ where: { id } });
if (!asset) return Response.json({ error: "Not found" }, { status: 404 });
if (asset.type !== "image") {
return Response.json({ error: "Only image assets can be repurposed" }, { status: 400 });
}
const outputDir = `outputs/repurposed_${id.slice(0, 8)}`;
// Launch async — Gemini generation takes time
(async () => {
try {
const expectedFiles = await repurposeImage(
asset.filePath,
asset.dimensions,
targetFormats,
outputDir
);
// Create DB records for files that were actually generated
for (const file of expectedFiles) {
const fullPath = path.join(PIPELINE_ROOT, file.filePath);
if (!existsSync(fullPath)) continue;
const originalMeta = asset.metadata ? JSON.parse(asset.metadata) : {};
let newMeta = { ...originalMeta };
if (originalMeta.caption && asset.platform && file.platform !== asset.platform) {
try {
const newCaption = await retoneCaption(
originalMeta.caption,
asset.platform,
file.platform
);
newMeta = { ...newMeta, caption: newCaption, originalCaption: originalMeta.caption };
} catch {
// Keep original caption
}
}
await prisma.asset.create({
data: {
campaignId: asset.campaignId,
type: "image",
platform: file.platform,
format: "png",
filePath: file.filePath,
fileName: file.fileName,
dimensions: file.dimensions,
metadata: JSON.stringify(newMeta),
status: "draft",
parentAssetId: asset.id,
},
});
}
} catch (err) {
console.error(`Repurpose failed for asset ${id}:`, err);
}
})();
return Response.json({ status: "repurposing", formats: targetFormats });
}