Files
twotalesanimation 2e9fa45580
Deploy / deploy (push) Successful in 3m31s
feat(review): flag handles in the review player (metadata only)
Renders run 8 frames past the cut at each end. Directors were flagging
glitches in unfixed handles as shot problems, so the player now marks
them — without trimming or burning anything into the EXRs/MOV/MP4.

- Version.handleHead/handleTail/pictureStartFrame, Export.handleHead/
  handleTail; migration backfills pipeline exports and their preview
  versions from the manifest's handleFrames (else 8)
- AE panel: "Includes handles [8] frames each end" -> manifest.review;
  older manifests fall back to source.handleFrames
- finalize copies handles onto the Version; worker 1.6.4 reports the
  head slate length (pictureStartSeconds), else the player infers it
  as MP4 frames - EXR frameCount
- Player: hatched HANDLE / SLATE regions and cut marks on the timeline,
  "HEAD/TAIL HANDLE · NOT IN EDIT" overlay on handle frames, HANDLE badge
  on notes, "Edit range only" toggle (default on in the client portal)
- Staff Handles editor on the review page for manual uploads/corrections

Also fixes two player bugs found while testing: timeToFrame floored
float error (every seek landed a frame early), and J/reverse never ran
(the keyboard effect's cleanup stopped it on the next render).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:07:29 +02:00

40 lines
1.7 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { manifestHandles, renderOutputBase } from "./exports";
import type { RenderManifest } from "./manifest";
import { nextTaskTitle } from "./preview";
describe("renderOutputBase", () => {
it("follows the panel's house convention", () => {
expect(renderOutputBase("UNG_102_019_010", "v001")).toBe("UNG_102_019_010_cmp_TT_v001");
});
// createExport names the comp task it creates after this basename, and
// buildPreviewManifest names the MOV/MP4 off the same call — so a shot queued
// with no task ends up with a task titled exactly like its preview file. The
// title already carries the version, which makes finalizePreview's rename of
// the task to the delivered version a no-op on that first export.
it("gives a freshly created comp task a title finalizePreview leaves alone", () => {
const title = renderOutputBase("UNG_102_019_010", "v003");
expect(nextTaskTitle(title, "v003")).toBe(title);
});
});
describe("manifestHandles", () => {
const base = {} as RenderManifest;
it("uses the panel's explicit handles, including an explicit none", () => {
expect(manifestHandles({ ...base, review: { handleHead: 8, handleTail: 12 } })).toEqual({ head: 8, tail: 12 });
expect(
manifestHandles({ ...base, review: { handleHead: 0, handleTail: 0 }, source: { handleFrames: 8 } })
).toEqual({ head: 0, tail: 0 });
});
it("falls back to the handle length older panels sent with the plates", () => {
expect(manifestHandles({ ...base, source: { handleFrames: 8 } })).toEqual({ head: 8, tail: 8 });
});
it("is unknown when the manifest says nothing", () => {
expect(manifestHandles(base)).toBeNull();
});
});