import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { PLATE_EXTS, parsePlateFilename } from "@/lib/batch-upload-parser"; export type PlatePreviewItemStatus = | "match" // shot found → a new footage plate will be added | "duplicate" // the shot already has a plate with this exact filename | "no-match" // no shot in this project matches the filename | "unsupported"; // extension is not an accepted video format export interface PlatePreviewItem { fileName: string; shotCode: string | null; shotId: string | null; /** Label the new plate will be created with */ label: string; status: PlatePreviewItemStatus; /** Number of plates the matched shot already has */ existingPlateCount: number; } /** * POST /api/batch-upload/plates * Body: { projectId: string; fileNames: string[] } * * Previews which shot each footage plate file will be attached to, derived * from the filename. e.g. "DH2_VFX_058_120_REF.mov" → shot "DH2_VFX_058_120" * with the label "REF". Nothing is uploaded. */ export async function POST(req: NextRequest) { const session = await auth(); if (!session?.user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); let body: unknown; try { body = await req.json(); } catch { return NextResponse.json({ error: "Invalid JSON" }, { status: 400 }); } const { projectId, fileNames } = body as { projectId?: unknown; fileNames?: unknown }; if (!projectId || typeof projectId !== "string" || !Array.isArray(fileNames)) { return NextResponse.json( { error: "projectId and fileNames[] are required" }, { status: 400 } ); } const shots = await db.shot.findMany({ where: { projectId }, select: { id: true, shotCode: true, footagePlates: { select: { fileName: true } }, }, }); const shotCodes = shots.map((s) => s.shotCode); const shotByCode = new Map(shots.map((s) => [s.shotCode, s])); // Several files in one batch can land on the same shot, so keep a running // count per shot to number the "Plate N" fallback labels correctly. const allocated = new Map(); const items: PlatePreviewItem[] = fileNames.map((fileName: string) => { const parsed = parsePlateFilename(fileName, shotCodes); if (!PLATE_EXTS.has(parsed.ext)) { return { fileName, shotCode: null, shotId: null, label: "", status: "unsupported" as const, existingPlateCount: 0, }; } const shot = parsed.shotCode ? shotByCode.get(parsed.shotCode) ?? null : null; if (!shot) { return { fileName, shotCode: null, shotId: null, label: "", status: "no-match" as const, existingPlateCount: 0, }; } const existingPlateCount = shot.footagePlates.length; // Re-running a batch shouldn't silently pile up identical plates. const isDuplicate = shot.footagePlates.some( (p) => p.fileName.toLowerCase() === fileName.toLowerCase() ); if (isDuplicate) { return { fileName, shotCode: shot.shotCode, shotId: shot.id, label: parsed.label, status: "duplicate" as const, existingPlateCount, }; } const takenSoFar = allocated.get(shot.id) ?? 0; allocated.set(shot.id, takenSoFar + 1); return { fileName, shotCode: shot.shotCode, shotId: shot.id, label: parsed.label || `Plate ${existingPlateCount + takenSoFar + 1}`, status: "match" as const, existingPlateCount, }; }); return NextResponse.json({ items }); }