70cf45ead4
Deploy / deploy (push) Successful in 7m2s
Blender work is not a shot submission: no Export, no version, no validation, QC or delivery. It is "Ctrl+F12, but on the farm", so it is modelled as what it is — a queue entry and nothing more. That turned out to need very little. Claim, progress, fail and complete-render already tolerated a job with no Export behind them, so a Blender job reuses the whole lifecycle; RenderJob gains projectId, label and submittedBy so a job with no shot code still has something to show. Capability gating is new and was already missing: the claim now filters job types by the machine's reported tools. Without it a workstation without Blender would claim Blender work and fail it repeatedly, burning the attempts while a machine that could have rendered it sat idle. The worker asks for Blender jobs unconditionally and the server refuses them — trusting the worker to self-select would have left the same hole. The runner treats a clean exit with no saved frames as a failure: Blender exits 0 when a frame range is empty or an output path is unwritable, and a job that silently rendered nothing is the worst outcome available. Standalone jobs auto-requeue on a transient failure like any other work; delivery builds are deliberately excluded, since a half-written package wants a human first. Adds the Blender add-on (BlenderPanel/) and its docs. The add-on refuses to submit an unsaved file — the farm opens the .blend from disk, so unsaved changes would render the previous state without warning. Its payload is pinned by a contract test, since add-on and server only ever meet over JSON. Worker 1.5.0. Adds migration 20260815000000_blender_render_jobs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
37 lines
1.4 KiB
TypeScript
37 lines
1.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { isExtAuthorized } from "@/lib/render-pipeline/ext-auth";
|
|
import { toErrorResponse } from "@/lib/render-pipeline/errors";
|
|
import { createBlenderJob, createBlenderJobSchema } from "@/lib/render-pipeline/blender";
|
|
|
|
// ── POST /api/ext/render/blender ─────────────────────────────────────────────
|
|
//
|
|
// Queue a standalone Blender render from the Blender add-on. No Export, no shot
|
|
// version, no validation or QC — the .blend is already set up to render, and
|
|
// this puts it on the farm instead of on the artist's machine.
|
|
|
|
export async function POST(req: NextRequest) {
|
|
if (!isExtAuthorized(req)) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
let raw: unknown;
|
|
try {
|
|
raw = await req.json();
|
|
} catch {
|
|
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
|
|
}
|
|
|
|
const parsed = createBlenderJobSchema.safeParse(raw);
|
|
if (!parsed.success) {
|
|
return NextResponse.json({ error: "Invalid Blender job", details: parsed.error.issues }, { status: 422 });
|
|
}
|
|
|
|
try {
|
|
const result = await createBlenderJob(parsed.data);
|
|
return NextResponse.json(result, { status: 201 });
|
|
} catch (err) {
|
|
const { body, status } = toErrorResponse(err);
|
|
return NextResponse.json(body, { status });
|
|
}
|
|
}
|