mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat: add Studio current-frame capture (#565)
## Problem Closes #555. Studio users could inspect the preview, but there was no first-class way to capture the current rendered frame as an image. ## What this fixes - Adds a `Capture` action to the Studio header toolbar so it does not cover the video preview. - Downloads the current composition frame as a PNG using the current player time. - Extends the existing thumbnail route and Studio/CLI thumbnail generators with an explicit PNG format path while preserving JPEG thumbnails for existing previews. - Adds URL/filename utility coverage plus thumbnail route coverage for PNG requests. ## Root cause Studio already had frame thumbnail generation, but the API path was JPEG-oriented and the editor UI only used it for previews. There was no current-frame capture affordance wired to the player state. ## Verification ### Local - `bun run --filter @hyperframes/core test src/studio-api/routes/thumbnail.test.ts` - `bun run --filter @hyperframes/studio test src/utils/frameCapture.test.ts src/player/components/PlayerControls.test.ts` - `bun run --filter @hyperframes/studio typecheck` - `bun run --filter @hyperframes/core typecheck` - `bun run --filter @hyperframes/cli typecheck` - `bunx oxlint packages/cli/src/server/studioServer.ts packages/core/src/studio-api/routes/thumbnail.test.ts packages/core/src/studio-api/routes/thumbnail.ts packages/core/src/studio-api/types.ts packages/studio/src/App.tsx packages/studio/src/icons/SystemIcons.tsx packages/studio/vite.config.ts packages/studio/src/utils/frameCapture.ts packages/studio/src/utils/frameCapture.test.ts` - `bunx oxfmt --check packages/cli/src/server/studioServer.ts packages/core/src/studio-api/routes/thumbnail.test.ts packages/core/src/studio-api/routes/thumbnail.ts packages/core/src/studio-api/types.ts packages/studio/src/App.tsx packages/studio/src/icons/SystemIcons.tsx packages/studio/vite.config.ts packages/studio/src/utils/frameCapture.ts packages/studio/src/utils/frameCapture.test.ts` - `git diff --check` ### Browser <img width="1027" height="910" alt="image" src="https://github.com/user-attachments/assets/71973af4-0279-4074-9 <img width="1026" height="902" alt="Screenshot 2026-04-29 at 16 17 22" src="https://github.com/user-attachments/assets/a32e1c19-b793-40b9-82f8-de8bbb11f123" /> 060-130839a2d419" />
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
export interface FrameCaptureRequest {
|
||||
projectId: string;
|
||||
compositionPath: string | null;
|
||||
currentTime: number;
|
||||
origin?: string;
|
||||
}
|
||||
|
||||
function normalizeCompositionPath(compositionPath: string | null): string {
|
||||
return compositionPath && compositionPath !== "master" ? compositionPath : "index.html";
|
||||
}
|
||||
|
||||
export function buildFrameCaptureUrl({
|
||||
projectId,
|
||||
compositionPath,
|
||||
currentTime,
|
||||
origin = window.location.origin,
|
||||
}: FrameCaptureRequest): string {
|
||||
const compPath = normalizeCompositionPath(compositionPath);
|
||||
const url = new URL(
|
||||
`/api/projects/${encodeURIComponent(projectId)}/thumbnail/${encodeURIComponent(compPath)}`,
|
||||
origin,
|
||||
);
|
||||
url.searchParams.set("t", Math.max(0, currentTime).toFixed(3));
|
||||
url.searchParams.set("format", "png");
|
||||
url.searchParams.set("v", String(Date.now()));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export function buildFrameCaptureFilename(compositionPath: string | null, currentTime: number) {
|
||||
const compPath = normalizeCompositionPath(compositionPath);
|
||||
const base =
|
||||
compPath
|
||||
.split("/")
|
||||
.pop()
|
||||
?.replace(/\.html$/i, "") || "frame";
|
||||
const frameTime = Math.max(0, currentTime).toFixed(3).replace(".", "-");
|
||||
return `${base}-${frameTime}s.png`;
|
||||
}
|
||||
Reference in New Issue
Block a user