a18dfc6203
Deploy / deploy (push) Successful in 6m19s
Production fallout after enabling it: the trimmed EXR footage started
after the head handle as intended, but the overlay's burn-in timecode
(hardcoded to start at frame 1001) did not shift with it, so the
delivered burn-in no longer matched the footage. The picture-lock
reference render also never worked correctly in practice. Reverts the
AE panel checkbox, the preview-build script's trim/reference logic, and
the server/worker plumbing around it back to their pre-feature state
(82dfaee) — untrimmed previews, no picture-lock reference, overlay
timecode correct again.
The noPictureLock/piclockRefKey schema columns and their migration are
deliberately left in place: they're already applied in production, and
dropping an applied migration's file risks the next `prisma migrate
deploy`. They sit unused and harmless until this is revisited.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
100 lines
3.8 KiB
TypeScript
100 lines
3.8 KiB
TypeScript
import { z } from "zod";
|
|
import { getConfigValue } from "./config";
|
|
|
|
/**
|
|
* Per-production AE workspace configuration.
|
|
*
|
|
* Comp names follow a convention derived from the show — `UNG_VFX_OVERLAY`,
|
|
* `UNG_EXPORT_TEMPLATE`, `_SHOW LUT` — so a project that names its template
|
|
* comps to the pattern needs no configuration at all. Anything that cannot be
|
|
* derived is stated explicitly: paths, above all, since a show's folder rarely
|
|
* matches its show id (UNG lives under `UNGO_VFX`).
|
|
*
|
|
* Resolved server-side and handed to both the panel (via the ext projects list)
|
|
* and the worker (inside the preview manifest), so the two cannot disagree
|
|
* about which template a render was built from.
|
|
*/
|
|
|
|
export const pipelineConfigSchema = z.object({
|
|
/** The show's slate/overlay/LUT template AEP, imported by Initialise Workspace. */
|
|
templateAep: z.string().max(1000).optional().nullable(),
|
|
/** Where per-shot pre-renders live, imported by Import Renders. */
|
|
rendersRoot: 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(),
|
|
lutComp: z.string().max(200).optional().nullable(),
|
|
/** AE output-module templates, when this show does not use the studio defaults. */
|
|
movTemplate: z.string().max(200).optional().nullable(),
|
|
mp4Template: z.string().max(200).optional().nullable(),
|
|
});
|
|
|
|
export type PipelineConfigInput = z.infer<typeof pipelineConfigSchema>;
|
|
|
|
export interface ResolvedTemplates {
|
|
templateAep: string;
|
|
rendersRoot: string;
|
|
overlayComp: string;
|
|
previewComp: string;
|
|
lutComp: string;
|
|
movTemplate: string;
|
|
mp4Template: string;
|
|
/** Which values came from the project rather than a convention or a default. */
|
|
explicit: string[];
|
|
}
|
|
|
|
/** The comp-name convention. UNG's existing templates already match it exactly. */
|
|
export function conventionalNames(showId: string) {
|
|
const prefix = showId?.trim() || "SHOW";
|
|
return {
|
|
overlayComp: `${prefix}_VFX_OVERLAY`,
|
|
previewComp: `${prefix}_EXPORT_TEMPLATE`,
|
|
// The LUT comp is named the same in every show's template; what differs is
|
|
// the LUT inside it, which is why the template AEP is per-project.
|
|
lutComp: "_SHOW LUT",
|
|
};
|
|
}
|
|
|
|
export async function resolveProjectTemplates(project: {
|
|
showId: string | null;
|
|
code: string;
|
|
pipelineConfig?: unknown;
|
|
}): Promise<ResolvedTemplates> {
|
|
const parsed = pipelineConfigSchema.safeParse(project.pipelineConfig ?? {});
|
|
const cfg: PipelineConfigInput = parsed.success ? parsed.data : {};
|
|
if (!parsed.success) {
|
|
console.error(`[render-pipeline] pipelineConfig for ${project.code} is malformed — using defaults`);
|
|
}
|
|
|
|
const showId = project.showId?.trim() || project.code;
|
|
const convention = conventionalNames(showId);
|
|
const explicit: string[] = [];
|
|
const pick = <K extends keyof PipelineConfigInput>(key: K, fallback: string): string => {
|
|
const value = cfg[key];
|
|
if (typeof value === "string" && value.trim()) {
|
|
explicit.push(key);
|
|
return value.trim();
|
|
}
|
|
return fallback;
|
|
};
|
|
|
|
const [defaultAep, defaultMov, defaultMp4] = await Promise.all([
|
|
getConfigValue("preview.templateAep"),
|
|
getConfigValue("preview.movTemplate"),
|
|
getConfigValue("preview.mp4Template"),
|
|
]);
|
|
|
|
return {
|
|
templateAep: pick("templateAep", defaultAep),
|
|
// 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", ""),
|
|
overlayComp: pick("overlayComp", convention.overlayComp),
|
|
previewComp: pick("previewComp", convention.previewComp),
|
|
lutComp: pick("lutComp", convention.lutComp),
|
|
movTemplate: pick("movTemplate", defaultMov),
|
|
mp4Template: pick("mp4Template", defaultMp4),
|
|
explicit,
|
|
};
|
|
}
|