Files
twotalesanimation cc89415a29 feat(pipeline): render queue, worker service and automated preview generation
One "Queue Export" click now renders the EXR sequence, then rebuilds the shot
headlessly with the studio slate/overlay template to produce the delivery MOV
and review MP4. Implements RenderPipeline2 phases 1-2 plus the preview stage.

Server:
- New models Export, RenderJob, ExportEvent, Machine, WorkerHeartbeat, plus
  Project.deliveryConfig and per-submission slate fields (Export.vfxScope,
  Export.submissionNote, inherited from the shot's previous export).
  Both migrations are purely additive; no existing column is touched.
- lib/render-pipeline: server-enforced state machine, transactional version
  increment with supersede, atomic FOR UPDATE SKIP LOCKED claim gated by
  machine availability windows, and a lease reaper run from instrumentation.ts.
- /api/ext/* endpoints for the panel and workers; session-auth mirrors under
  /api/render and /api/machines for the web UI.
- Pipeline pages: render queue, export detail, machine monitoring, plus an
  Exports tab on shot detail.

RenderWorker (.NET 8 Windows service, new):
- Registration, heartbeat as cancel channel, claim loop, aerender runner with
  progress parsing and stall watchdog, crash recovery and disk-spooled
  reporting that survives server downtime.
- Preview stage: headless AE assembles the preview comp into a throwaway AEP
  with both output modules queued, then a single aerender pass renders them.
  Preview jobs are not claimed while an interactive AE session is open, so an
  artist's project is never taken over.

AE panel: Queue Export with live status polling, urgent flag, retry, and the
VFX Scope / Submission Note fields. Every existing panel action is unchanged.

Preview chaining ships disabled behind SystemConfig preview.enabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 15:46:53 +02:00

179 lines
5.9 KiB
SQL

-- CreateEnum
CREATE TYPE "ExportStatus" AS ENUM ('QUEUED', 'RENDERING', 'RENDER_FAILED', 'VALIDATING', 'VALIDATION_FAILED', 'GENERATING_PREVIEW', 'PREVIEW_FAILED', 'READY_FOR_QC', 'QC_FAILED', 'READY_FOR_DELIVERY', 'PACKAGED', 'DELIVERED', 'SUPERSEDED', 'ARCHIVED', 'CANCELLED');
-- CreateEnum
CREATE TYPE "RenderJobStatus" AS ENUM ('QUEUED', 'CLAIMED', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED');
-- CreateEnum
CREATE TYPE "RenderJobType" AS ENUM ('AE_RENDER', 'PREVIEW_ONLY', 'DELIVERY_BUILD');
-- CreateEnum
CREATE TYPE "ValidationStatus" AS ENUM ('PASS', 'FAIL', 'WARN', 'SKIPPED');
-- CreateEnum
CREATE TYPE "QCResult" AS ENUM ('PASS', 'FAIL');
-- CreateEnum
CREATE TYPE "MachineStatus" AS ENUM ('ONLINE', 'OFFLINE', 'DISABLED');
-- CreateEnum
CREATE TYPE "DeliveryStatus" AS ENUM ('DRAFT', 'QUEUED', 'BUILDING', 'READY', 'DELIVERED', 'FAILED', 'CANCELLED');
-- AlterTable
ALTER TABLE "projects" ADD COLUMN "deliveryConfig" JSONB;
-- CreateTable
CREATE TABLE "exports" (
"id" TEXT NOT NULL,
"shotId" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"taskId" TEXT,
"versionId" TEXT,
"versionNumber" INTEGER NOT NULL,
"versionString" TEXT NOT NULL,
"status" "ExportStatus" NOT NULL DEFAULT 'QUEUED',
"statusChangedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"aepPath" TEXT NOT NULL,
"compName" TEXT NOT NULL,
"rendererType" TEXT NOT NULL,
"outputDir" TEXT NOT NULL,
"outputPattern" TEXT NOT NULL,
"frameStart" INTEGER NOT NULL,
"frameEnd" INTEGER NOT NULL,
"fps" DOUBLE PRECISION NOT NULL,
"width" INTEGER NOT NULL,
"height" INTEGER NOT NULL,
"colorspace" TEXT,
"deliveryMovPath" TEXT,
"previewMovKey" TEXT,
"thumbnailKey" TEXT,
"metadataKey" TEXT,
"exrFileCount" INTEGER,
"exrTotalBytes" BIGINT,
"checksum" TEXT,
"submittedById" TEXT,
"submittedByName" TEXT,
"supersededById" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "exports_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "render_jobs" (
"id" TEXT NOT NULL,
"type" "RenderJobType" NOT NULL DEFAULT 'AE_RENDER',
"exportId" TEXT,
"deliveryId" TEXT,
"attempt" INTEGER NOT NULL DEFAULT 1,
"maxAttempts" INTEGER NOT NULL DEFAULT 3,
"status" "RenderJobStatus" NOT NULL DEFAULT 'QUEUED',
"priority" INTEGER NOT NULL DEFAULT 50,
"manifest" JSONB NOT NULL,
"machineId" TEXT,
"claimedAt" TIMESTAMP(3),
"leaseExpiresAt" TIMESTAMP(3),
"startedAt" TIMESTAMP(3),
"finishedAt" TIMESTAMP(3),
"progress" DOUBLE PRECISION NOT NULL DEFAULT 0,
"currentFrame" INTEGER,
"totalFrames" INTEGER,
"etaSeconds" INTEGER,
"exitCode" INTEGER,
"errorMessage" TEXT,
"logTail" TEXT,
"logFileKey" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "render_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "export_events" (
"id" TEXT NOT NULL,
"exportId" TEXT NOT NULL,
"fromStatus" TEXT,
"toStatus" TEXT NOT NULL,
"actorType" TEXT NOT NULL,
"actorId" TEXT,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "export_events_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "machines" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"hostname" TEXT NOT NULL,
"status" "MachineStatus" NOT NULL DEFAULT 'OFFLINE',
"enabled" BOOLEAN NOT NULL DEFAULT true,
"lastSeenAt" TIMESTAMP(3),
"workerVersion" TEXT,
"aeVersion" TEXT,
"capabilities" JSONB,
"availability" JSONB,
"renderNowUntil" TIMESTAMP(3),
"apiKeyHash" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "machines_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "worker_heartbeats" (
"id" TEXT NOT NULL,
"machineId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"cpuPercent" DOUBLE PRECISION,
"memPercent" DOUBLE PRECISION,
"diskFreeGb" DOUBLE PRECISION,
"currentJobId" TEXT,
CONSTRAINT "worker_heartbeats_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "exports_status_idx" ON "exports"("status");
-- CreateIndex
CREATE INDEX "exports_projectId_status_idx" ON "exports"("projectId", "status");
-- CreateIndex
CREATE UNIQUE INDEX "exports_shotId_versionNumber_key" ON "exports"("shotId", "versionNumber");
-- CreateIndex
CREATE INDEX "render_jobs_status_priority_createdAt_idx" ON "render_jobs"("status", "priority", "createdAt");
-- CreateIndex
CREATE INDEX "render_jobs_machineId_status_idx" ON "render_jobs"("machineId", "status");
-- CreateIndex
CREATE INDEX "export_events_exportId_createdAt_idx" ON "export_events"("exportId", "createdAt");
-- CreateIndex
CREATE UNIQUE INDEX "machines_name_key" ON "machines"("name");
-- CreateIndex
CREATE INDEX "worker_heartbeats_machineId_createdAt_idx" ON "worker_heartbeats"("machineId", "createdAt");
-- AddForeignKey
ALTER TABLE "exports" ADD CONSTRAINT "exports_shotId_fkey" FOREIGN KEY ("shotId") REFERENCES "shots"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "render_jobs" ADD CONSTRAINT "render_jobs_exportId_fkey" FOREIGN KEY ("exportId") REFERENCES "exports"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "render_jobs" ADD CONSTRAINT "render_jobs_machineId_fkey" FOREIGN KEY ("machineId") REFERENCES "machines"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "export_events" ADD CONSTRAINT "export_events_exportId_fkey" FOREIGN KEY ("exportId") REFERENCES "exports"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "worker_heartbeats" ADD CONSTRAINT "worker_heartbeats_machineId_fkey" FOREIGN KEY ("machineId") REFERENCES "machines"("id") ON DELETE RESTRICT ON UPDATE CASCADE;