Files
vfxreview/actions/deliveries.ts
T
twotalesanimation 7f066c1372
Deploy / deploy (push) Successful in 7m28s
feat(pipeline): MOV review delivery packages from selected shots
A formal review is shared as a flat, dated folder of the preview MOVs —
{deliveryRoot}/PRODUCER_REVIEWS/20260828_Producer_review, then _02, _03
for further reviews the same day. It reuses the delivery package's
machinery end to end: DeliveryPackage/DeliveryItem, the DELIVERY_BUILD
job, the same claim path and the same hardlink-or-copy builder, with
DeliveryPackage.kind telling the two apart.

Three things separate a review from a delivery:

A review never advances an Export. Reviews happen throughout a shot's
life, long before QC passes it, and the same version is routinely shown
twice — so nothing is marked PACKAGED, and markDelivered() refuses a
review package outright (the button is hidden too).

Selection is by shot, not by export. Shot Status → Bulk Actions → Build
MOV Review Package; the server resolves each shot to its newest export
that actually has a deliveryMovPath, which is not always its newest
export — a shot whose latest version is still rendering belongs in the
review as the version that can be watched. Shots with no MOV at all are
listed in the confirm dialog with the reason rather than silently
shrinking the folder.

The folder is exactly the MOVs. No CSV, no manifest.json written beside
them; the manifest still returns to the server, so the record of what was
shared is kept without cluttering the share. Names follow the same
client-naming rule as a delivery, so a shot reads identically in a review
folder and in the delivery that follows it.

Verification (DeliveryStage.BuildReviewItem) is proportional as it is for
EXRs, adapted to a single file with no validation-time digest: a hard
link is the source and needs nothing, a copy is checked against its
source by length always and by full hash under "always-hash". A missing
MOV fails the item here — in an EXR delivery the MOV is a viewing copy
beside the real deliverable, but in a review it is the deliverable, and a
review folder quietly one shot short is worse than one that says so.

Worker bumped to 1.6.0 for the new manifest field (DeliveryManifest.Kind).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:58:21 +02:00

44 lines
1.4 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { requirePipelineAdmin } from "@/lib/render-pipeline/session-auth";
import { PipelineError } from "@/lib/render-pipeline/errors";
import {
createMovReviewPackage,
previewMovReviewPackage,
} from "@/lib/render-pipeline/reviews";
/**
* Server actions behind "Build MOV Review Package" on the shot-status page
* (RenderPipeline2 §11.6). The heavy lifting lives in lib/render-pipeline;
* these only carry the session and turn service errors into messages a toast
* can show.
*/
function message(err: unknown): string {
if (err instanceof PipelineError) return err.message;
return err instanceof Error ? err.message : "Unknown error";
}
/** What the confirm dialog shows: the folder, the resolved versions, the gaps. */
export async function previewMovReview(shotIds: string[]) {
try {
await requirePipelineAdmin();
const preview = await previewMovReviewPackage(shotIds);
return { ok: true as const, ...preview };
} catch (err) {
return { ok: false as const, error: message(err) };
}
}
export async function buildMovReview(shotIds: string[]) {
try {
const user = await requirePipelineAdmin();
const result = await createMovReviewPackage({ shotIds }, user.id);
revalidatePath("/pipeline/deliveries");
return { ok: true as const, ...result };
} catch (err) {
return { ok: false as const, error: message(err) };
}
}