chore: import upstream snapshot with attribution
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Helpers shared between the distributed activity scripts (`plan.ts`,
|
||||
* `renderChunk.ts`, `assemble.ts`). Kept module-local so the public surface
|
||||
* stays just the three activity functions plus their result types.
|
||||
*/
|
||||
|
||||
import { execFile as execFileCallback } from "node:child_process";
|
||||
import { dirname, join } from "node:path";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { type Fps } from "@hyperframes/core";
|
||||
import { type VideoElement, type VideoFrameFormat, type VideoMetadata } from "@hyperframes/engine";
|
||||
import { type RenderConfig, type RenderJob, createRenderJob } from "../renderOrchestrator.js";
|
||||
import { defaultLogger, type ProducerLogger } from "../../logger.js";
|
||||
|
||||
/**
|
||||
* Output container formats the distributed pipeline supports end-to-end.
|
||||
* Single source of truth for the format union — `plan()`, `renderChunk()`,
|
||||
* `assemble()`, the aws-lambda handler, and the harness all derive from
|
||||
* this type. Adding a new format starts here.
|
||||
*/
|
||||
export type DistributedFormat = "mp4" | "mov" | "png-sequence" | "webm";
|
||||
|
||||
/**
|
||||
* Filename of the per-video extraction manifest written by `plan()` into
|
||||
* `<planDir>/meta/` and consumed by `renderChunk()` to rebuild the
|
||||
* BeforeCaptureHook that injects pre-extracted frames into the page.
|
||||
* Absence is fine — compositions with no `<video>` elements never
|
||||
* produce the file.
|
||||
*/
|
||||
export const PLAN_VIDEOS_META_RELATIVE_PATH = "meta/videos.json";
|
||||
|
||||
/**
|
||||
* On-disk shape of `<planDir>/meta/videos.json`. The engine's
|
||||
* `ExtractedFrames` shape carries an absolute `outputDir`, a `framePaths`
|
||||
* Map, and potentially an open file descriptor — none of those survive
|
||||
* a serialize → re-deserialize round trip across processes. The
|
||||
* serialized form keeps only what plan-time produced; `renderChunk` re-
|
||||
* derives `outputDir` (always `<planDir>/video-frames/<videoId>`) and
|
||||
* `framePaths` (re-listed from that directory) when reconstructing the
|
||||
* `FrameLookupTable`.
|
||||
*/
|
||||
export interface PlanVideosJson {
|
||||
videos: VideoElement[];
|
||||
extracted: Array<{
|
||||
videoId: string;
|
||||
srcPath: string;
|
||||
framePattern: string;
|
||||
fps: number;
|
||||
totalFrames: number;
|
||||
metadata: VideoMetadata;
|
||||
}>;
|
||||
}
|
||||
|
||||
const execFile = promisify(execFileCallback);
|
||||
|
||||
/**
|
||||
* Cached first line of `ffmpeg -version` (e.g. `"ffmpeg version 6.1.1"`).
|
||||
* Cached because workers that fan out multiple `renderChunk()` calls in the
|
||||
* same process (Cloud Run Jobs, Temporal activity workers) would otherwise
|
||||
* spawn ffmpeg once per chunk just to read the version — ~20-50ms each.
|
||||
*/
|
||||
let cachedFfmpegVersion: string | null = null;
|
||||
|
||||
/**
|
||||
* Read `ffmpeg -version` first line. The string is opaque — `planHash`
|
||||
* mixes it in verbatim, so any drift across worker hosts trips a
|
||||
* `FFMPEG_VERSION_MISMATCH` rather than producing pixels that subtly
|
||||
* disagree with the plan's baked-in encoder args.
|
||||
*/
|
||||
export async function readFfmpegVersion(): Promise<string> {
|
||||
if (cachedFfmpegVersion !== null) return cachedFfmpegVersion;
|
||||
const { stdout } = await execFile("ffmpeg", ["-version"], { maxBuffer: 1024 * 1024 });
|
||||
const firstLine = stdout.split(/\r?\n/)[0]?.trim() ?? "";
|
||||
if (!firstLine) {
|
||||
throw new Error("ffmpeg -version returned empty output");
|
||||
}
|
||||
cachedFfmpegVersion = firstLine;
|
||||
return firstLine;
|
||||
}
|
||||
|
||||
/** Test-only: clear the cached ffmpeg version so a fresh probe runs. */
|
||||
function _resetFfmpegVersionCacheForTests(): void {
|
||||
cachedFfmpegVersion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs for {@link buildSyntheticRenderJob}. The two distributed activity
|
||||
* scripts (`plan.ts`, `renderChunk.ts`) reach for slightly different
|
||||
* sources — caller config vs. frozen `LockedRenderConfig` — but the
|
||||
* resulting `RenderJob` shape is identical, so the helper accepts both.
|
||||
*/
|
||||
export interface SyntheticRenderJobInput {
|
||||
fps: Fps;
|
||||
format: RenderConfig["format"];
|
||||
quality: RenderConfig["quality"];
|
||||
crf?: number;
|
||||
bitrate?: string;
|
||||
videoFrameFormat?: VideoFrameFormat;
|
||||
outputResolution?: RenderConfig["outputResolution"];
|
||||
hdrMode: RenderConfig["hdrMode"];
|
||||
entryFile: string;
|
||||
logger?: ProducerLogger;
|
||||
producerConfig?: RenderConfig["producerConfig"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a `RenderJob` from a distributed-render config. The distributed
|
||||
* activities operate without a full `RenderJob` (they're stateless workers),
|
||||
* so we build one to feed the existing stage interfaces.
|
||||
*/
|
||||
export function buildSyntheticRenderJob(input: SyntheticRenderJobInput): RenderJob {
|
||||
const renderConfig: RenderConfig = {
|
||||
fps: input.fps,
|
||||
quality: input.quality,
|
||||
format: input.format,
|
||||
crf: input.crf,
|
||||
videoBitrate: input.bitrate,
|
||||
videoFrameFormat: input.videoFrameFormat,
|
||||
outputResolution: input.outputResolution,
|
||||
// Distributed mode hard-pins to software GPU. The plan-time validator
|
||||
// refuses to fan out otherwise.
|
||||
useGpu: false,
|
||||
debug: false,
|
||||
entryFile: input.entryFile,
|
||||
logger: input.logger ?? defaultLogger,
|
||||
hdrMode: input.hdrMode,
|
||||
producerConfig: input.producerConfig,
|
||||
};
|
||||
return createRenderJob(renderConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the producer package version by walking up from the calling
|
||||
* module until a `package.json` whose `name === "@hyperframes/producer"`
|
||||
* is found. Works for both the bundled `dist/index.js` (1 level up) and
|
||||
* the unbundled source tree (4 levels up).
|
||||
*
|
||||
* Cached at module load — the version is fixed for the life of the process,
|
||||
* and reading the package.json over and over wastes per-chunk syscalls.
|
||||
*/
|
||||
let cachedProducerVersion: string | null = null;
|
||||
export function readProducerVersion(): string {
|
||||
if (cachedProducerVersion !== null) return cachedProducerVersion;
|
||||
const startDir = dirname(fileURLToPath(import.meta.url));
|
||||
let current = startDir;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const candidate = join(current, "package.json");
|
||||
if (existsSync(candidate)) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(candidate, "utf-8")) as {
|
||||
name?: string;
|
||||
version?: string;
|
||||
};
|
||||
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
||||
cachedProducerVersion = pkg.version;
|
||||
return pkg.version;
|
||||
}
|
||||
} catch {
|
||||
// Fall through to the next ancestor.
|
||||
}
|
||||
}
|
||||
const parent = dirname(current);
|
||||
if (parent === current) break;
|
||||
current = parent;
|
||||
}
|
||||
cachedProducerVersion = "0.0.0-unknown";
|
||||
return cachedProducerVersion;
|
||||
}
|
||||
Reference in New Issue
Block a user