@hyperframes/producer 0.8.180 → 0.8.183
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/distributed.d.ts +2 -2
- package/dist/distributed.js +486 -237
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +17 -17
- package/dist/index.js +508 -255
- package/dist/public-server.js +224 -120
- package/dist/services/distributed/assemble.d.ts +2 -1
- package/dist/services/distributed/chunkFrames.d.ts +6 -1
- package/dist/services/distributed/mp4Stitch.d.ts +4 -4
- package/dist/services/distributed/plan.d.ts +11 -0
- package/dist/services/distributed/planV2.d.ts +1 -0
- package/dist/services/distributed/projectHash.d.ts +1 -4
- package/dist/services/render/artifactTransaction.d.ts +7 -0
- package/dist/services/render/audioPadTrim.d.ts +20 -15
- package/dist/services/render/audioTruePeak.d.ts +8 -0
- package/dist/services/render/perfSummary.d.ts +2 -0
- package/dist/services/render/stages/audioStage.d.ts +2 -0
- package/dist/services/render/stages/captureStage.d.ts +9 -3
- package/dist/services/render/stages/freezePlan.d.ts +1 -0
- package/dist/services/renderOrchestrator.d.ts +2 -2
- package/package.json +7 -7
|
@@ -31,9 +31,9 @@ export interface TrackTable {
|
|
|
31
31
|
}
|
|
32
32
|
/** Each track's sample tables, expanded to one entry per sample (and per chunk). */
|
|
33
33
|
export declare function readTracks(moov: Buffer): TrackTable[];
|
|
34
|
-
export declare function buildStitchedHeader(layouts: readonly Mp4Layout[]): Buffer;
|
|
34
|
+
export declare function buildStitchedHeader(layouts: readonly Mp4Layout[], audio?: Mp4Layout): Buffer;
|
|
35
35
|
export declare function fileRange(path: string): ReadRange;
|
|
36
|
-
/** Local join: the
|
|
37
|
-
*
|
|
38
|
-
export declare function writeStitchedMp4(chunkPaths: readonly string[], outputPath: string): Promise<void>;
|
|
36
|
+
/** Local join: the header, then `audioPath`'s and each chunk's payload. Like the ffmpeg join, it
|
|
37
|
+
* writes in a work folder beside `outputPath` and renames only a finished file there. */
|
|
38
|
+
export declare function writeStitchedMp4(chunkPaths: readonly string[], outputPath: string, audioPath?: string): Promise<void>;
|
|
39
39
|
export {};
|
|
@@ -211,6 +211,8 @@ export interface BuildLocalExecutionPlanOptions {
|
|
|
211
211
|
readonly chunksCarryAudio?: boolean;
|
|
212
212
|
/** With `chunksCarryAudio`, also write each chunk's audio packets to their own file. Plan v2 only. */
|
|
213
213
|
readonly sliceChunkAudio?: boolean;
|
|
214
|
+
/** Leave the audio to {@link buildPlanAudio}; chunks render video only. mp4 without `cfr` only. */
|
|
215
|
+
readonly deferAudio?: boolean;
|
|
214
216
|
}
|
|
215
217
|
export interface RemoteProjectFile {
|
|
216
218
|
readonly url: string;
|
|
@@ -401,7 +403,16 @@ export declare function resolveDistributedEngineConfig(config: DistributedRender
|
|
|
401
403
|
* Build the shared local execution representation used by both transport
|
|
402
404
|
* protocols. See the module docstring for the directory layout.
|
|
403
405
|
*/
|
|
406
|
+
/** Whether the plan leaves its audio to {@link buildPlanAudio}. */
|
|
407
|
+
export declare function plansSeparateAudio(config: Pick<DistributedRenderConfig, "format" | "cfr">, options: Pick<BuildLocalExecutionPlanOptions, "deferAudio">): boolean;
|
|
404
408
|
export declare function buildLocalExecutionPlan(projectDir: string, config: DistributedRenderConfig, executionPlanDir: string, options?: Readonly<BuildLocalExecutionPlanOptions>): Promise<LocalExecutionPlan>;
|
|
409
|
+
/**
|
|
410
|
+
* The audio of a plan made with `deferAudio`: the mix and true-peak hold that plan would have
|
|
411
|
+
* made, written to `outputPath` (on `workRoot`'s filesystem) at exactly the video's length.
|
|
412
|
+
*/
|
|
413
|
+
export declare function buildPlanAudio(projectDir: string, config: DistributedRenderConfig, workRoot: string, outputPath: string, options?: Pick<BuildLocalExecutionPlanOptions, "remoteFiles">): Promise<{
|
|
414
|
+
hasAudio: boolean;
|
|
415
|
+
}>;
|
|
405
416
|
/**
|
|
406
417
|
* Legacy v1 transport wrapper around {@link buildLocalExecutionPlan}.
|
|
407
418
|
*
|
|
@@ -91,6 +91,7 @@ export interface PlanV2WithPublisherOptions {
|
|
|
91
91
|
/** See {@link BuildLocalExecutionPlanOptions.remoteFiles}; needs a publisher with `referenceStoredBlob`. */
|
|
92
92
|
readonly remoteFiles?: BuildLocalExecutionPlanOptions["remoteFiles"];
|
|
93
93
|
readonly chunksCarryAudio?: boolean;
|
|
94
|
+
readonly deferAudio?: boolean;
|
|
94
95
|
}
|
|
95
96
|
export interface PlanV2MaterializationResult {
|
|
96
97
|
readonly planDir: string;
|
|
@@ -22,9 +22,6 @@ export declare function listSiteFiles(projectDir: string): string[];
|
|
|
22
22
|
* same-bytes rename still yields a fresh id. We trim to 16 chars because the
|
|
23
23
|
* full 64 isn't useful in an object key for legibility. Pass `files` when the
|
|
24
24
|
* caller already listed them, so the hashed set is the shipped set.
|
|
25
|
-
*
|
|
26
|
-
* Reads are synchronous: project trees are typically tens of MB at most
|
|
27
|
-
* (HTML/CSS/JS plus a few composition assets), so the simpler shape wins over
|
|
28
|
-
* a streaming pipeline.
|
|
25
|
+
* Files are read in fixed-size chunks, so no file is ever held whole in memory.
|
|
29
26
|
*/
|
|
30
27
|
export declare function hashProjectDir(projectDir: string, files?: readonly string[]): string;
|
|
@@ -22,6 +22,8 @@ export interface ArtifactTransactionFileSystem {
|
|
|
22
22
|
export interface ArtifactDurationProbeResult {
|
|
23
23
|
durationSeconds: number;
|
|
24
24
|
frames?: number;
|
|
25
|
+
width?: number;
|
|
26
|
+
height?: number;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Pluggable probe used by `ArtifactTransaction.validate()` when the caller
|
|
@@ -60,6 +62,9 @@ export interface ArtifactValidationExpectation {
|
|
|
60
62
|
expectedDurationSeconds: number;
|
|
61
63
|
fps?: number;
|
|
62
64
|
expectedFrames?: number;
|
|
65
|
+
/** The output size the render was asked for; a route that ignores it must not publish. */
|
|
66
|
+
expectedWidth?: number;
|
|
67
|
+
expectedHeight?: number;
|
|
63
68
|
toleranceSeconds?: number;
|
|
64
69
|
/** Path relative to the staging directory to run the duration probe against. */
|
|
65
70
|
probeTarget?: string;
|
|
@@ -84,6 +89,8 @@ export declare function buildArtifactExpectation(input: {
|
|
|
84
89
|
durationSeconds: number;
|
|
85
90
|
fps: number;
|
|
86
91
|
expectedFrames: number | undefined;
|
|
92
|
+
width?: number;
|
|
93
|
+
height?: number;
|
|
87
94
|
}): ArtifactValidationExpectation | undefined;
|
|
88
95
|
/**
|
|
89
96
|
* Stages a render beside its final destination and promotes only a validated
|
|
@@ -54,12 +54,16 @@ export interface PadTrimAudioInput {
|
|
|
54
54
|
probeVideoFrameInfo?: (videoPath: string) => Promise<ProbeVideoFrameInfo>;
|
|
55
55
|
probeAudioInfo?: (audioPath: string, signal?: AbortSignal) => Promise<AudioProbeInfo>;
|
|
56
56
|
probeAudioTruePeakDbfs?: (audioPath: string, signal?: AbortSignal) => Promise<number>;
|
|
57
|
-
runFfmpeg?: (args: string[]) => Promise<
|
|
58
|
-
|
|
59
|
-
error?: string;
|
|
60
|
-
failureReason?: "external_interruption";
|
|
61
|
-
}>;
|
|
57
|
+
runFfmpeg?: (args: string[]) => Promise<StepResult>;
|
|
58
|
+
encodeAac?: AacEncoder;
|
|
62
59
|
}
|
|
60
|
+
interface StepResult {
|
|
61
|
+
success: boolean;
|
|
62
|
+
error?: string;
|
|
63
|
+
failureReason?: "external_interruption";
|
|
64
|
+
}
|
|
65
|
+
/** Encodes `filter` applied to `inputPath` as AAC of exactly `durationSeconds`. */
|
|
66
|
+
type AacEncoder = (inputPath: string, outputPath: string, durationSeconds: number, filter: string) => Promise<StepResult>;
|
|
63
67
|
export type PadTrimOperation = "pad" | "trim" | "copy";
|
|
64
68
|
export interface PadTrimAudioResult {
|
|
65
69
|
success: boolean;
|
|
@@ -77,19 +81,21 @@ export interface PadTrimAudioResult {
|
|
|
77
81
|
/** dB the true-peak limiter lowered the whole mix by; absent when it did not engage. */
|
|
78
82
|
audioLoweredDb?: number;
|
|
79
83
|
}
|
|
80
|
-
export type
|
|
81
|
-
|
|
82
|
-
kind: PadTrimAudioStepKind;
|
|
84
|
+
export type PadTrimAudioStep = {
|
|
85
|
+
kind: "copy";
|
|
83
86
|
args: string[];
|
|
84
|
-
}
|
|
87
|
+
} | {
|
|
88
|
+
kind: "trim" | "normalize";
|
|
89
|
+
filter: string;
|
|
90
|
+
};
|
|
85
91
|
export interface PadTrimAudioPlan {
|
|
86
92
|
operation: PadTrimOperation;
|
|
87
93
|
steps: PadTrimAudioStep[];
|
|
88
94
|
cleanupPaths: string[];
|
|
89
95
|
}
|
|
90
96
|
/**
|
|
91
|
-
* Pure helper: decide the pad/trim operation and
|
|
92
|
-
*
|
|
97
|
+
* Pure helper: decide the pad/trim operation and the steps that
|
|
98
|
+
* materialize it. Exported separately so unit tests can pin
|
|
93
99
|
* every branch without spawning ffmpeg.
|
|
94
100
|
*
|
|
95
101
|
* - `sourceDuration < targetDuration` → pad with `apad` to the exact target
|
|
@@ -102,10 +108,6 @@ export interface PadTrimAudioPlan {
|
|
|
102
108
|
* run ffmpeg with `-c:a copy` to materialize the output path.
|
|
103
109
|
*/
|
|
104
110
|
export declare function buildPadTrimAudioPlan(audioPath: string, outputPath: string, sourceDurationSeconds: number, targetDurationSeconds: number): PadTrimAudioPlan;
|
|
105
|
-
export declare function buildPadTrimAudioArgs(audioPath: string, outputPath: string, sourceDurationSeconds: number, targetDurationSeconds: number): {
|
|
106
|
-
args: string[];
|
|
107
|
-
operation: PadTrimOperation;
|
|
108
|
-
};
|
|
109
111
|
/**
|
|
110
112
|
* Pad or trim the mixed audio so its exact duration matches `frameCount / fps`
|
|
111
113
|
* for the assembled video.
|
|
@@ -117,6 +119,8 @@ export declare function padOrTrimAudioToVideoFrameCount(input: PadTrimAudioInput
|
|
|
117
119
|
*/
|
|
118
120
|
export declare function constrainAacTruePeak(input: {
|
|
119
121
|
audioPath: string;
|
|
122
|
+
/** The PCM `audioPath` was encoded from: a correction re-encodes it instead of decoding the AAC. */
|
|
123
|
+
sourcePath?: string;
|
|
120
124
|
targetDurationSeconds: number;
|
|
121
125
|
signal?: AbortSignal;
|
|
122
126
|
timeoutMs?: number;
|
|
@@ -128,3 +132,4 @@ export declare function constrainAacTruePeak(input: {
|
|
|
128
132
|
}>;
|
|
129
133
|
export declare function defaultProbeVideoFrameInfo(videoPath: string, signal?: AbortSignal): Promise<ProbeVideoFrameInfo>;
|
|
130
134
|
export declare function runFfprobeJson<T>(args: string[], signal?: AbortSignal): Promise<T>;
|
|
135
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The true peak one `ebur128=peak=true` pass over the whole file reports, measured in parallel
|
|
3
|
+
* segments when the file is 48 kHz with a known length: the same digit, not an estimate.
|
|
4
|
+
*/
|
|
5
|
+
export declare function measureTruePeakDbfs(audioPath: string, options: {
|
|
6
|
+
timeoutMs: number;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}): Promise<number>;
|
|
@@ -50,6 +50,8 @@ export declare function roundDb(value: number | undefined): number | undefined;
|
|
|
50
50
|
export interface DrawElementPerfInput {
|
|
51
51
|
compileGate?: string;
|
|
52
52
|
clampReason?: string;
|
|
53
|
+
/** Engine init-time gate the pre-capture probe hit; its session may be closed before capture. */
|
|
54
|
+
probeGateReason?: string;
|
|
53
55
|
workerInversion?: "inverted" | "reverted";
|
|
54
56
|
/** Auto-resolved worker count before the inversion pinned it to 1 (set only when the inversion fired). */
|
|
55
57
|
preInversionWorkers?: number;
|
|
@@ -34,6 +34,8 @@ export interface AudioStageInput {
|
|
|
34
34
|
audios: CompositionMetadata["audios"];
|
|
35
35
|
masterFxChain?: string;
|
|
36
36
|
urlByAbsentLocalPath?: ReadonlyMap<string, string>;
|
|
37
|
+
/** Keep the float mix the AAC was encoded from here, for a later re-encode from it. */
|
|
38
|
+
keepEncoderInputAt?: string;
|
|
37
39
|
abortSignal: AbortSignal | undefined;
|
|
38
40
|
assertNotAborted: () => void;
|
|
39
41
|
/** Where a per-track failure's detail goes. Optional so tests need not pass one. */
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
import { type BeforeCaptureHook, type CaptureOptions, type CapturePerfSummary, type CaptureSession, type EngineConfig } from "@hyperframes/engine";
|
|
38
38
|
import type { FileServerHandle } from "../../fileServer.js";
|
|
39
39
|
import type { ProducerLogger } from "../../../logger.js";
|
|
40
|
-
import { type CaptureAttemptSummary, type ProgressCallback, type RenderJob } from "../../renderOrchestrator.js";
|
|
40
|
+
import { type CaptureAttemptSummary, type ProgressCallback, type RenderConfig, type RenderJob } from "../../renderOrchestrator.js";
|
|
41
41
|
import type { SdrDiskCapturePlan } from "../capturePlan.js";
|
|
42
42
|
export interface CaptureStageInput {
|
|
43
43
|
fileServer: FileServerHandle;
|
|
@@ -121,8 +121,13 @@ export type DiskCaptureHeadroom = {
|
|
|
121
121
|
};
|
|
122
122
|
/** Shared 90% disk gate used by both fallback planning and disk execution. */
|
|
123
123
|
export declare function inspectDiskCaptureHeadroom(framesDir: string, totalFrames: number, captureOptions: CaptureOptions, freeDiskBytes?: (path: string) => number | null): DiskCaptureHeadroom;
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Why this render stores frames and what would take it off disk. `--workers 1`
|
|
126
|
+
* is offered only when a one-worker run of the same render would stream.
|
|
127
|
+
*/
|
|
128
|
+
export declare function diskCaptureRemedy(cfg: EngineConfig, outputFormat: NonNullable<RenderConfig["format"]>, durationSeconds: number, workerCount: number): string;
|
|
129
|
+
export declare function diskCaptureShortfallError(needBytes: number, freeBytes: number, framesDir: string, basis: "estimate" | "measured", remedy: string): Error;
|
|
130
|
+
export declare function assertDiskCaptureHeadroom(framesDir: string, totalFrames: number, captureOptions: CaptureOptions, remedy: string, freeDiskBytes?: (path: string) => number | null): void;
|
|
126
131
|
/** Frame bytes written so far: the merged frames dir plus every capture attempt's worker dirs. */
|
|
127
132
|
export declare function measureCaptureFrameBytes(workDir: string, framesDir: string): number;
|
|
128
133
|
/** Per-frame callback: after the sample frames, throws if the projected remainder exceeds 90% of free space. */
|
|
@@ -130,6 +135,7 @@ export declare function createDiskCaptureProjection(input: {
|
|
|
130
135
|
framesDir: string;
|
|
131
136
|
totalFrames: number;
|
|
132
137
|
measureBytes: () => number;
|
|
138
|
+
remedy: string;
|
|
133
139
|
freeDiskBytes?: (path: string) => number | null;
|
|
134
140
|
}): (capturedFrames: number) => void;
|
|
135
141
|
export declare function runCaptureStage(input: CaptureStageInput): Promise<CaptureStageResult>;
|
|
@@ -1152,8 +1152,8 @@ export declare function closeOrphanedProbeForRetry(probe: CaptureSession, closer
|
|
|
1152
1152
|
*/
|
|
1153
1153
|
export declare function resolveParallelCaptureMode(args: {
|
|
1154
1154
|
platform: NodeJS.Platform;
|
|
1155
|
-
/**
|
|
1156
|
-
|
|
1155
|
+
/** The chrome-headless-shell binary resolved for this render, if any. */
|
|
1156
|
+
headlessShellPath: string | undefined;
|
|
1157
1157
|
forceScreenshot: boolean;
|
|
1158
1158
|
deviceScaleFactor?: number;
|
|
1159
1159
|
}): "screenshot" | "beginframe";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.183",
|
|
4
4
|
"description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"postcss": "^8.4.0",
|
|
36
36
|
"puppeteer": "^25.2.1",
|
|
37
37
|
"puppeteer-core": "^25.2.1",
|
|
38
|
-
"@hyperframes/
|
|
39
|
-
"@hyperframes/
|
|
40
|
-
"@hyperframes/parsers": "0.8.
|
|
41
|
-
"@hyperframes/
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/core": "0.8.183",
|
|
39
|
+
"@hyperframes/engine": "0.8.183",
|
|
40
|
+
"@hyperframes/parsers": "0.8.183",
|
|
41
|
+
"@hyperframes/studio-server": "0.8.183",
|
|
42
|
+
"@hyperframes/lint": "0.8.183"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.0.10",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.11",
|
|
51
|
-
"@hyperframes/aws-lambda": "0.8.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.8.183"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|