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>
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
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,
|
||||
@@ -36,44 +40,57 @@ export async function POST(
|
||||
}
|
||||
|
||||
const outputDir = `outputs/repurposed_${id.slice(0, 8)}`;
|
||||
const resized = await repurposeImage(asset.filePath, targetFormats, outputDir);
|
||||
const results = [];
|
||||
|
||||
for (const file of resized) {
|
||||
const originalMeta = asset.metadata ? JSON.parse(asset.metadata) : {};
|
||||
let newMeta = { ...originalMeta };
|
||||
// Launch async — Gemini generation takes time
|
||||
(async () => {
|
||||
try {
|
||||
const expectedFiles = await repurposeImage(
|
||||
asset.filePath,
|
||||
asset.dimensions,
|
||||
targetFormats,
|
||||
outputDir
|
||||
);
|
||||
|
||||
// Re-tone caption if platform changed and caption exists
|
||||
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 if re-toning fails
|
||||
// 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);
|
||||
}
|
||||
})();
|
||||
|
||||
const newAsset = 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,
|
||||
},
|
||||
});
|
||||
|
||||
results.push(newAsset);
|
||||
}
|
||||
|
||||
return Response.json({ created: results.length, assets: results });
|
||||
return Response.json({ status: "repurposing", formats: targetFormats });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user