From 1163bd645557f3b8b9b29337449f8f9cce52fdb0 Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:42:37 +0200 Subject: [PATCH] feat(preview): burn the production name into the slate and the overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preview manifest now carries the show name, taken from the project's name rather than its code — a slate is read by a human, so "Ungovernable" beats "UNG_S1". Both the worker's headless build and the panel's own preview set it, so a farm-generated preview and an artist-built one still match. The panel writes it whenever it touches an overlay, not only when building a preview, so existing comps pick it up on the next Add Overlay. The slate's other properties are title case ("Date", "Scene") while the overlay's are upper ("DATE", "SHOT NAME"), so both spellings of SHOW are attempted; a template exposing neither reports its actual property names as a build warning rather than failing silently, matching how the VFX Scope and Submission Note fields already behave. Panel 2.2.0, worker 1.2.0. Co-Authored-By: Claude Opus 5 --- RenderWorker/VFXReviewWorker/Dtos.cs | 2 ++ .../VFXReviewWorker/VFXReviewWorker.csproj | 2 +- RenderWorker/scripts/vfxr_build_preview.jsx | 23 ++++++++++++ VFXReviewConnector.jsx | 35 ++++++++++++++++++- lib/render-pipeline/preview.ts | 5 +++ 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/RenderWorker/VFXReviewWorker/Dtos.cs b/RenderWorker/VFXReviewWorker/Dtos.cs index 04d9337..cb60bdf 100644 --- a/RenderWorker/VFXReviewWorker/Dtos.cs +++ b/RenderWorker/VFXReviewWorker/Dtos.cs @@ -134,6 +134,8 @@ public sealed class SlateInfo public string? Description { get; set; } public string? Notes { get; set; } public string ShotCode { get; set; } = ""; + /// Production name, burned into the slate and the overlay. + public string? Show { get; set; } public string? Episode { get; set; } public string? Scene { get; set; } public string? VfxScope { get; set; } diff --git a/RenderWorker/VFXReviewWorker/VFXReviewWorker.csproj b/RenderWorker/VFXReviewWorker/VFXReviewWorker.csproj index 22db3e5..8bb313f 100644 --- a/RenderWorker/VFXReviewWorker/VFXReviewWorker.csproj +++ b/RenderWorker/VFXReviewWorker/VFXReviewWorker.csproj @@ -15,7 +15,7 @@ every change that gets published. publish.ps1 appends the git SHA via SourceRevisionId, giving "1.1.0+feb1c7b". --> - 1.1.0 + 1.2.0 diff --git a/RenderWorker/scripts/vfxr_build_preview.jsx b/RenderWorker/scripts/vfxr_build_preview.jsx index 15ce765..81c8b13 100644 --- a/RenderWorker/scripts/vfxr_build_preview.jsx +++ b/RenderWorker/scripts/vfxr_build_preview.jsx @@ -266,6 +266,27 @@ } } + // The production name, burned into both the slate and the overlay. The + // slate's other fields are title case ("Date", "Scene") while the overlay's + // are upper ("DATE", "SHOT NAME"), so both spellings are attempted rather + // than assuming which template uses which — and a template that exposes + // neither reports its actual property names instead of failing silently. + function setShowField(layer, value, label) { + var candidates = ["SHOW", "Show"]; + var i; + + if (value === null || value === undefined || value === "") { + return; + } + for (i = 0; i < candidates.length; i += 1) { + if (setEssentialProperty(layer, candidates[i], value)) { + return; + } + } + warn(label + ": no Essential Property named " + candidates.join(" or ") + + ". Available: " + listEssentialPropertyNames(layer).join(", ")); + } + function setCompStartFrame(comp, frameNumber) { try { comp.displayStartFrame = frameNumber; @@ -437,6 +458,7 @@ overlayLayer.enabled = true; setEssentialProperty(overlayLayer, "DATE", ctx.slate.date); setEssentialProperty(overlayLayer, "SHOT NAME", ctx.slate.versionName); + setShowField(overlayLayer, ctx.slate.show, "Overlay SHOW"); } else { warn("Overlay comp \"" + ctx.overlayComp + "\" not found — rendering without burn-ins"); } @@ -471,6 +493,7 @@ setEssentialProperty(slateLayer, "Episode", ctx.slate.episode); setEssentialProperty(slateLayer, "Scene", ctx.slate.scene); setEssentialProperty(slateLayer, "Frames", result.frameCount); + setShowField(slateLayer, ctx.slate.show, "Slate SHOW"); setSlateField(slateLayer, ctx.slateScopeProp, ctx.slate.vfxScope, "VFX Scope"); // The slate's Notes field carries this submission's note; when the diff --git a/VFXReviewConnector.jsx b/VFXReviewConnector.jsx index 31392fb..03d98bc 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.1.2"; +var PANEL_VERSION = "2.2.0"; (function VFXReviewConnector(thisObj) { var uiState = { @@ -1331,6 +1331,36 @@ var PANEL_VERSION = "2.1.2"; return null; } + // The production name, burned into both the slate and the overlay. The + // slate's fields are title case ("Date", "Scene") and the overlay's are + // upper ("DATE", "SHOT NAME"), so both spellings are attempted rather than + // assuming which template uses which. Mirrors setShowField in the worker's + // vfxr_build_preview.jsx so panel- and farm-built previews match. + function setShowProperty(layer, label) { + var value = getShowName(); + var candidates = ["SHOW", "Show"]; + var i; + + if (!value) { + return false; + } + for (i = 0; i < candidates.length; i += 1) { + if (setEssentialProperty(layer, candidates[i], value)) { + return true; + } + } + diagLog(label + ": no Essential Property named SHOW or Show on this layer"); + return false; + } + + // The production's own name, not its code: this is read off a slate. + function getShowName() { + if (!CURRENT_PROJECT) { + return ""; + } + return trimText(CURRENT_PROJECT.name || CURRENT_PROJECT.code || ""); + } + function setEssentialProperty(layer, propName, value) { try { layer.property("Essential Properties").property(propName).setValue(String(value)); @@ -1480,6 +1510,7 @@ var PANEL_VERSION = "2.1.2"; essentialProperties = overlayLayer.property("Essential Properties"); essentialProperties.property("DATE").setValue(getDateString()); essentialProperties.property("SHOT NAME").setValue(overlayShotCode + "_cmp_TT_" + (apiData.shot.shotVersion || "v001")); + setShowProperty(overlayLayer, "Overlay SHOW"); return true; } catch (overlayError) { return false; @@ -1620,6 +1651,7 @@ var PANEL_VERSION = "2.1.2"; essentialProperties = overlayLayer.property("Essential Properties"); essentialProperties.property("DATE").setValue(dateString); essentialProperties.property("SHOT NAME").setValue(shotCode + "_cmp_TT_" + (data.shot.shotVersion || "v001")); + setShowProperty(overlayLayer, "Overlay SHOW"); updated += 1; } catch (overlayError) { skipped += 1; @@ -1670,6 +1702,7 @@ var PANEL_VERSION = "2.1.2"; setEssentialProperty(slateLayer, "Episode", shot.episode); setEssentialProperty(slateLayer, "Scene", shot.scene); setEssentialProperty(slateLayer, "Frames", frameCount); + setShowProperty(slateLayer, "Slate SHOW"); shotLayer = previewComp.layer("SHOT"); var targetOutPoint = shotLayer.inPoint + shotComp.duration; diff --git a/lib/render-pipeline/preview.ts b/lib/render-pipeline/preview.ts index 0069354..07ca98f 100644 --- a/lib/render-pipeline/preview.ts +++ b/lib/render-pipeline/preview.ts @@ -37,6 +37,8 @@ export interface PreviewManifest { description: string | null; notes: string | null; shotCode: string; + /** Production name, burned into the slate and the overlay. */ + show: string | null; episode: string | null; scene: string | null; vfxScope: string | null; @@ -75,6 +77,7 @@ export async function buildPreviewManifest(exportRow: ExportForPreview): Promise description: true, notes: true, slateDescription: true, + project: { select: { name: true, code: true } }, }, }); if (!shot) throw new PipelineError(404, "Shot not found for export"); @@ -111,6 +114,8 @@ export async function buildPreviewManifest(exportRow: ExportForPreview): Promise description: shot.slateDescription ?? shot.description, notes: shot.notes, shotCode: shot.shotCode, + // The production's own name, not its code: this is read off a slate. + show: shot.project?.name ?? shot.project?.code ?? null, episode: shot.episode, scene: shot.scene, vfxScope: exportRow.vfxScope,