Files
vfxreview/RenderWorker/VFXReviewWorker/Dtos.cs
T
twotalesanimation 1163bd6455
Deploy / deploy (push) Successful in 5m57s
feat(preview): burn the production name into the slate and the overlay
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 <noreply@anthropic.com>
2026-08-14 01:42:37 +02:00

281 lines
9.9 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
namespace VFXReviewWorker;
// Wire types for the /api/ext pipeline endpoints (RenderPipeline2 §6).
public sealed class RegisterResponse
{
public MachineInfo Machine { get; set; } = new();
public ServerConfig Config { get; set; } = new();
}
public sealed class MachineInfo
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
public bool Enabled { get; set; }
}
/// <summary>Server-supplied tuning (E6) — fleet-tunable without touching installs.</summary>
public sealed class ServerConfig
{
public int PollSeconds { get; set; } = 10;
public int HeartbeatSeconds { get; set; } = 30;
public int LeaseSeconds { get; set; } = 300;
public int MaxAttempts { get; set; } = 3;
public int StallTimeoutSeconds { get; set; } = 600;
/// <summary>
/// Validation stage (§8). The server decides whether it runs, so turning it
/// off is a config change there rather than a redeploy of every worker.
/// </summary>
public bool ValidationEnabled { get; set; } = true;
public int ValidationSampleEvery { get; set; } = 25;
/// <summary>Head handles the plates carry; only used for the API timecode fallback (§8.1a).</summary>
public int ValidationHandleFrames { get; set; } = 8;
}
public sealed class ClaimResponse
{
public ClaimedJob Job { get; set; } = new();
}
public sealed class ClaimedJob
{
public string Id { get; set; } = "";
public string Type { get; set; } = "AE_RENDER";
public int Attempt { get; set; }
public int MaxAttempts { get; set; }
public string? ExportId { get; set; }
public DateTimeOffset? LeaseExpiresAt { get; set; }
/// <summary>Manifest shape depends on the job type — deserialize per stage.</summary>
public JsonElement Manifest { get; set; }
public RenderManifest AsRenderManifest() =>
Manifest.Deserialize<RenderManifest>(WorkerOptions.JsonOpts)
?? throw new InvalidOperationException("Job manifest is not a render manifest");
public PreviewManifest AsPreviewManifest() =>
Manifest.Deserialize<PreviewManifest>(WorkerOptions.JsonOpts)
?? throw new InvalidOperationException("Job manifest is not a preview manifest");
public DeliveryManifest AsDeliveryManifest() =>
Manifest.Deserialize<DeliveryManifest>(WorkerOptions.JsonOpts)
?? throw new InvalidOperationException("Job manifest is not a delivery manifest");
}
/// <summary>DELIVERY_BUILD manifest (server-built, RenderPipeline2 §11).</summary>
public sealed class DeliveryManifest
{
public string Stage { get; set; } = "DELIVERY";
public string PackageId { get; set; } = "";
public string PackageName { get; set; } = "";
public int PackageNumber { get; set; }
public string Episode { get; set; } = "";
public string PackageDir { get; set; } = "";
public string ReportFile { get; set; } = "";
public bool IncludeMovs { get; set; } = true;
public string CopyMode { get; set; } = "auto";
public string ChecksumAlgo { get; set; } = "xxh64";
public List<string> ReportFormats { get; set; } = new();
public List<DeliveryManifestItem> Items { get; set; } = new();
}
public sealed class DeliveryManifestItem
{
public string ItemId { get; set; } = "";
public string ExportId { get; set; } = "";
public string ShotCode { get; set; } = "";
public string VersionString { get; set; } = "";
public string SourceDir { get; set; } = "";
public string OutputPattern { get; set; } = "";
public int FrameStart { get; set; }
public int FrameEnd { get; set; }
public int? FileCount { get; set; }
public string? Checksum { get; set; }
public string? DeliveryMovPath { get; set; }
public string ShotDir { get; set; } = "";
}
/// <summary>PREVIEW_ONLY manifest (server-built, RenderPipeline2 §9).</summary>
public sealed class PreviewManifest
{
public string Stage { get; set; } = "PREVIEW";
public string ExportId { get; set; } = "";
public string ShotCode { get; set; } = "";
public string VersionString { get; set; } = "";
public string OutputDir { get; set; } = "";
public string OutputPattern { get; set; } = "";
public int FrameStart { get; set; }
public int FrameEnd { get; set; }
public double Fps { get; set; } = 24;
public string TemplateAep { get; set; } = "";
public string TemplateComp { get; set; } = "";
public string? OverlayComp { get; set; }
public string? LutComp { get; set; }
public string MovTemplate { get; set; } = "";
public string Mp4Template { get; set; } = "";
public string MovOutput { get; set; } = "";
public string Mp4Output { get; set; } = "";
public string? SlateScopeProp { get; set; }
public string? SlateSubmissionProp { get; set; }
public SlateInfo Slate { get; set; } = new();
public int TotalFrames => FrameEnd - FrameStart + 1;
}
public sealed class SlateInfo
{
public string VersionName { get; set; } = "";
public string Date { get; set; } = "";
public string? Description { get; set; }
public string? Notes { get; set; }
public string ShotCode { get; set; } = "";
/// <summary>Production name, burned into the slate and the overlay.</summary>
public string? Show { get; set; }
public string? Episode { get; set; }
public string? Scene { get; set; }
public string? VfxScope { get; set; }
public string? SubmissionNote { get; set; }
}
/// <summary>Result JSON written by vfxr_build_preview.jsx.</summary>
public sealed class PreviewBuildResult
{
public bool Ok { get; set; }
public string? Error { get; set; }
public List<string> Warnings { get; set; } = new();
public string? TempAep { get; set; }
public string? PreviewComp { get; set; }
public int? FrameCount { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
}
/// <summary>The Render Manifest (§6.2) — the fields the render and validation stages need; the rest is preserved raw.</summary>
public sealed class RenderManifest
{
public string ShotCode { get; set; } = "";
public string AepPath { get; set; } = "";
public string CompName { get; set; } = "";
public string OutputDir { get; set; } = "";
public string OutputPattern { get; set; } = "";
public string? OutputModuleTemplate { get; set; }
public string? RenderSettingsTemplate { get; set; }
/// <summary>Comp timeline range handed to aerender (-s/-e).</summary>
public int FrameStart { get; set; }
public int FrameEnd { get; set; }
/// <summary>
/// The number the *first output file* carries — the output module's
/// starting number (house convention 1001), which is deliberately not the
/// comp's frame range: a comp anchored to the plate's timecode starts in
/// the millions. Null on manifests predating this field.
/// </summary>
public int? OutputFrameStart { get; set; }
public double Fps { get; set; } = 24;
public int Width { get; set; }
public int Height { get; set; }
public ManifestExpected? Expected { get; set; }
public ManifestSource? Source { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? Extra { get; set; }
public int TotalFrames => FrameEnd - FrameStart + 1;
}
/// <summary>Per-project expectations the validation engine checks against (§8.1).</summary>
public sealed class ManifestExpected
{
public string? Colorspace { get; set; }
public string? BitDepth { get; set; }
public string? ExrCompression { get; set; }
public bool? Alpha { get; set; }
/// <summary>The API's editorial in-point — the rank 3 timecode fallback only (§8.1a).</summary>
public string? TimecodeStart { get; set; }
}
/// <summary>Plate provenance resolved by the panel from the comp's footage layers (§6.2).</summary>
public sealed class ManifestSource
{
public PlateRef? TimingPlate { get; set; }
public List<PlateRef> Plates { get; set; } = new();
public string? StartSource { get; set; }
public int? StartFrames { get; set; }
public int? HandleFrames { get; set; }
}
public sealed class PlateRef
{
public string? Element { get; set; }
public string Path { get; set; } = "";
public int? FrameStart { get; set; }
public int? FrameEnd { get; set; }
}
public sealed class ProgressResponse
{
public bool Ok { get; set; }
public bool CancelRequested { get; set; }
public DateTimeOffset? LeaseExpiresAt { get; set; }
}
public sealed class HeartbeatResponse
{
public bool Ok { get; set; }
public List<WorkerCommand> Commands { get; set; } = new();
}
public sealed class WorkerCommand
{
public string Type { get; set; } = "";
public string? JobId { get; set; }
}
public sealed class PresignResponse
{
public string PresignedUrl { get; set; } = "";
public string Key { get; set; } = "";
public string Url { get; set; } = "";
}
public sealed class ExportDetailResponse
{
public ExportDetail Export { get; set; } = new();
}
public sealed class ExportDetail
{
public string Id { get; set; } = "";
public string Status { get; set; } = "";
public List<RenderJobDetail> RenderJobs { get; set; } = new();
}
public sealed class RenderJobDetail
{
public string Id { get; set; } = "";
public string Status { get; set; } = "";
}
/// <summary>Durable lifecycle report persisted to the spool directory (§7.6).</summary>
public sealed class SpooledReport
{
public string Kind { get; set; } = ""; // "fail" | "complete-render"
public string JobId { get; set; } = "";
public Dictionary<string, object?> Payload { get; set; } = new();
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
}
/// <summary>current-job.json — crash recovery state (§7.5).</summary>
public sealed class CurrentJobState
{
public string JobId { get; set; } = "";
public string? ExportId { get; set; } = "";
public string MachineId { get; set; } = "";
public string OutputDirLocal { get; set; } = "";
public DateTimeOffset ClaimedAt { get; set; }
}