@hyperframes/producer 0.8.189 → 0.8.191
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 +1 -0
- package/dist/distributed.js +75 -17
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +61 -61
- package/dist/index.js +76 -30
- package/dist/public-server.js +40 -15
- package/dist/services/distributed/audioSidecar.d.ts +10 -0
- package/dist/services/distributed/chunkFrames.d.ts +2 -2
- package/dist/services/distributed/plan.d.ts +2 -0
- package/dist/services/distributed/renderChunk.d.ts +7 -1
- package/dist/services/render/artifactTransaction.d.ts +14 -11
- package/dist/services/render/frameCacheBudget.d.ts +3 -0
- package/dist/services/render/stages/audioStage.d.ts +1 -0
- package/package.json +7 -7
package/dist/distributed.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export { muxChunkWithAudio } from "./services/distributed/chunkAudio.js";
|
|
|
39
39
|
export { buildStitchedHeader, readMp4Layout, type Mp4Layout, type ReadRange, } from "./services/distributed/mp4Stitch.js";
|
|
40
40
|
export { InvalidConfigError, type SerializableDistributedRenderConfig, validateDistributedRenderConfig, validateVariablesPayload, } from "./services/distributed/renderConfigValidation.js";
|
|
41
41
|
export { hashProjectDir, listSiteFiles } from "./services/distributed/projectHash.js";
|
|
42
|
+
export { AUDIO_SIDECAR_MIN_BYTES, buildAudioSidecar, wantsAudioSidecar, type AudioSidecarOutcome, } from "./services/distributed/audioSidecar.js";
|
|
42
43
|
export { CURRENT_PLAN_PROTOCOL, DISTRIBUTED_RENDER_CAPABILITIES, getDistributedRenderCapabilities, PLAN_ARTIFACT_LAYOUT, PLAN_HASH_SCHEMA, PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2, PLAN_PROTOCOL_UNSUPPORTED, PLAN_SCHEMA_VERSION, PLAN_V2_ARTIFACT_LAYOUT, PLAN_V2_HASH_SCHEMA, PLAN_V2_SCHEMA_VERSION, PlanProtocolUnsupportedError, readPlanProtocol, readPlanProtocolV1, type DistributedRenderCapabilities, type PlanProtocolConsumerCapabilities, type PlanProtocolDescriptor, type PlanProtocolV1Descriptor, type PlanProtocolV2Descriptor, type SupportedPlanProtocolDescriptor, } from "./services/distributed/planProtocol.js";
|
|
43
44
|
export { PlanVideosMetadataError, type DistributedFormat } from "./services/distributed/shared.js";
|
|
44
45
|
export { isPlanAudioArtifactPath, PLAN_AUDIO_LEGACY_RELATIVE_PATH, PLAN_AUDIO_RELATIVE_PATH, resolvePlanAudioPath, } from "./services/distributed/shared.js";
|
package/dist/distributed.js
CHANGED
|
@@ -1097,6 +1097,14 @@ function materializeExtractedFramesForCompiledDir(extracted, compiledDir, option
|
|
|
1097
1097
|
}
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
|
+
// src/services/render/frameCacheBudget.ts
|
|
1101
|
+
function frameCacheOptions(cfg, workers) {
|
|
1102
|
+
return {
|
|
1103
|
+
frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
|
|
1104
|
+
frameDataUriCacheBytesLimitMb: Math.floor(cfg.frameDataUriCacheBytesLimitMb / (workers || 1))
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1100
1108
|
// src/utils/errorMessage.ts
|
|
1101
1109
|
function normalizeErrorMessage(error) {
|
|
1102
1110
|
if (error instanceof Error) return error.message;
|
|
@@ -3387,7 +3395,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3387
3395
|
);
|
|
3388
3396
|
if (analysis.isProblematic) {
|
|
3389
3397
|
defaultLogger.warn(
|
|
3390
|
-
`[Compiler] WARNING: Video "${video.id}" has sparse keyframes (max interval: ${analysis.maxIntervalSeconds}s).
|
|
3398
|
+
`[Compiler] WARNING: Video "${video.id}" has sparse keyframes (max interval: ${analysis.maxIntervalSeconds}s). Long GOPs can make preview seeking slower; rendering is unaffected. If preview seeking is slow, re-encode with: ${reencode}`
|
|
3391
3399
|
);
|
|
3392
3400
|
}
|
|
3393
3401
|
if (metadata.isVFR) {
|
|
@@ -5438,6 +5446,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
5438
5446
|
audios,
|
|
5439
5447
|
masterFxChain,
|
|
5440
5448
|
urlByAbsentLocalPath,
|
|
5449
|
+
audioSidecarUrlByLocalPath,
|
|
5441
5450
|
abortSignal,
|
|
5442
5451
|
assertNotAborted,
|
|
5443
5452
|
log
|
|
@@ -5461,6 +5470,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
5461
5470
|
audioGain,
|
|
5462
5471
|
masterFxChain,
|
|
5463
5472
|
urlByAbsentLocalPath,
|
|
5473
|
+
audioSidecarUrlByLocalPath,
|
|
5464
5474
|
keepEncoderInputAt: pcmSourcePath
|
|
5465
5475
|
},
|
|
5466
5476
|
compiledDir
|
|
@@ -6847,12 +6857,13 @@ var RenderChunkValidationError = class extends Error {
|
|
|
6847
6857
|
this.code = code;
|
|
6848
6858
|
}
|
|
6849
6859
|
};
|
|
6850
|
-
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
6851
|
-
if (!(frameLookup instanceof Promise))
|
|
6860
|
+
function createChunkVideoFrameInjectorFactory(frameLookup, options) {
|
|
6861
|
+
if (!(frameLookup instanceof Promise))
|
|
6862
|
+
return () => createVideoFrameInjector2(frameLookup, options);
|
|
6852
6863
|
return () => {
|
|
6853
6864
|
let hook;
|
|
6854
6865
|
return async (page, time, heldVideoTime) => {
|
|
6855
|
-
hook ??= createVideoFrameInjector2(await frameLookup);
|
|
6866
|
+
hook ??= createVideoFrameInjector2(await frameLookup, options);
|
|
6856
6867
|
await hook?.(page, time, heldVideoTime);
|
|
6857
6868
|
};
|
|
6858
6869
|
};
|
|
@@ -8574,6 +8585,11 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8574
8585
|
const localPathByUrl = new Map(
|
|
8575
8586
|
remoteFiles.map(([rel, file]) => [file.url, remoteLocalPath(rel)])
|
|
8576
8587
|
);
|
|
8588
|
+
const audioSidecarUrlByLocalPath = new Map(
|
|
8589
|
+
remoteFiles.flatMap(
|
|
8590
|
+
([rel, file]) => file.audioSidecarUrl ? [[remoteLocalPath(rel), file.audioSidecarUrl]] : []
|
|
8591
|
+
)
|
|
8592
|
+
);
|
|
8577
8593
|
stageStarted = Date.now();
|
|
8578
8594
|
const compileResult = await runCompileStage({
|
|
8579
8595
|
urlByAbsentLocalPath,
|
|
@@ -8658,6 +8674,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8658
8674
|
finalCompiledDir,
|
|
8659
8675
|
remoteFiles,
|
|
8660
8676
|
urlByAbsentLocalPath,
|
|
8677
|
+
audioSidecarUrlByLocalPath,
|
|
8661
8678
|
localPathByUrl,
|
|
8662
8679
|
compileResult,
|
|
8663
8680
|
compiled,
|
|
@@ -8675,6 +8692,7 @@ function mixPlanAudio(c, compiledDir) {
|
|
|
8675
8692
|
workDir: c.workDir,
|
|
8676
8693
|
compiledDir,
|
|
8677
8694
|
urlByAbsentLocalPath: c.urlByAbsentLocalPath,
|
|
8695
|
+
audioSidecarUrlByLocalPath: c.audioSidecarUrlByLocalPath,
|
|
8678
8696
|
totalFrames: c.totalFrames,
|
|
8679
8697
|
fps: c.job.config.fps,
|
|
8680
8698
|
ffmpegProcessTimeout: c.cfg.ffmpegProcessTimeout,
|
|
@@ -10283,6 +10301,22 @@ async function createVerifiedDistributedCaptureSession(serverUrl, framesDir, cap
|
|
|
10283
10301
|
function isCaptureMode(value) {
|
|
10284
10302
|
return value === "beginframe" || value === "screenshot" || value === "drawelement";
|
|
10285
10303
|
}
|
|
10304
|
+
var MixedCaptureModesError = class extends Error {
|
|
10305
|
+
constructor(modes) {
|
|
10306
|
+
super(`[renderChunk] capture workers reported inconsistent modes: ${modes.join(",")}`);
|
|
10307
|
+
this.modes = modes;
|
|
10308
|
+
this.name = "MixedCaptureModesError";
|
|
10309
|
+
}
|
|
10310
|
+
};
|
|
10311
|
+
function observedChunkCaptureMode(perfs) {
|
|
10312
|
+
const modes = [...new Set(perfs.map((perf) => perf.captureMode))];
|
|
10313
|
+
if (modes.length > 1) throw new MixedCaptureModesError(modes);
|
|
10314
|
+
const [mode] = modes;
|
|
10315
|
+
if (!mode || !isCaptureMode(mode)) {
|
|
10316
|
+
throw new Error(`[renderChunk] capture workers reported an invalid mode: ${mode ?? "<none>"}`);
|
|
10317
|
+
}
|
|
10318
|
+
return mode;
|
|
10319
|
+
}
|
|
10286
10320
|
function shouldRetryChunkCaptureWithScreenshot(error) {
|
|
10287
10321
|
const failure = classifyCaptureFailure4(error);
|
|
10288
10322
|
if (failure.kind === "cancelled" || failure.kind === "memory_exhaustion") return false;
|
|
@@ -10580,7 +10614,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10580
10614
|
) : null;
|
|
10581
10615
|
videoFrameLookup?.catch(() => {
|
|
10582
10616
|
});
|
|
10583
|
-
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(
|
|
10617
|
+
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(
|
|
10618
|
+
videoFrameLookup,
|
|
10619
|
+
frameCacheOptions(cfg, chunkWorkerCount)
|
|
10620
|
+
);
|
|
10584
10621
|
const captureStarted = Date.now();
|
|
10585
10622
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
10586
10623
|
forceScreenshot: forceScreenshotForChunk,
|
|
@@ -10634,17 +10671,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10634
10671
|
},
|
|
10635
10672
|
frameRange: range
|
|
10636
10673
|
});
|
|
10637
|
-
|
|
10638
|
-
if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
|
|
10639
|
-
throw new Error(
|
|
10640
|
-
`[renderChunk] capture workers reported invalid or inconsistent modes: ${[...observedModes].join(",") || "<none>"}`
|
|
10641
|
-
);
|
|
10642
|
-
}
|
|
10643
|
-
const [observedMode] = observedModes;
|
|
10644
|
-
if (!observedMode || !isCaptureMode(observedMode)) {
|
|
10645
|
-
throw new Error("[renderChunk] capture completed without an observed mode");
|
|
10646
|
-
}
|
|
10647
|
-
return observedMode;
|
|
10674
|
+
return observedChunkCaptureMode(capturePerfs);
|
|
10648
10675
|
},
|
|
10649
10676
|
resetForScreenshotRetry: () => {
|
|
10650
10677
|
rmSync13(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
@@ -11146,7 +11173,36 @@ function hashProjectDir(projectDir, files = listSiteFiles(projectDir)) {
|
|
|
11146
11173
|
}
|
|
11147
11174
|
return hash.digest("hex").slice(0, 16);
|
|
11148
11175
|
}
|
|
11176
|
+
|
|
11177
|
+
// src/services/distributed/audioSidecar.ts
|
|
11178
|
+
import { extractAudioMetadata as extractAudioMetadata4, runFfmpeg as runFfmpeg8 } from "@hyperframes/engine";
|
|
11179
|
+
import { extname as extname5 } from "node:path";
|
|
11180
|
+
var AUDIO_SIDECAR_MIN_BYTES = 32 * 1024 * 1024;
|
|
11181
|
+
var AUDIO_SIDECAR_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".m4v"]);
|
|
11182
|
+
function wantsAudioSidecar(path, sizeBytes) {
|
|
11183
|
+
return sizeBytes >= AUDIO_SIDECAR_MIN_BYTES && AUDIO_SIDECAR_SOURCE_EXTENSIONS.has(extname5(path).toLowerCase());
|
|
11184
|
+
}
|
|
11185
|
+
async function buildAudioSidecar(inputPath, outputPath, signal) {
|
|
11186
|
+
const audio = await extractAudioMetadata4(inputPath, { signal }).catch(() => null);
|
|
11187
|
+
if (!audio) return await ffmpegIsMissing(signal) ? "unavailable" : "none";
|
|
11188
|
+
if (audio.audioStreamCount !== 1 || (audio.audioStreamLeadSeconds ?? 0) > 0) return "none";
|
|
11189
|
+
const result = await runFfmpeg8(
|
|
11190
|
+
["-v", "error", "-y", "-i", inputPath, "-map", "0:a:0", "-vn", "-c:a", "copy"].concat([
|
|
11191
|
+
"-movflags",
|
|
11192
|
+
"+faststart",
|
|
11193
|
+
"-f",
|
|
11194
|
+
"mp4",
|
|
11195
|
+
outputPath
|
|
11196
|
+
]),
|
|
11197
|
+
{ signal }
|
|
11198
|
+
);
|
|
11199
|
+
if (result.success) return "built";
|
|
11200
|
+
return isMissing(result) ? "unavailable" : "none";
|
|
11201
|
+
}
|
|
11202
|
+
var isMissing = (result) => result.error?.code === "ENOENT";
|
|
11203
|
+
var ffmpegIsMissing = async (signal) => isMissing(await runFfmpeg8(["-version"], { signal }));
|
|
11149
11204
|
export {
|
|
11205
|
+
AUDIO_SIDECAR_MIN_BYTES,
|
|
11150
11206
|
CHUNK_EXCEEDS_WORKER_TIMEOUT,
|
|
11151
11207
|
CHUNK_FRAME_RANGE_INVALID,
|
|
11152
11208
|
CURRENT_PLAN_PROTOCOL,
|
|
@@ -11189,6 +11245,7 @@ export {
|
|
|
11189
11245
|
applyRuntimeEnvSnapshot,
|
|
11190
11246
|
assemble,
|
|
11191
11247
|
assembleV2,
|
|
11248
|
+
buildAudioSidecar,
|
|
11192
11249
|
buildChunkSlices,
|
|
11193
11250
|
buildPlanAudio,
|
|
11194
11251
|
buildStitchedHeader,
|
|
@@ -11224,5 +11281,6 @@ export {
|
|
|
11224
11281
|
stitchesSeparateAudio,
|
|
11225
11282
|
validateDistributedRenderConfig,
|
|
11226
11283
|
validatePlanV2MaterializedTarget,
|
|
11227
|
-
validateVariablesPayload
|
|
11284
|
+
validateVariablesPayload,
|
|
11285
|
+
wantsAudioSidecar
|
|
11228
11286
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "cd5f930c1b25aea193005449687af10c1b706fc870e51d1ff160dd46751d1581",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|