9660cdc21c
Deploy / deploy (push) Successful in 3m23s
Worker 1.7.0 (preview.highQuality, default on) now, after the AE render: - encodes a High quality MP4 from the delivery MOV (full-res H.264 High, CRF 14 capped at 100 Mbps) -> Version.hqFileUrl - uploads the MOV itself -> Version.highResKey/Filename/Size Both best-effort (the preview never fails over them), with a lease keep-alive during the long encode/uploads; the export timeline records what was attached. Player: HQ switch when a High quality source exists; keeps frame, play state, speed and zoom, remembered per browser, falls back to Standard if the file won't load. ProRes itself stays download-only (browser support). Downloads: the client portal's High Res button serves the version's ProRes (client-code filename), falling back to the shot's manual file; staff get a ProRes button on the review page. Housekeeping (lib/render-pipeline/high-res.ts): HQ MP4 + ProRes are kept while a version is latest, client-shared, approved or younger than preview.highResRetentionDays (14); otherwise deleted from storage. Runs per task after finalize and hourly from the reaper; deleting a version or a reslate removes its files. The Standard MP4 is never touched. Also: ProRes/Handles top-bar buttons go icon-only below lg so they don't overlap the version selector. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
/**
|
|
* What a client is shown for a shot.
|
|
*
|
|
* `Shot.clientCode` is the naming alias some productions supply — their own
|
|
* code for the same shot. It already drives slates, overlay burn-ins and
|
|
* delivered file names (RenderPipeline2 §11.2); internal paths, comp names and
|
|
* every DB lookup stay on `shotCode`.
|
|
*
|
|
* The client portal is the other place the client's own naming belongs, and
|
|
* that includes task titles: they follow the house convention
|
|
* `UNG_102_019_010_cmp_TT_v004`, so the internal code is embedded in the
|
|
* string the client reads and copies.
|
|
*
|
|
* Apply these at the API boundary rather than at each render site, so nothing
|
|
* downstream has to remember which code it is holding.
|
|
*/
|
|
|
|
export interface ShotCodeFields {
|
|
shotCode: string;
|
|
clientCode?: string | null;
|
|
}
|
|
|
|
/** The shot's code as the client knows it; their alias if set, ours otherwise. */
|
|
export function clientShotCode(shot: ShotCodeFields): string {
|
|
return shot.clientCode?.trim() || shot.shotCode;
|
|
}
|
|
|
|
/**
|
|
* A task title with the internal shot code swapped for the client's alias.
|
|
*
|
|
* Titles are built from the shot code by convention, so this is a substring
|
|
* swap rather than a rename — a title that does not contain the code (a
|
|
* hand-edited one, say) is returned untouched rather than guessed at.
|
|
*/
|
|
export function clientTaskTitle(title: string, shot: ShotCodeFields): string {
|
|
const alias = shot.clientCode?.trim();
|
|
if (!alias || !title || alias === shot.shotCode) return title;
|
|
// split/join rather than a regex: shot codes are user-supplied and would
|
|
// otherwise need escaping.
|
|
return title.split(shot.shotCode).join(alias);
|
|
}
|
|
|
|
/**
|
|
* The High Res (ProRes) download name for a client. With an alias it follows
|
|
* the slate's client naming, `{clientCode}_{version}` — no internal code and
|
|
* no "_cmp_TT_" studio suffix; without one the file keeps its own name.
|
|
*/
|
|
export function clientHighResFilename(fileName: string, shot: ShotCodeFields | null): string {
|
|
const alias = shot?.clientCode?.trim();
|
|
if (!shot || !alias || alias === shot.shotCode) return fileName;
|
|
const m = /_(v\d+)(\.[a-z0-9]+)$/i.exec(fileName);
|
|
if (m && fileName.startsWith(shot.shotCode)) return `${alias}_${m[1]}${m[2]}`;
|
|
return fileName.split(shot.shotCode).join(alias);
|
|
}
|