diff --git a/RenderPipeline2.md b/RenderPipeline2.md index 0ce817d..88a16c4 100644 --- a/RenderPipeline2.md +++ b/RenderPipeline2.md @@ -1131,6 +1131,14 @@ Cost note: the AE preview pass roughly doubles machine time per export. This mat Finalize (E13) then creates the `Version` row server-side (Section 6.4) in pipeline mode — visible internally, never client-shared, no task/shot status changes. +### 9.4 Skipping the preview (`Export.skipPreview`) + +Not every export is framed like its review. A deliverable rendered unscaled or without the punch-in shares a shot and a version history with the review, but a preview rebuilt from *its* frames would show the wrong picture — the earlier export's MOV is the correct one. + +`skipPreview` (set at Queue Export, or per-action when overriding a validation failure) renders the EXRs and goes straight to READY_FOR_QC, **inheriting** `deliveryMovPath`, `previewMovKey`, `thumbnailKey` and `versionId` from the newest earlier export of that shot which has them. Inheritance matters as much as the skip: the delivery builder takes its MOV from `deliveryMovPath`, so an export with none would package EXRs alone. QC is unaffected — the export still needs a human pass before it can be delivered. + +With no earlier export to inherit from, the export still reaches READY_FOR_QC and the timeline records that it has no review media. + --- ## 10. QC Workflow diff --git a/VFXReviewConnector.jsx b/VFXReviewConnector.jsx index 534e4ed..9a7e420 100644 --- a/VFXReviewConnector.jsx +++ b/VFXReviewConnector.jsx @@ -32,7 +32,7 @@ var SETTINGS_KEY = "cfg"; // Bump on every change deployed to a workstation. The panel reports this to // the server on load, so Pipeline → Machines shows which version each machine // is actually running and flags the ones left behind. -var PANEL_VERSION = "2.2.1"; +var PANEL_VERSION = "2.3.0"; (function VFXReviewConnector(thisObj) { var uiState = { @@ -44,6 +44,7 @@ var PANEL_VERSION = "2.2.1"; lastActionText: null, pipelineStatusText: null, urgentCheckbox: null, + skipPreviewCheckbox: null, submissionNoteField: null, qcList: null, qcNotesField: null, @@ -3851,6 +3852,13 @@ var PANEL_VERSION = "2.2.1"; force: force ? true : false }; + // Renders the EXRs and inherits the previous export's review media + // rather than rebuilding a preview from frames that are not the review + // framing (unscaled / un-punched-in deliverables). + if (uiState.skipPreviewCheckbox && uiState.skipPreviewCheckbox.value) { + body.skipPreview = true; + } + if (CONFIG.artistEmail) { body.submittedByEmail = CONFIG.artistEmail; } @@ -3942,7 +3950,9 @@ var PANEL_VERSION = "2.2.1"; if (lastQueued) { updateStatus(comps.length, "Queued " + lastQueued.versionString + - (urgent ? " (urgent)" : "") + " on render farm. " + + (urgent ? " (urgent)" : "") + + ((uiState.skipPreviewCheckbox && uiState.skipPreviewCheckbox.value) ? " (no preview)" : "") + + " on render farm. " + result.queued + " queued, " + result.skipped + " skipped."); setPipelineStatus(formatExportStatus(lastShotCode, lastQueued)); startExportPolling(lastShotCode); @@ -4410,6 +4420,13 @@ var PANEL_VERSION = "2.2.1"; pipelineRow1.spacing = 6; queueExportButton = pipelineRow1.add("button", undefined, "Queue Export"); uiState.urgentCheckbox = pipelineRow1.add("checkbox", undefined, "Urgent"); + uiState.skipPreviewCheckbox = pipelineRow1.add("checkbox", undefined, "No preview"); + uiState.skipPreviewCheckbox.helpTip = + "Render the EXRs but build no preview, reusing the previous export's\n" + + "MOV and review media.\n\n" + + "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."; var slateNoteGroup = pipelinePanel.add("group"); slateNoteGroup.orientation = "row"; diff --git a/VFXReviewConnector.md b/VFXReviewConnector.md index 80d732b..93ab933 100644 --- a/VFXReviewConnector.md +++ b/VFXReviewConnector.md @@ -198,6 +198,29 @@ see [Diagnostics and logging](#diagnostics-and-logging). --- +## Render Pipeline + +### Queue Export + +Saves the project, builds the render manifest from the selected comp and the +shot record, and submits it to the farm. + +| Option | Effect | +|---|---| +| **Urgent** | Priority 20 — claimable outside the render window, ahead of normal jobs | +| **No preview** | Renders the EXRs but builds no preview, inheriting the previous export's MOV, review MP4 and thumbnail | + +**No preview** exists for deliverables framed differently from the review — +unscaled or un-punched-in EXRs. A preview rebuilt from those frames would be +the wrong picture, while the earlier export's MOV is already correct, so it +carries over instead. The export still goes through QC, and the delivery +package still gets a MOV. + +Leave it unticked for an ordinary submission: the preview is what QC and the +client review see. + +--- + ## QC Technical QC on the **rendered EXRs**, not the H.264 preview — the point is diff --git a/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx b/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx index 23cccb7..ba54937 100644 --- a/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx +++ b/app/(dashboard)/pipeline/exports/[exportId]/ExportDetailClient.tsx @@ -108,6 +108,7 @@ export function ExportDetailClient({ exportId }: { exportId: string }) { const queryClient = useQueryClient(); const [overrideOpen, setOverrideOpen] = useState(false); const [overrideReason, setOverrideReason] = useState(""); + const [overrideSkipPreview, setOverrideSkipPreview] = useState(false); const [overriding, setOverriding] = useState(false); const { data, isLoading, error } = useQuery({ @@ -135,7 +136,7 @@ export function ExportDetailClient({ exportId }: { exportId: string }) { const res = await fetch(`/api/render/exports/${exportId}/override-validation`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ reason: overrideReason }), + body: JSON.stringify({ reason: overrideReason, skipPreview: overrideSkipPreview }), }); const body = await res.json().catch(() => ({})); if (!res.ok) { @@ -240,6 +241,21 @@ export function ExportDetailClient({ exportId }: { exportId: string }) { placeholder="e.g. plate unreachable from the render node; timecode checked by hand against the EDL" className="w-full rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-sm text-zinc-200 placeholder:text-zinc-600" /> +