diff --git a/RenderWorker/VFXReviewWorker/Dtos.cs b/RenderWorker/VFXReviewWorker/Dtos.cs index 108a18d..8143cc7 100644 --- a/RenderWorker/VFXReviewWorker/Dtos.cs +++ b/RenderWorker/VFXReviewWorker/Dtos.cs @@ -157,19 +157,6 @@ public sealed class PreviewManifest public string? SlateSubmissionProp { get; set; } public SlateInfo Slate { get; set; } = new(); - /// Skip picture-lock trimming and the reference render entirely. - public bool NoPictureLock { get; set; } - /// "{footageRoot}/{showId}/_PICLOCKS" — empty when not configured. - public string? PiclockDir { get; set; } - /// e.g. "UNG_106" — the picture-lock file glob prefix. - public string? PiclockGlobPrefix { get; set; } - public string? SeqTimecodeStart { get; set; } - public string? SeqTimecodeEnd { get; set; } - /// Studio-standard head/tail handle count, applied symmetrically. - public int HandleFrames { get; set; } - /// Full-duration (handles included), ungraded picture-lock reference render. - public string? PiclockRefOutput { get; set; } - public int TotalFrames => FrameEnd - FrameStart + 1; } @@ -199,10 +186,6 @@ public sealed class PreviewBuildResult public int? FrameCount { get; set; } public int? Width { get; set; } public int? Height { get; set; } - /// Whether the picture-lock reference comp was found, positioned and queued. - public bool PiclockRefBuilt { get; set; } - /// Why the picture-lock reference/trim was skipped, when it was (non-fatal). - public string? PiclockSkippedReason { get; set; } } /// The Render Manifest (§6.2) — the fields the render and validation stages need; the rest is preserved raw. diff --git a/RenderWorker/VFXReviewWorker/JobExecutor.cs b/RenderWorker/VFXReviewWorker/JobExecutor.cs index 01f270f..b5a8d67 100644 --- a/RenderWorker/VFXReviewWorker/JobExecutor.cs +++ b/RenderWorker/VFXReviewWorker/JobExecutor.cs @@ -438,19 +438,6 @@ public sealed class JobExecutor job.Id, machineId, "thumbnail", result.ThumbnailPath, "image/jpeg", shutdown); } - // Diagnostic sidecar, not a deliverable — an upload failure here must - // never hold back the mov/mp4/thumbnail that already succeeded. - string? piclockRefKey = null; - if (result.PiclockRefPath is not null) - { - piclockRefKey = await _api.UploadArtifactAsync( - job.Id, machineId, "piclockRef", result.PiclockRefPath, "video/mp4", shutdown); - if (piclockRefKey is null) - { - _log.LogWarning("Picture-lock reference upload failed for job {JobId} (non-fatal)", job.Id); - } - } - _spooler.Enqueue("finalize", job.Id, new Dictionary { ["machineId"] = machineId, @@ -459,9 +446,7 @@ public sealed class JobExecutor ["deliveryMovPath"] = manifest.MovOutput, ["previewMovKey"] = previewKey, ["thumbnailKey"] = thumbKey, - ["piclockRefKey"] = piclockRefKey, }, - ["piclockSkippedReason"] = result.Build?.PiclockSkippedReason, ["renderStats"] = new Dictionary { ["previewSeconds"] = (int)result.Seconds, diff --git a/RenderWorker/VFXReviewWorker/PreviewStage.cs b/RenderWorker/VFXReviewWorker/PreviewStage.cs index 169a221..9eec8f3 100644 --- a/RenderWorker/VFXReviewWorker/PreviewStage.cs +++ b/RenderWorker/VFXReviewWorker/PreviewStage.cs @@ -14,8 +14,7 @@ public sealed record PreviewStageResult( string? ThumbnailPath, string? TempAep, PreviewBuildResult? Build, - double Seconds, - string? PiclockRefPath = null); + double Seconds); /// /// Stage 3 — preview generation (RenderPipeline2 §9), the second half of a @@ -60,8 +59,6 @@ public sealed class PreviewStage var movLocal = mapper.Map(manifest.MovOutput); var mp4Local = mapper.Map(manifest.Mp4Output); - var piclockDirLocal = string.IsNullOrWhiteSpace(manifest.PiclockDir) ? null : mapper.Map(manifest.PiclockDir); - var piclockRefLocal = string.IsNullOrWhiteSpace(manifest.PiclockRefOutput) ? null : mapper.Map(manifest.PiclockRefOutput); // Everything the script needs, in machine-local paths. var context = new @@ -86,13 +83,6 @@ public sealed class PreviewStage slateScopeProp = manifest.SlateScopeProp, slateSubmissionProp = manifest.SlateSubmissionProp, slate = manifest.Slate, - noPictureLock = manifest.NoPictureLock, - piclockDir = piclockDirLocal, - piclockGlobPrefix = manifest.PiclockGlobPrefix, - seqTimecodeStart = manifest.SeqTimecodeStart, - seqTimecodeEnd = manifest.SeqTimecodeEnd, - handleFrames = manifest.HandleFrames, - piclockRefOutput = piclockRefLocal, }; await File.WriteAllTextAsync(contextPath, JsonSerializer.Serialize(context, WorkerOptions.JsonOpts), shutdown); @@ -191,29 +181,12 @@ public sealed class PreviewStage var thumbnail = await TryMakeThumbnailAsync(job.Id, mp4Local, shutdown); - // Non-fatal by design: the picture-lock reference is a diagnostic aid, - // not part of the deliverable, so a missing file here must never hold - // back the mov/mp4 that already rendered successfully. - string? piclockRefResult = null; - if (build.PiclockRefBuilt) - { - if (!string.IsNullOrWhiteSpace(piclockRefLocal) && File.Exists(piclockRefLocal)) - { - piclockRefResult = piclockRefLocal; - } - else - { - _log.LogWarning("Picture-lock reference was queued but is missing after render: {Path}", piclockRefLocal); - } - } - sw.Stop(); - _log.LogInformation("Preview complete for job {JobId} in {Sec:F0}s (mov + mp4{Thumb}{Piclock})", - job.Id, sw.Elapsed.TotalSeconds, thumbnail is null ? "" : " + thumbnail", - piclockRefResult is null ? "" : " + piclock ref"); + _log.LogInformation("Preview complete for job {JobId} in {Sec:F0}s (mov + mp4{Thumb})", + job.Id, sw.Elapsed.TotalSeconds, thumbnail is null ? "" : " + thumbnail"); return new PreviewStageResult(true, true, null, movLocal, mp4Local, thumbnail, - build.TempAep, build, sw.Elapsed.TotalSeconds, piclockRefResult); + build.TempAep, build, sw.Elapsed.TotalSeconds); } private async Task RunAfterFxAsync( diff --git a/RenderWorker/scripts/vfxr_build_preview.jsx b/RenderWorker/scripts/vfxr_build_preview.jsx index a6a32ff..81c8b13 100644 --- a/RenderWorker/scripts/vfxr_build_preview.jsx +++ b/RenderWorker/scripts/vfxr_build_preview.jsx @@ -30,9 +30,7 @@ queued: [], frameCount: null, width: null, - height: null, - piclockRefBuilt: false, - piclockSkippedReason: null + height: null }; var resultPath = null; @@ -158,67 +156,6 @@ return null; } - // Mirrors the panel's parseTimecodeToSeconds exactly. - function parseTimecodeToSeconds(tc, fps) { - var parts = String(tc).split(/[:;]/); - if (parts.length < 4) { - return 0; - } - return parseInt(parts[0], 10) * 3600 + parseInt(parts[1], 10) * 60 + - parseInt(parts[2], 10) + parseInt(parts[3], 10) / (fps || 24); - } - - // Locates the picture-lock reference footage the same way the panel's - // pullPictureLock does: the _PICLOCKS project folder first, then a disk - // glob under ctx.piclockDir. Returns the FootageItem, or null (with a - // warn()) if it cannot be found — never fatal to the preview build. - function findPiclockFootage(ctx) { - var piclocksProjectFolder = findFolder("_PICLOCKS"); - var i; - var item; - var piclocksDir; - var files; - var importOptions; - - if (piclocksProjectFolder) { - for (i = 1; i <= app.project.numItems; i += 1) { - item = app.project.item(i); - if (item instanceof FootageItem && - item.parentFolder === piclocksProjectFolder && - item.name.indexOf(ctx.piclockGlobPrefix + "_") === 0) { - return item; - } - } - } - - if (!ctx.piclockDir) { - warn("Picture lock: no footage root configured for this project — skipping reference render"); - return null; - } - piclocksDir = new Folder(ctx.piclockDir); - if (!piclocksDir.exists) { - warn("Picture lock: folder not found: " + piclocksDir.fsName); - return null; - } - files = piclocksDir.getFiles(ctx.piclockGlobPrefix + "_*"); - if (!files || files.length < 1) { - warn("Picture lock: no file matching " + ctx.piclockGlobPrefix + "_* found in " + piclocksDir.fsName); - return null; - } - for (i = 0; i < files.length; i += 1) { - if (files[i] instanceof File) { - try { - importOptions = new ImportOptions(files[i]); - return app.project.importFile(importOptions); - } catch (importError) { - warn("Picture lock: import failed: " + importError.message); - return null; - } - } - } - return null; - } - // Mirrors the panel's addOCIOToLayer: effect added but disabled, so the // artist/farm chain matches exactly. function addOCIOToLayer(layer) { @@ -484,56 +421,22 @@ result.height = height; result.frameCount = Math.round(duration * fps); - // Trim decision (§9, picture lock): the render's frame range is - // editorial ± a fixed symmetric handle count by studio convention (the - // same constant pullPictureLock hardcodes), so no picture-lock file is - // needed for this — only ctx.handleFrames. Applied at the footage/shot - // comp level (below) rather than by juggling layer timing inside - // previewComp — previewComp is duplicated from an artist-authored - // template whose slate/shot timing relationship this script does not - // own, so it is left completely untouched, exactly as it always - // rendered correctly before this feature existed. - var handleSeconds = (ctx.handleFrames || 0) / fps; - var contentDuration = duration; - var headSkipSeconds = 0; - var trimmedToPictureLock = false; - if (ctx.noPictureLock) { - result.piclockSkippedReason = "No Picture Lock was set for this export"; - } else if (handleSeconds <= 0) { - result.piclockSkippedReason = "No handle frame count configured"; - } else if (2 * handleSeconds >= duration) { - warn("Picture lock: handle count (" + ctx.handleFrames + "fr) covers the whole render (" + - result.frameCount + "fr) — trim skipped"); - result.piclockSkippedReason = "handle count exceeds the render duration"; - } else { - headSkipSeconds = handleSeconds; - contentDuration = duration - (2 * handleSeconds); - trimmedToPictureLock = true; - } - var contentStartFrame = (ctx.frameStart !== null && ctx.frameStart !== undefined) - ? ctx.frameStart + Math.round(headSkipSeconds * fps) - : null; - // 3. Footage precomp (mirrors buildShotFromCode) var precompFolder = findFolder("_PRECOMPS"); var footageComp = app.project.items.addComp( - ctx.shotCode + "_FOOTAGE", width, height, 1, contentDuration, fps); + ctx.shotCode + "_FOOTAGE", width, height, 1, duration, fps); if (precompFolder) { footageComp.parentFolder = precompFolder; } - if (contentStartFrame !== null) { - setCompStartFrame(footageComp, contentStartFrame); + if (ctx.frameStart !== null && ctx.frameStart !== undefined) { + setCompStartFrame(footageComp, ctx.frameStart); } - var footageLayer = footageComp.layers.add(footage); - if (headSkipSeconds > 0) { - footageLayer.startTime -= headSkipSeconds; - } - addOCIOToLayer(footageLayer); + addOCIOToLayer(footageComp.layers.add(footage)); // 4. Shot comp: footage + show LUT + overlay - var shotComp = app.project.items.addComp(ctx.shotCode, width, height, 1, contentDuration, fps); - if (contentStartFrame !== null) { - setCompStartFrame(shotComp, contentStartFrame); + var shotComp = app.project.items.addComp(ctx.shotCode, width, height, 1, duration, fps); + if (ctx.frameStart !== null && ctx.frameStart !== undefined) { + setCompStartFrame(shotComp, ctx.frameStart); } shotComp.layers.add(footageComp); @@ -607,10 +510,6 @@ warn("NETFLIX_SLATE layer not found in template — slate fields not filled"); } - // previewComp sizes itself to whatever shotComp.duration actually is — - // unchanged from before this feature existed. shotComp is already the - // trimmed (or full, if skipped above) duration, so this needs no - // special-casing here. var shotLayer = previewComp.layer("SHOT"); var targetOutPoint = shotLayer.inPoint + shotComp.duration; if (targetOutPoint > previewComp.duration) { @@ -620,85 +519,13 @@ previewComp.duration = targetOutPoint; result.previewComp = previewComp.name; - result.trimmedToPictureLock = trimmedToPictureLock; + // 6. Queue both outputs against the same preview comp. aerender will + // render the whole queue in one launch. app.project.renderQueue.showWindow(false); while (app.project.renderQueue.numItems > 0) { app.project.renderQueue.item(1).remove(); } - - // 5b. Picture-lock reference comp: the raw plate only — no OCIO, no - // show LUT, no overlay — spanning the FULL padded duration (handles - // included), so a supervisor can check back against it if a delivered - // edit is ever suspected to be off. Independent of the trim above: - // this one needs the actual picture-lock file and sequence timecodes, - // and is skipped gracefully (never fatal) if either is missing. - if (ctx.noPictureLock) { - if (!result.piclockSkippedReason) { - result.piclockSkippedReason = "No Picture Lock was set for this export"; - } - } else if (!ctx.seqTimecodeStart || !ctx.seqTimecodeEnd) { - warn("Picture lock: no sequence timecodes for this shot — reference render skipped"); - result.piclockSkippedReason = "no sequence timecodes for this shot"; - } else { - // A diagnostic aid, not a deliverable: any failure building or - // queuing it must warn and move on, never take down the mov/mp4 - // that already succeeded (queueOutput() throws on template errors). - try { - var piclockFootage = findPiclockFootage(ctx); - if (piclockFootage) { - var piclockStartTC = 0; - try { - if (piclockFootage.mainSource && typeof piclockFootage.mainSource.startTimecode === "number") { - piclockStartTC = piclockFootage.mainSource.startTimecode; - } - } catch (tcReadError) { - } - - var tcIn = parseTimecodeToSeconds(ctx.seqTimecodeStart, fps); - var tcOut = parseTimecodeToSeconds(ctx.seqTimecodeEnd, fps); - var refIn = tcIn - piclockStartTC - handleSeconds; - var refOut = tcOut - piclockStartTC + handleSeconds; - if (refIn < 0) { - refIn = 0; - } - if (refOut > piclockFootage.duration) { - refOut = piclockFootage.duration; - } - - var piclockRefComp = app.project.items.addComp( - ctx.shotCode + "_PICLOCK_REF", width, height, 1, refOut - refIn, fps); - if (previewsFolder) { - piclockRefComp.parentFolder = previewsFolder; - } - var piclockLayer = piclockRefComp.layers.add(piclockFootage); - var piclockScale = Math.max( - piclockRefComp.width / piclockFootage.width, - piclockRefComp.height / piclockFootage.height - ) * 100; - piclockLayer.property("Scale").setValue([piclockScale, piclockScale]); - piclockLayer.startTime = -refIn; - piclockLayer.inPoint = 0; - piclockLayer.outPoint = refOut - refIn; - - if (queueOutput(piclockRefComp, ctx.mp4Template, ctx.piclockRefOutput, "piclockRef")) { - result.piclockRefBuilt = true; - } else { - result.piclockSkippedReason = "no mp4 template or output path configured"; - } - } else if (!result.piclockSkippedReason) { - result.piclockSkippedReason = "picture-lock file not found"; - } - } catch (piclockError) { - warn("Picture lock: reference render skipped — " + - (piclockError && piclockError.toString ? piclockError.toString() : String(piclockError))); - result.piclockSkippedReason = "error building the reference render"; - result.piclockRefBuilt = false; - } - } - - // 6. Queue both outputs against the same preview comp. aerender will - // render the whole queue in one launch. queueOutput(previewComp, ctx.movTemplate, ctx.movOutput, "mov"); queueOutput(previewComp, ctx.mp4Template, ctx.mp4Output, "mp4"); diff --git a/VFXReviewConnector.jsx b/VFXReviewConnector.jsx index f2c856f..8dea799 100644 --- a/VFXReviewConnector.jsx +++ b/VFXReviewConnector.jsx @@ -3962,9 +3962,6 @@ var PANEL_VERSION = "2.5.0"; if (uiState.skipPreviewCheckbox && uiState.skipPreviewCheckbox.value) { body.skipPreview = true; } - if (uiState.noPictureLockCheckbox && uiState.noPictureLockCheckbox.value) { - body.noPictureLock = true; - } if (CONFIG.artistEmail) { body.submittedByEmail = CONFIG.artistEmail; @@ -4534,13 +4531,6 @@ var PANEL_VERSION = "2.5.0"; "For deliverables framed differently from the review — unscaled or\n" + "un-punched-in EXRs — where a preview rebuilt from these frames\n" + "would be the wrong picture."; - uiState.noPictureLockCheckbox = pipelineRow1.add("checkbox", undefined, "No Picture Lock"); - uiState.noPictureLockCheckbox.helpTip = - "Skip picture-lock trimming and the picture-lock reference render\n" + - "for this export.\n\n" + - "Check if picture lock is missing or wrong for this shot — the\n" + - "preview build will fall back to the full rendered range instead\n" + - "of trimming against bad data."; var slateNoteGroup = pipelinePanel.add("group"); slateNoteGroup.orientation = "row"; diff --git a/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx b/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx index a42a363..ba54937 100644 --- a/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx +++ b/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import Link from "next/link"; import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { ArrowLeft, RotateCcw, XCircle, ShieldAlert, Clapperboard } from "lucide-react"; +import { ArrowLeft, RotateCcw, XCircle, ShieldAlert } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useToast } from "@/components/ui/use-toast"; import { cn } from "@/lib/utils"; @@ -36,9 +36,6 @@ interface ExportDetail { vfxScope: string | null; submissionNote: string | null; submittedByName: string | null; - noPictureLock: boolean; - /** Full-duration, ungraded picture-lock reference MP4 — a diagnostic sidecar, not a Version. */ - piclockRefKey: string | null; shot: { id: string; shotCode: string; @@ -180,7 +177,6 @@ export function ExportDetailClient({ exportId }: { exportId: string }) { ["Colorspace", e.colorspace ?? "—"], ["EXR files", e.exrFileCount != null ? String(e.exrFileCount) : "—"], ["Submitted by", e.submittedByName ?? "—"], - ["Picture lock", e.noPictureLock ? "Skipped (No Picture Lock)" : "Trimmed to picture lock"], ]; return ( @@ -207,13 +203,6 @@ export function ExportDetailClient({ exportId }: { exportId: string }) { - {e.piclockRefKey && ( - - - Picture Lock Reference - - - )} {FAILED_STATUSES.includes(e.status) && ( action("retry")}> Retry diff --git a/app/api/ext/exports/route.ts b/app/api/ext/exports/route.ts index f7144a7..fd394ad 100644 --- a/app/api/ext/exports/route.ts +++ b/app/api/ext/exports/route.ts @@ -5,7 +5,7 @@ import { createExport } from "@/lib/render-pipeline/exports"; // ── POST /api/ext/exports (E1 — Queue Export) ──────────────────────────────── // -// Body: { manifest: RenderManifest, submittedByEmail?, priority?, force?, skipPreview?, noPictureLock? } +// Body: { manifest: RenderManifest, submittedByEmail?, priority?, force?, skipPreview? } // The server decides the new version number, updates Shot.shotVersion/exrOutput, // supersedes older non-terminal exports, and creates Export(QUEUED) + RenderJob. @@ -21,7 +21,6 @@ export async function POST(req: NextRequest) { vfxScope?: string | null; submissionNote?: string | null; skipPreview?: boolean; - noPictureLock?: boolean; }; try { body = await req.json(); @@ -41,7 +40,6 @@ export async function POST(req: NextRequest) { vfxScope: body.vfxScope, submissionNote: body.submissionNote, skipPreview: body.skipPreview === true, - noPictureLock: body.noPictureLock === true, }); return NextResponse.json(result, { status: 201 }); } catch (err) { diff --git a/app/api/ext/render/jobs/[jobId]/artifact-presign/route.ts b/app/api/ext/render/jobs/[jobId]/artifact-presign/route.ts index 8ca73a9..4fc2c0e 100644 --- a/app/api/ext/render/jobs/[jobId]/artifact-presign/route.ts +++ b/app/api/ext/render/jobs/[jobId]/artifact-presign/route.ts @@ -16,7 +16,6 @@ const KIND_FOLDERS: Record = { thumbnail: "image", metadata: "renders", log: "renders", - piclockRef: "videos", }; export async function POST( diff --git a/lib/render-pipeline/config.ts b/lib/render-pipeline/config.ts index eaa112e..5cbadf8 100644 --- a/lib/render-pipeline/config.ts +++ b/lib/render-pipeline/config.ts @@ -26,11 +26,7 @@ export const PIPELINE_CONFIG_DEFAULTS = { "validation.sampleEvery": 25, /** Per-check severity overrides, e.g. {"exr_type":"warn"} (§17.3). */ "validation.overrides": "{}", - /** - * Frames of head/tail handle the studio pads every render with. Used for the - * API timecode fallback (§8.1a) and, symmetrically, to trim the preview - * build's client-facing comp back to the picture-lock edit (§9). - */ + /** Frames of head handle the plates carry; only used for the API timecode fallback (§8.1a). */ "validation.handleFrames": 8, /** * Studio-wide delivery defaults (§11.2). A project's own `deliveryConfig` @@ -47,11 +43,6 @@ export const PIPELINE_CONFIG_DEFAULTS = { "preview.lutComp": "_SHOW LUT", "preview.movTemplate": "4444 Tri", "preview.mp4Template": "REVIEW_PREVIEW", - // Bare footage root the preview build reads picture-lock plates from, at - // `{footageRoot}/{showId}/_PICLOCKS` — same convention as the panel's own - // "Footage Root" setting (VFXReviewConnector.md). Per-project override via - // pipelineConfig.footageRoot for a show on different storage. - "preview.footageRoot": "V:/_FOOTAGE", // Essential Property names on the NETFLIX_SLATE layer for the per-submission // fields. If a name is wrong the build logs the template's actual property // names as a warning, so it is a config fix rather than a code change. diff --git a/lib/render-pipeline/exports.ts b/lib/render-pipeline/exports.ts index cb5ed80..10f20ff 100644 --- a/lib/render-pipeline/exports.ts +++ b/lib/render-pipeline/exports.ts @@ -70,11 +70,6 @@ export interface CreateExportInput { * frames would produce the wrong MOV. */ skipPreview?: boolean; - /** - * Skip picture-lock trimming and the picture-lock reference render for this - * export — set when picture lock is missing or wrong for the shot. - */ - noPictureLock?: boolean; } /** @@ -210,7 +205,6 @@ export async function createExport(input: CreateExportInput) { vfxScope, submissionNote, skipPreview: input.skipPreview === true, - noPictureLock: input.noPictureLock === true, submittedById: submittedBy?.id ?? null, submittedByName: submittedBy?.name ?? input.submittedByEmail ?? null, }, @@ -253,8 +247,7 @@ export async function createExport(input: CreateExportInput) { actorId: submittedBy?.id ?? null, note: `Queue Export ${versionString}${input.force ? " (force)" : ""}` + - `${input.skipPreview ? " (skip preview)" : ""}` + - `${input.noPictureLock ? " (no picture lock)" : ""} — ` + + `${input.skipPreview ? " (skip preview)" : ""} — ` + `${provenance.ok ? "" : "WARNING: "}${provenance.note}`, }, }); diff --git a/lib/render-pipeline/preview.ts b/lib/render-pipeline/preview.ts index 422b92f..471764c 100644 --- a/lib/render-pipeline/preview.ts +++ b/lib/render-pipeline/preview.ts @@ -32,18 +32,6 @@ export interface PreviewManifest { mp4Output: string; slateScopeProp: string; slateSubmissionProp: string; - /** Skip picture-lock trimming and the reference render entirely (§9, picture lock). */ - noPictureLock: boolean; - /** `{footageRoot}/{showId}/_PICLOCKS`, mirroring the panel's getShowFootageRoot(). */ - piclockDir: string; - /** e.g. "UNG_106" — mirrors the panel's getEpisodePrefix(); the picture-lock glob prefix. */ - piclockGlobPrefix: string; - seqTimecodeStart: string | null; - seqTimecodeEnd: string | null; - /** Studio-standard head/tail handle count, applied symmetrically. */ - handleFrames: number; - /** Full-duration (handles included), ungraded picture-lock reference render. */ - piclockRefOutput: string; slate: { versionName: string; date: string; @@ -70,7 +58,6 @@ type ExportForPreview = { fps: number; vfxScope: string | null; submissionNote: string | null; - noPictureLock: boolean; }; /** @@ -91,27 +78,18 @@ export async function buildPreviewManifest(exportRow: ExportForPreview): Promise description: true, notes: true, slateDescription: true, - seqTimecodeStart: true, - seqTimecodeEnd: true, project: { select: { name: true, code: true, showId: true, pipelineConfig: true } }, }, }); if (!shot) throw new PipelineError(404, "Shot not found for export"); - const [cfg, handleFrames] = await Promise.all([getPreviewConfig(), getConfigNumber("validation.handleFrames")]); + const cfg = await getPreviewConfig(); // Per-production templates: each show has its own slate/overlay AEP, and // above all its own show LUT. Falling back to another show's would render a // preview that is quietly the wrong colour. const templates = await resolveProjectTemplates(shot.project); const base = `${shot.shotCode}_cmp_TT_${exportRow.versionString}`; - // Same convention as the panel's getShowFootageRoot()/getEpisodePrefix(): the - // picture-lock plate lives at {footageRoot}/{showId}/_PICLOCKS, glob-matched - // by "{showId}[_{episode}]_*". - const showId = shot.project.showId?.trim() || shot.project.code; - const piclockDir = templates.footageRoot ? `${templates.footageRoot}/${showId}/_PICLOCKS` : ""; - const piclockGlobPrefix = shot.episode?.trim() ? `${showId}_${shot.episode.trim()}` : showId; - return { stage: "PREVIEW", exportId: exportRow.id, @@ -132,13 +110,6 @@ export async function buildPreviewManifest(exportRow: ExportForPreview): Promise mp4Output: `${exportRow.outputDir}/${base}.mp4`, slateScopeProp: cfg.slateScopeProp, slateSubmissionProp: cfg.slateSubmissionProp, - noPictureLock: exportRow.noPictureLock, - piclockDir, - piclockGlobPrefix, - seqTimecodeStart: shot.seqTimecodeStart, - seqTimecodeEnd: shot.seqTimecodeEnd, - handleFrames, - piclockRefOutput: `${exportRow.outputDir}/${base}_PICLOCK_REF.mp4`, slate: { versionName: base, date: slateDate(), @@ -187,7 +158,6 @@ export async function inheritPreviewMedia( deliveryMovPath: true, previewMovKey: true, thumbnailKey: true, - piclockRefKey: true, versionId: true, }, }); @@ -204,7 +174,6 @@ export async function inheritPreviewMedia( deliveryMovPath: donor.deliveryMovPath, previewMovKey: donor.previewMovKey, thumbnailKey: donor.thumbnailKey, - piclockRefKey: donor.piclockRefKey, // The Version row stays attached to the export that actually produced the // media; this one only points at the same files. versionId: donor.versionId, @@ -298,13 +267,9 @@ export interface FinalizeInput { thumbnailKey?: string; metadataKey?: string; logFileKey?: string; - /** Full-duration, ungraded picture-lock reference MP4 — a diagnostic sidecar, not a Version. */ - piclockRefKey?: string; }; renderStats?: { renderSeconds?: number; previewSeconds?: number }; media?: { width?: number; height?: number; frameCount?: number; fps?: number; fileName?: string }; - /** Why the picture-lock trim/reference was skipped, when it was — surfaced on the export's timeline. */ - piclockSkippedReason?: string | null; } /** @@ -473,12 +438,7 @@ export async function finalizePreview(jobId: string, machineId: string, body: Fi ? " — review version media replaced" : ` — review version created (${inheritedApproval ?? "PENDING_REVIEW"})` : " — no preview media reported") + - (taskRenamed ? ` — task renamed to ${taskTitle}` : "") + - (artifacts.piclockRefKey - ? " — picture-lock reference built" - : body.piclockSkippedReason - ? ` — picture lock skipped (${body.piclockSkippedReason})` - : ""), + (taskRenamed ? ` — task renamed to ${taskTitle}` : ""), }, { ...(versionId ? { versionId } : {}), @@ -486,7 +446,6 @@ export async function finalizePreview(jobId: string, machineId: string, body: Fi ...(artifacts.previewMovKey ? { previewMovKey: artifacts.previewMovKey } : {}), ...(artifacts.thumbnailKey ? { thumbnailKey: artifacts.thumbnailKey } : {}), ...(artifacts.metadataKey ? { metadataKey: artifacts.metadataKey } : {}), - ...(artifacts.piclockRefKey ? { piclockRefKey: artifacts.piclockRefKey } : {}), } ); diff --git a/lib/render-pipeline/project-templates.ts b/lib/render-pipeline/project-templates.ts index 3461c17..d2907de 100644 --- a/lib/render-pipeline/project-templates.ts +++ b/lib/render-pipeline/project-templates.ts @@ -20,10 +20,6 @@ export const pipelineConfigSchema = z.object({ templateAep: z.string().max(1000).optional().nullable(), /** Where per-shot pre-renders live, imported by Import Renders. */ rendersRoot: z.string().max(1000).optional().nullable(), - /** Bare footage root (e.g. "V:/_FOOTAGE") the panel's Pull Picture Lock reads - * from at `{footageRoot}/{showId}/_PICLOCKS` — same convention as the panel's - * own `getShowFootageRoot()`. */ - footageRoot: z.string().max(1000).optional().nullable(), /** Overrides for templates that depart from the {showId}_* convention. */ overlayComp: z.string().max(200).optional().nullable(), previewComp: z.string().max(200).optional().nullable(), @@ -38,7 +34,6 @@ export type PipelineConfigInput = z.infer; export interface ResolvedTemplates { templateAep: string; rendersRoot: string; - footageRoot: string; overlayComp: string; previewComp: string; lutComp: string; @@ -83,11 +78,10 @@ export async function resolveProjectTemplates(project: { return fallback; }; - const [defaultAep, defaultMov, defaultMp4, defaultFootageRoot] = await Promise.all([ + const [defaultAep, defaultMov, defaultMp4] = await Promise.all([ getConfigValue("preview.templateAep"), getConfigValue("preview.movTemplate"), getConfigValue("preview.mp4Template"), - getConfigValue("preview.footageRoot"), ]); return { @@ -95,7 +89,6 @@ export async function resolveProjectTemplates(project: { // No sensible studio-wide default: a renders root that points at another // show's folder is worse than an empty one the panel can report. rendersRoot: pick("rendersRoot", ""), - footageRoot: pick("footageRoot", defaultFootageRoot), overlayComp: pick("overlayComp", convention.overlayComp), previewComp: pick("previewComp", convention.previewComp), lutComp: pick("lutComp", convention.lutComp),