@hyperframes/producer 0.8.227 → 0.8.228
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.js +303 -242
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +61 -61
- package/dist/index.js +362 -301
- package/dist/public-server.js +155 -94
- package/dist/services/render/audioDeliveryLimit.d.ts +26 -0
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import {
|
|
5
|
-
existsSync as
|
|
5
|
+
existsSync as existsSync21,
|
|
6
6
|
mkdirSync as mkdirSync11,
|
|
7
7
|
statSync as statSync10,
|
|
8
8
|
mkdtempSync as mkdtempSync4,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
rmSync as rmSync11,
|
|
11
11
|
createReadStream as createReadStream3
|
|
12
12
|
} from "node:fs";
|
|
13
|
-
import { resolve as resolve10, dirname as
|
|
13
|
+
import { resolve as resolve10, dirname as dirname14, join as join32 } from "node:path";
|
|
14
14
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
15
15
|
import { parseArgs } from "node:util";
|
|
16
16
|
import crypto from "node:crypto";
|
|
@@ -22,7 +22,7 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
|
|
|
22
22
|
// src/services/renderOrchestrator.ts
|
|
23
23
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
24
24
|
import {
|
|
25
|
-
existsSync as
|
|
25
|
+
existsSync as existsSync18,
|
|
26
26
|
mkdirSync as mkdirSync10,
|
|
27
27
|
readdirSync as readdirSync5,
|
|
28
28
|
rmSync as rmSync10,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
augmentProtocolTimeoutError,
|
|
78
78
|
augmentPageNavigationTimeoutError
|
|
79
79
|
} from "@hyperframes/engine";
|
|
80
|
-
import { join as
|
|
80
|
+
import { join as join28, dirname as dirname12, resolve as resolve8 } from "path";
|
|
81
81
|
import { totalmem as totalmem2 } from "node:os";
|
|
82
82
|
import { randomUUID } from "crypto";
|
|
83
83
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -9809,7 +9809,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
9809
9809
|
}
|
|
9810
9810
|
|
|
9811
9811
|
// src/services/render/stages/audioStage.ts
|
|
9812
|
-
import { join as
|
|
9812
|
+
import { join as join21 } from "node:path";
|
|
9813
9813
|
import {
|
|
9814
9814
|
MIXED_AUDIO_FILENAME,
|
|
9815
9815
|
audioFailureDiagnostics,
|
|
@@ -9818,6 +9818,16 @@ import {
|
|
|
9818
9818
|
collectScratchLeftovers
|
|
9819
9819
|
} from "@hyperframes/engine";
|
|
9820
9820
|
|
|
9821
|
+
// src/services/render/audioDeliveryLimit.ts
|
|
9822
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
9823
|
+
import { dirname as dirname9, join as join20 } from "node:path";
|
|
9824
|
+
import {
|
|
9825
|
+
applyAudioFxChain,
|
|
9826
|
+
describeFfmpegFailure as describeFfmpegFailure3,
|
|
9827
|
+
encodeAac as encodeAac2,
|
|
9828
|
+
renameWithRetry as renameWithRetry6
|
|
9829
|
+
} from "@hyperframes/engine";
|
|
9830
|
+
|
|
9821
9831
|
// src/services/render/audioPadTrim.ts
|
|
9822
9832
|
import { spawn } from "node:child_process";
|
|
9823
9833
|
import { mkdtempSync as mkdtempSync3 } from "node:fs";
|
|
@@ -10278,6 +10288,48 @@ async function runFfprobeJson(args, signal) {
|
|
|
10278
10288
|
}
|
|
10279
10289
|
}
|
|
10280
10290
|
|
|
10291
|
+
// src/services/render/audioDeliveryLimit.ts
|
|
10292
|
+
var LIMIT_CEILING_DBTP = AAC_DELIVERY_TRUE_PEAK_DBFS - 2;
|
|
10293
|
+
var LIMIT_CHAIN = {
|
|
10294
|
+
version: 1,
|
|
10295
|
+
nodes: [{ type: "truepeak", id: "delivery", params: { ceiling: LIMIT_CEILING_DBTP } }]
|
|
10296
|
+
};
|
|
10297
|
+
async function limitDeliveryPeaks(input, deps = {
|
|
10298
|
+
measure: measureTruePeakDbfs,
|
|
10299
|
+
applyChain: applyAudioFxChain,
|
|
10300
|
+
encode: encodeAac2,
|
|
10301
|
+
rename: renameWithRetry6,
|
|
10302
|
+
sourceExists: existsSync13
|
|
10303
|
+
}) {
|
|
10304
|
+
const { audioPath, sourcePath, signal, timeoutMs } = input;
|
|
10305
|
+
if (!deps.sourceExists(sourcePath)) return { success: true };
|
|
10306
|
+
const peak = await deps.measure(audioPath, { timeoutMs, signal });
|
|
10307
|
+
if (peak <= AAC_DELIVERY_TRUE_PEAK_DBFS) return { success: true };
|
|
10308
|
+
const limitedPath = join20(dirname9(sourcePath), "audio-source-limited.wav");
|
|
10309
|
+
try {
|
|
10310
|
+
await deps.applyChain(sourcePath, LIMIT_CHAIN, limitedPath, { trackId: "delivery", signal });
|
|
10311
|
+
} catch (error) {
|
|
10312
|
+
if (signal?.aborted) throw error;
|
|
10313
|
+
input.warn?.("[audio] true-peak limit failed, the whole-mix cut applies", {
|
|
10314
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10315
|
+
});
|
|
10316
|
+
return { success: true };
|
|
10317
|
+
}
|
|
10318
|
+
const encoded = await deps.encode(limitedPath, audioPath, input.targetDurationSeconds, {
|
|
10319
|
+
signal,
|
|
10320
|
+
timeout: timeoutMs
|
|
10321
|
+
});
|
|
10322
|
+
if (!encoded.success) {
|
|
10323
|
+
return {
|
|
10324
|
+
success: false,
|
|
10325
|
+
error: `[audioDeliveryLimit] ${describeFfmpegFailure3(encoded, timeoutMs)}`,
|
|
10326
|
+
failureReason: encoded.failureReason
|
|
10327
|
+
};
|
|
10328
|
+
}
|
|
10329
|
+
deps.rename(limitedPath, sourcePath, { copyFallback: true });
|
|
10330
|
+
return { success: true };
|
|
10331
|
+
}
|
|
10332
|
+
|
|
10281
10333
|
// src/services/render/stages/audioStage.ts
|
|
10282
10334
|
function assertAudioNotInterrupted(failures) {
|
|
10283
10335
|
const interrupted = failures?.find((failure) => failure.reason === "external_interruption");
|
|
@@ -10301,13 +10353,22 @@ function runAudioStage(input) {
|
|
|
10301
10353
|
}
|
|
10302
10354
|
async function runAudioStageScoped(input) {
|
|
10303
10355
|
const stageStart = Date.now();
|
|
10304
|
-
const pcmSourcePath =
|
|
10356
|
+
const pcmSourcePath = join21(input.workDir, "audio-source.wav");
|
|
10305
10357
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
10306
10358
|
try {
|
|
10307
10359
|
const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
|
|
10308
10360
|
let audioLoweredDb;
|
|
10309
10361
|
if (result.hasAudio) {
|
|
10310
10362
|
const guardStart = Date.now();
|
|
10363
|
+
const limited = await limitDeliveryPeaks({
|
|
10364
|
+
audioPath: result.audioOutputPath,
|
|
10365
|
+
sourcePath: pcmSourcePath,
|
|
10366
|
+
targetDurationSeconds: duration,
|
|
10367
|
+
signal: input.abortSignal,
|
|
10368
|
+
timeoutMs: input.ffmpegProcessTimeout,
|
|
10369
|
+
warn: (message, details) => input.log?.warn?.(message, details)
|
|
10370
|
+
});
|
|
10371
|
+
if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
|
|
10311
10372
|
const peak = await constrainAacTruePeak({
|
|
10312
10373
|
audioPath: result.audioOutputPath,
|
|
10313
10374
|
sourcePath: pcmSourcePath,
|
|
@@ -10339,7 +10400,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10339
10400
|
assertNotAborted,
|
|
10340
10401
|
log
|
|
10341
10402
|
} = input;
|
|
10342
|
-
const audioOutputPath =
|
|
10403
|
+
const audioOutputPath = join21(workDir, MIXED_AUDIO_FILENAME);
|
|
10343
10404
|
let hasAudio = false;
|
|
10344
10405
|
let audioError;
|
|
10345
10406
|
let audioFailures;
|
|
@@ -10350,7 +10411,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10350
10411
|
audioResult = await processCompositionAudio(
|
|
10351
10412
|
audios,
|
|
10352
10413
|
projectDir,
|
|
10353
|
-
|
|
10414
|
+
join21(workDir, "audio-work"),
|
|
10354
10415
|
audioOutputPath,
|
|
10355
10416
|
duration,
|
|
10356
10417
|
abortSignal,
|
|
@@ -10418,8 +10479,8 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10418
10479
|
}
|
|
10419
10480
|
|
|
10420
10481
|
// src/services/render/stages/captureStage.ts
|
|
10421
|
-
import { existsSync as
|
|
10422
|
-
import { join as
|
|
10482
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
10483
|
+
import { join as join22 } from "node:path";
|
|
10423
10484
|
import {
|
|
10424
10485
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
10425
10486
|
frameFileExtension,
|
|
@@ -10489,16 +10550,16 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
|
|
|
10489
10550
|
}
|
|
10490
10551
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
10491
10552
|
let total = sampleDirectoryBytes(framesDir);
|
|
10492
|
-
if (!
|
|
10553
|
+
if (!existsSync14(workDir)) return total;
|
|
10493
10554
|
for (const attempt of readdirSync3(workDir)) {
|
|
10494
10555
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
10495
|
-
total += sampleDirectoryBytes(
|
|
10556
|
+
total += sampleDirectoryBytes(join22(workDir, attempt));
|
|
10496
10557
|
continue;
|
|
10497
10558
|
}
|
|
10498
10559
|
if (!/^capture-attempt-\d+$/.test(attempt)) continue;
|
|
10499
|
-
const attemptDir =
|
|
10560
|
+
const attemptDir = join22(workDir, attempt);
|
|
10500
10561
|
for (const name of readdirSync3(attemptDir)) {
|
|
10501
|
-
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(
|
|
10562
|
+
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join22(attemptDir, name));
|
|
10502
10563
|
}
|
|
10503
10564
|
}
|
|
10504
10565
|
return total;
|
|
@@ -10792,8 +10853,8 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
10792
10853
|
}
|
|
10793
10854
|
|
|
10794
10855
|
// src/services/render/stages/captureHdrStage.ts
|
|
10795
|
-
import { existsSync as
|
|
10796
|
-
import { join as
|
|
10856
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8 } from "node:fs";
|
|
10857
|
+
import { join as join26 } from "node:path";
|
|
10797
10858
|
import {
|
|
10798
10859
|
closeCaptureSession as closeCaptureSession7,
|
|
10799
10860
|
cloneCaptureWarnings as cloneCaptureWarnings2,
|
|
@@ -10865,7 +10926,7 @@ function createHdrImageTransferCache(options = {}) {
|
|
|
10865
10926
|
}
|
|
10866
10927
|
|
|
10867
10928
|
// src/services/render/stages/captureHdrSequentialLoop.ts
|
|
10868
|
-
import { join as
|
|
10929
|
+
import { join as join23 } from "node:path";
|
|
10869
10930
|
import {
|
|
10870
10931
|
TRANSITIONS,
|
|
10871
10932
|
crossfade
|
|
@@ -10990,7 +11051,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
10990
11051
|
);
|
|
10991
11052
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
10992
11053
|
writeFileExclusiveSync(
|
|
10993
|
-
|
|
11054
|
+
join23(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
10994
11055
|
normalCanvas
|
|
10995
11056
|
);
|
|
10996
11057
|
}
|
|
@@ -11022,7 +11083,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
11022
11083
|
}
|
|
11023
11084
|
|
|
11024
11085
|
// src/services/render/stages/captureHdrHybridLoop.ts
|
|
11025
|
-
import { join as
|
|
11086
|
+
import { join as join25 } from "node:path";
|
|
11026
11087
|
import {
|
|
11027
11088
|
TRANSITIONS as TRANSITIONS2,
|
|
11028
11089
|
closeCaptureSession as closeCaptureSession6,
|
|
@@ -11036,9 +11097,9 @@ import {
|
|
|
11036
11097
|
// src/services/shaderTransitionWorkerPool.ts
|
|
11037
11098
|
import { Worker } from "node:worker_threads";
|
|
11038
11099
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
11039
|
-
import { dirname as
|
|
11100
|
+
import { dirname as dirname10, join as join24 } from "node:path";
|
|
11040
11101
|
import { createRequire } from "node:module";
|
|
11041
|
-
import { existsSync as
|
|
11102
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
11042
11103
|
import { cpus as cpus2 } from "node:os";
|
|
11043
11104
|
function resolveWorkerEntry(explicit) {
|
|
11044
11105
|
if (explicit && explicit.length > 0) {
|
|
@@ -11049,10 +11110,10 @@ function resolveWorkerEntry(explicit) {
|
|
|
11049
11110
|
const isTs = override.endsWith(".ts");
|
|
11050
11111
|
return { path: override, isTs };
|
|
11051
11112
|
}
|
|
11052
|
-
const moduleDir =
|
|
11053
|
-
const jsPath =
|
|
11054
|
-
if (
|
|
11055
|
-
const tsPath =
|
|
11113
|
+
const moduleDir = dirname10(fileURLToPath3(import.meta.url));
|
|
11114
|
+
const jsPath = join24(moduleDir, "shaderTransitionWorker.js");
|
|
11115
|
+
if (existsSync15(jsPath)) return { path: jsPath, isTs: false };
|
|
11116
|
+
const tsPath = join24(moduleDir, "shaderTransitionWorker.ts");
|
|
11056
11117
|
return { path: tsPath, isTs: true };
|
|
11057
11118
|
}
|
|
11058
11119
|
function buildExecArgv(entryIsTs) {
|
|
@@ -11420,7 +11481,7 @@ async function runHybridLayeredFrameLoop(input) {
|
|
|
11420
11481
|
);
|
|
11421
11482
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
11422
11483
|
writeFileExclusiveSync(
|
|
11423
|
-
|
|
11484
|
+
join25(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
11424
11485
|
canvas
|
|
11425
11486
|
);
|
|
11426
11487
|
}
|
|
@@ -11611,8 +11672,8 @@ async function runCaptureHdrStage(input) {
|
|
|
11611
11672
|
if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
|
|
11612
11673
|
}
|
|
11613
11674
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
11614
|
-
const debugDumpDir = debugDumpEnabled ?
|
|
11615
|
-
if (debugDumpDir && !
|
|
11675
|
+
const debugDumpDir = debugDumpEnabled ? join26(framesDir, "debug-composite") : null;
|
|
11676
|
+
if (debugDumpDir && !existsSync16(debugDumpDir)) {
|
|
11616
11677
|
mkdirSync8(debugDumpDir, { recursive: true });
|
|
11617
11678
|
}
|
|
11618
11679
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
@@ -11769,7 +11830,7 @@ async function runCaptureHdrStage(input) {
|
|
|
11769
11830
|
// src/services/render/stages/encodeStage.ts
|
|
11770
11831
|
import {
|
|
11771
11832
|
copyFileSync as copyFileSync4,
|
|
11772
|
-
existsSync as
|
|
11833
|
+
existsSync as existsSync17,
|
|
11773
11834
|
mkdirSync as mkdirSync9,
|
|
11774
11835
|
readdirSync as readdirSync4,
|
|
11775
11836
|
readFileSync as readFileSync7,
|
|
@@ -11778,7 +11839,7 @@ import {
|
|
|
11778
11839
|
writeFileSync as writeFileSync5
|
|
11779
11840
|
} from "node:fs";
|
|
11780
11841
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
11781
|
-
import { dirname as
|
|
11842
|
+
import { dirname as dirname11, join as join27 } from "node:path";
|
|
11782
11843
|
import {
|
|
11783
11844
|
encodeFramesChunkedConcat,
|
|
11784
11845
|
encodeFramesFromDir,
|
|
@@ -11903,7 +11964,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11903
11964
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
11904
11965
|
if (failure) return failure;
|
|
11905
11966
|
}
|
|
11906
|
-
const fileSize =
|
|
11967
|
+
const fileSize = existsSync17(outputPath) ? statSync7(outputPath).size : 0;
|
|
11907
11968
|
return {
|
|
11908
11969
|
success: true,
|
|
11909
11970
|
outputPath,
|
|
@@ -11989,7 +12050,7 @@ function encodeWhileCapturing(input, totalFrames) {
|
|
|
11989
12050
|
await new Promise((resolve11) => wake = resolve11);
|
|
11990
12051
|
}
|
|
11991
12052
|
if (signal.aborted) return;
|
|
11992
|
-
const mergedPath =
|
|
12053
|
+
const mergedPath = join27(input.framesDir, formatCaptureFrameName(next, "jpg"));
|
|
11993
12054
|
const frame = await readLandedFrame(landed.get(next), mergedPath);
|
|
11994
12055
|
landed.delete(next);
|
|
11995
12056
|
if (!await stream.writeFrame(frame)) {
|
|
@@ -12064,7 +12125,7 @@ async function runEncodeStage(input) {
|
|
|
12064
12125
|
const stage5Start = Date.now();
|
|
12065
12126
|
if (isPngSequence) {
|
|
12066
12127
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
12067
|
-
if (!
|
|
12128
|
+
if (!existsSync17(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
12068
12129
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
12069
12130
|
if (captured.length === 0) {
|
|
12070
12131
|
throw new Error(
|
|
@@ -12072,11 +12133,11 @@ async function runEncodeStage(input) {
|
|
|
12072
12133
|
);
|
|
12073
12134
|
}
|
|
12074
12135
|
captured.forEach((name, i) => {
|
|
12075
|
-
const dst =
|
|
12076
|
-
copyFileSync4(
|
|
12136
|
+
const dst = join27(outputPath, formatExportFrameName(i, "png"));
|
|
12137
|
+
copyFileSync4(join27(framesDir, name), dst);
|
|
12077
12138
|
});
|
|
12078
|
-
if (hasAudio && audioOutputPath &&
|
|
12079
|
-
copyFileSync4(audioOutputPath,
|
|
12139
|
+
if (hasAudio && audioOutputPath && existsSync17(audioOutputPath)) {
|
|
12140
|
+
copyFileSync4(audioOutputPath, join27(outputPath, MIXED_AUDIO_FILENAME2));
|
|
12080
12141
|
log.info(
|
|
12081
12142
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
12082
12143
|
);
|
|
@@ -12096,7 +12157,7 @@ async function runEncodeStage(input) {
|
|
|
12096
12157
|
const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
|
|
12097
12158
|
fps: job.config.fps,
|
|
12098
12159
|
loop,
|
|
12099
|
-
palettePath:
|
|
12160
|
+
palettePath: join27(dirname11(videoOnlyPath), "gif-palette.png"),
|
|
12100
12161
|
preserveAlpha: needsAlpha,
|
|
12101
12162
|
signal: abortSignal,
|
|
12102
12163
|
timeout: engineCfg.ffmpegEncodeTimeout
|
|
@@ -12306,7 +12367,7 @@ function sampleDirectoryBytes(dir) {
|
|
|
12306
12367
|
continue;
|
|
12307
12368
|
}
|
|
12308
12369
|
for (const name of entries) {
|
|
12309
|
-
const full =
|
|
12370
|
+
const full = join28(current, name);
|
|
12310
12371
|
try {
|
|
12311
12372
|
const st = statSync8(full);
|
|
12312
12373
|
if (st.isDirectory()) {
|
|
@@ -12455,8 +12516,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
12455
12516
|
const ranges = [];
|
|
12456
12517
|
let rangeStart = null;
|
|
12457
12518
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12458
|
-
const framePath =
|
|
12459
|
-
const missing = !
|
|
12519
|
+
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12520
|
+
const missing = !existsSync18(framePath) || statSync8(framePath).size <= 8;
|
|
12460
12521
|
if (missing && rangeStart === null) {
|
|
12461
12522
|
rangeStart = frameIndex;
|
|
12462
12523
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -12472,7 +12533,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
12472
12533
|
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
|
|
12473
12534
|
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
12474
12535
|
const batches = [];
|
|
12475
|
-
const outputDirFor = (batchIndex, workerId) =>
|
|
12536
|
+
const outputDirFor = (batchIndex, workerId) => join28(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
|
|
12476
12537
|
if (stride > 1) {
|
|
12477
12538
|
const spans = /* @__PURE__ */ new Map();
|
|
12478
12539
|
for (const range of ranges) {
|
|
@@ -12532,8 +12593,8 @@ function getNextRetryWorkerCount(currentWorkers) {
|
|
|
12532
12593
|
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
12533
12594
|
}
|
|
12534
12595
|
function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
|
|
12535
|
-
if (platform2 === "win32" || platform2 === "darwin") return
|
|
12536
|
-
return
|
|
12596
|
+
if (platform2 === "win32" || platform2 === "darwin") return join28(systemTempDir, "hf-render-");
|
|
12597
|
+
return join28(dirname12(outputPath), `work-${jobId}-`);
|
|
12537
12598
|
}
|
|
12538
12599
|
var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
|
|
12539
12600
|
function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
|
|
@@ -12580,8 +12641,8 @@ The composition is too large for the available memory. To reduce memory pressure
|
|
|
12580
12641
|
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
12581
12642
|
let captured = 0;
|
|
12582
12643
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12583
|
-
const framePath =
|
|
12584
|
-
if (
|
|
12644
|
+
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12645
|
+
if (existsSync18(framePath)) captured++;
|
|
12585
12646
|
}
|
|
12586
12647
|
return captured;
|
|
12587
12648
|
}
|
|
@@ -12606,7 +12667,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
12606
12667
|
reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
|
|
12607
12668
|
});
|
|
12608
12669
|
pendingTransientRetry = false;
|
|
12609
|
-
const attemptWorkDir =
|
|
12670
|
+
const attemptWorkDir = join28(options.workDir, `capture-attempt-${attempt}`);
|
|
12610
12671
|
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
12611
12672
|
missingRanges,
|
|
12612
12673
|
currentWorkers,
|
|
@@ -13236,19 +13297,19 @@ function deVerifyFallbackTelemetry(err) {
|
|
|
13236
13297
|
};
|
|
13237
13298
|
}
|
|
13238
13299
|
function resolveRenderDebugDir() {
|
|
13239
|
-
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(
|
|
13240
|
-
return
|
|
13300
|
+
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname12(fileURLToPath4(import.meta.url)), "../..");
|
|
13301
|
+
return join28(producerRoot, ".debug");
|
|
13241
13302
|
}
|
|
13242
13303
|
async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
|
|
13243
13304
|
validateHlsRenderConfig(job.config);
|
|
13244
13305
|
validateDeliveryEncoderConfig(job.config);
|
|
13245
13306
|
const debugDir = resolveRenderDebugDir();
|
|
13246
|
-
const outputDir =
|
|
13247
|
-
if (!
|
|
13248
|
-
const workDir = job.config.debug ?
|
|
13307
|
+
const outputDir = dirname12(outputPath);
|
|
13308
|
+
if (!existsSync18(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
13309
|
+
const workDir = job.config.debug ? join28(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
13249
13310
|
const pipelineStart = Date.now();
|
|
13250
13311
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
13251
|
-
const logPath = job.config.debug ?
|
|
13312
|
+
const logPath = job.config.debug ? join28(workDir, "render.log") : null;
|
|
13252
13313
|
const execution = new RenderExecutionContext({
|
|
13253
13314
|
request: { renderJobId: job.id, projectDir, outputPath },
|
|
13254
13315
|
logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
|
|
@@ -13309,7 +13370,7 @@ async function executeRenderPipeline(input) {
|
|
|
13309
13370
|
imageDecodeFailures: 0
|
|
13310
13371
|
};
|
|
13311
13372
|
let hdrPerf;
|
|
13312
|
-
const perfOutputPath =
|
|
13373
|
+
const perfOutputPath = join28(workDir, "perf-summary.json");
|
|
13313
13374
|
const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
|
|
13314
13375
|
const observability = new RenderObservabilityRecorder({
|
|
13315
13376
|
pipelineStartMs: pipelineStart,
|
|
@@ -13385,7 +13446,7 @@ async function executeRenderPipeline(input) {
|
|
|
13385
13446
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
13386
13447
|
assertNotAborted();
|
|
13387
13448
|
assertConfiguredFfmpegBinariesExist();
|
|
13388
|
-
if (!
|
|
13449
|
+
if (!existsSync18(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
13389
13450
|
if (job.config.debug) {
|
|
13390
13451
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
13391
13452
|
}
|
|
@@ -13414,16 +13475,16 @@ async function executeRenderPipeline(input) {
|
|
|
13414
13475
|
requestedWorkers: job.config.workers ?? "auto"
|
|
13415
13476
|
});
|
|
13416
13477
|
const entryFile = job.config.entryFile || "index.html";
|
|
13417
|
-
let htmlPath =
|
|
13418
|
-
if (!
|
|
13478
|
+
let htmlPath = join28(projectDir, entryFile);
|
|
13479
|
+
if (!existsSync18(htmlPath)) {
|
|
13419
13480
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
13420
13481
|
}
|
|
13421
13482
|
assertNotAborted();
|
|
13422
13483
|
const rawEntry = readTextFile2(htmlPath);
|
|
13423
13484
|
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
13424
|
-
const wrapperPath =
|
|
13425
|
-
const projectIndexPath =
|
|
13426
|
-
if (!
|
|
13485
|
+
const wrapperPath = join28(workDir, "standalone-entry.html");
|
|
13486
|
+
const projectIndexPath = join28(projectDir, "index.html");
|
|
13487
|
+
if (!existsSync18(projectIndexPath)) {
|
|
13427
13488
|
throw new Error(
|
|
13428
13489
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
13429
13490
|
);
|
|
@@ -13613,7 +13674,7 @@ async function executeRenderPipeline(input) {
|
|
|
13613
13674
|
canvas: { width, height },
|
|
13614
13675
|
page: probeSession.page,
|
|
13615
13676
|
projectDir,
|
|
13616
|
-
compiledDir:
|
|
13677
|
+
compiledDir: join28(workDir, "compiled")
|
|
13617
13678
|
});
|
|
13618
13679
|
if (videoLayerPlan.videoId === void 0) {
|
|
13619
13680
|
log.info(`[Render] Video layer passthrough off: ${videoLayerPlan.reason}`);
|
|
@@ -13631,7 +13692,7 @@ async function executeRenderPipeline(input) {
|
|
|
13631
13692
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
|
|
13632
13693
|
code: "extract_video"
|
|
13633
13694
|
});
|
|
13634
|
-
const compiledDir =
|
|
13695
|
+
const compiledDir = join28(workDir, "compiled");
|
|
13635
13696
|
const extractResult = await observeRenderStage(
|
|
13636
13697
|
observability,
|
|
13637
13698
|
"video_extract",
|
|
@@ -13743,7 +13804,7 @@ async function executeRenderPipeline(input) {
|
|
|
13743
13804
|
try {
|
|
13744
13805
|
fileServer = await createFileServer({
|
|
13745
13806
|
projectDir,
|
|
13746
|
-
compiledDir:
|
|
13807
|
+
compiledDir: join28(workDir, "compiled"),
|
|
13747
13808
|
port: 0,
|
|
13748
13809
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
13749
13810
|
fps: job.config.fps
|
|
@@ -13761,8 +13822,8 @@ async function executeRenderPipeline(input) {
|
|
|
13761
13822
|
if (!activeFileServer) {
|
|
13762
13823
|
throw new Error("File server failed to initialize before frame capture");
|
|
13763
13824
|
}
|
|
13764
|
-
const framesDir =
|
|
13765
|
-
if (!
|
|
13825
|
+
const framesDir = join28(workDir, "captured-frames");
|
|
13826
|
+
if (!existsSync18(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
13766
13827
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
13767
13828
|
code: "start_browsers"
|
|
13768
13829
|
});
|
|
@@ -14259,7 +14320,7 @@ async function executeRenderPipeline(input) {
|
|
|
14259
14320
|
hls: ".mp4"
|
|
14260
14321
|
};
|
|
14261
14322
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
14262
|
-
const videoOnlyPath =
|
|
14323
|
+
const videoOnlyPath = join28(workDir, `video-only${videoExt}`);
|
|
14263
14324
|
const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
|
|
14264
14325
|
if (usePageSideCompositingForTransitions) {
|
|
14265
14326
|
activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
|
|
@@ -14547,7 +14608,7 @@ async function executeRenderPipeline(input) {
|
|
|
14547
14608
|
pageMotionBlur: job.config.pageMotionBlur,
|
|
14548
14609
|
captureQuality: captureOptions.quality
|
|
14549
14610
|
});
|
|
14550
|
-
const segmentDir = segmentDirFor(
|
|
14611
|
+
const segmentDir = segmentDirFor(join28(projectDir, "renders"), segmentPlanHash);
|
|
14551
14612
|
let completedSegments = /* @__PURE__ */ new Set();
|
|
14552
14613
|
if (job.config.resumeSegments === true) {
|
|
14553
14614
|
const existing = readSegmentManifest(segmentDir);
|
|
@@ -15084,7 +15145,7 @@ async function executeRenderPipeline(input) {
|
|
|
15084
15145
|
})
|
|
15085
15146
|
);
|
|
15086
15147
|
const totalElapsed = Date.now() - pipelineStart;
|
|
15087
|
-
const tmpPeakBytes =
|
|
15148
|
+
const tmpPeakBytes = existsSync18(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
15088
15149
|
recordTransientRetryObservability();
|
|
15089
15150
|
observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
|
|
15090
15151
|
const observabilitySummary = observability.summary({
|
|
@@ -15165,8 +15226,8 @@ async function executeRenderPipeline(input) {
|
|
|
15165
15226
|
}
|
|
15166
15227
|
}
|
|
15167
15228
|
if (job.config.debug) {
|
|
15168
|
-
if (!isPngSequence && !isHls &&
|
|
15169
|
-
const debugOutput =
|
|
15229
|
+
if (!isPngSequence && !isHls && existsSync18(stagedOutputPath)) {
|
|
15230
|
+
const debugOutput = join28(workDir, `output${videoExt}`);
|
|
15170
15231
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
15171
15232
|
}
|
|
15172
15233
|
}
|
|
@@ -15272,7 +15333,7 @@ async function executeRenderPipeline(input) {
|
|
|
15272
15333
|
|
|
15273
15334
|
// src/services/hyperframeLint.ts
|
|
15274
15335
|
import {
|
|
15275
|
-
existsSync as
|
|
15336
|
+
existsSync as existsSync19,
|
|
15276
15337
|
readFileSync as readFileSync8,
|
|
15277
15338
|
statSync as statSync9,
|
|
15278
15339
|
openSync as openSync5,
|
|
@@ -15280,7 +15341,7 @@ import {
|
|
|
15280
15341
|
closeSync as closeSync5,
|
|
15281
15342
|
constants as constants3
|
|
15282
15343
|
} from "node:fs";
|
|
15283
|
-
import { resolve as resolve9, join as
|
|
15344
|
+
import { resolve as resolve9, join as join29, relative as relative3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
15284
15345
|
import { lintHyperframeHtml } from "@hyperframes/lint";
|
|
15285
15346
|
function isStringRecord(value) {
|
|
15286
15347
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -15326,7 +15387,7 @@ function readEntryHtml(filePath) {
|
|
|
15326
15387
|
}
|
|
15327
15388
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
15328
15389
|
const absProjectDir = resolve9(projectDir);
|
|
15329
|
-
if (!
|
|
15390
|
+
if (!existsSync19(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
|
|
15330
15391
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15331
15392
|
}
|
|
15332
15393
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -15348,7 +15409,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
15348
15409
|
}
|
|
15349
15410
|
}
|
|
15350
15411
|
return {
|
|
15351
|
-
error: `No HTML entry file found in project directory: ${
|
|
15412
|
+
error: `No HTML entry file found in project directory: ${join29(absProjectDir, preferredEntryFile || "index.html")}`
|
|
15352
15413
|
};
|
|
15353
15414
|
}
|
|
15354
15415
|
function prepareHyperframeLintBody(body) {
|
|
@@ -15394,8 +15455,8 @@ async function runHyperframeLint(prepared) {
|
|
|
15394
15455
|
// src/services/healthWorker.ts
|
|
15395
15456
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
15396
15457
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
15397
|
-
import { dirname as
|
|
15398
|
-
import { existsSync as
|
|
15458
|
+
import { dirname as dirname13, join as join30 } from "node:path";
|
|
15459
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
15399
15460
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
15400
15461
|
async function startHealthWorker(options = {}) {
|
|
15401
15462
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -15469,10 +15530,10 @@ function defaultLogger2() {
|
|
|
15469
15530
|
};
|
|
15470
15531
|
}
|
|
15471
15532
|
function resolveWorkerEntry2() {
|
|
15472
|
-
const here =
|
|
15473
|
-
const candidates = [
|
|
15533
|
+
const here = dirname13(fileURLToPath5(import.meta.url));
|
|
15534
|
+
const candidates = [join30(here, "healthWorkerThread.js"), join30(here, "healthWorkerThread.ts")];
|
|
15474
15535
|
for (const candidate of candidates) {
|
|
15475
|
-
if (
|
|
15536
|
+
if (existsSync20(candidate)) return candidate;
|
|
15476
15537
|
}
|
|
15477
15538
|
return void 0;
|
|
15478
15539
|
}
|
|
@@ -15507,7 +15568,7 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
|
15507
15568
|
|
|
15508
15569
|
// src/services/distributed/plan.ts
|
|
15509
15570
|
import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
|
|
15510
|
-
import { extname as extname4, isAbsolute as isAbsolute4, join as
|
|
15571
|
+
import { extname as extname4, isAbsolute as isAbsolute4, join as join31, relative as relative4, sep as sep3 } from "node:path";
|
|
15511
15572
|
import {
|
|
15512
15573
|
fpsToNumber as fpsToNumber9,
|
|
15513
15574
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
@@ -15518,7 +15579,7 @@ import {
|
|
|
15518
15579
|
normalizeVp9CpuUsed,
|
|
15519
15580
|
resolveConfig as resolveConfig3,
|
|
15520
15581
|
declareContentAddressedUrl,
|
|
15521
|
-
renameWithRetry as
|
|
15582
|
+
renameWithRetry as renameWithRetry8,
|
|
15522
15583
|
declareProbeFactsUrl
|
|
15523
15584
|
} from "@hyperframes/engine";
|
|
15524
15585
|
|
|
@@ -15589,10 +15650,10 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
15589
15650
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
15590
15651
|
|
|
15591
15652
|
// src/services/distributed/chunkAudio.ts
|
|
15592
|
-
import { appendRenderProvenanceArgs, describeFfmpegFailure as
|
|
15653
|
+
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
15593
15654
|
|
|
15594
15655
|
// src/services/distributed/mp4Stitch.ts
|
|
15595
|
-
import { renameWithRetry as
|
|
15656
|
+
import { renameWithRetry as renameWithRetry7 } from "@hyperframes/engine";
|
|
15596
15657
|
var SCAN_BYTES = 64 * 1024;
|
|
15597
15658
|
|
|
15598
15659
|
// src/services/distributed/chunkAudio.ts
|
|
@@ -15612,9 +15673,9 @@ var PLAN_ROOT_CATEGORY = {
|
|
|
15612
15673
|
var PLAN_DIR_SIZE_LIMIT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
15613
15674
|
var FREEZE_OWNED_PLAN_FILES = [
|
|
15614
15675
|
"plan.json",
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15676
|
+
join31("meta", "composition.json"),
|
|
15677
|
+
join31("meta", "encoder.json"),
|
|
15678
|
+
join31("meta", "chunks.json")
|
|
15618
15679
|
];
|
|
15619
15680
|
|
|
15620
15681
|
// src/services/distributed/renderConfigValidation.ts
|
|
@@ -15998,8 +16059,8 @@ function buildRenderJobConfig(input, outputPath, log) {
|
|
|
15998
16059
|
function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
15999
16060
|
const { input, cleanupProjectDir } = prepared;
|
|
16000
16061
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
16001
|
-
const outputDir =
|
|
16002
|
-
if (!
|
|
16062
|
+
const outputDir = dirname14(absoluteOutputPath);
|
|
16063
|
+
if (!existsSync21(outputDir)) mkdirSync11(outputDir, { recursive: true });
|
|
16003
16064
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
16004
16065
|
}
|
|
16005
16066
|
function validateRenderOverrides(body) {
|
|
@@ -16040,11 +16101,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
16040
16101
|
const candidate = nonEmptyString(projectDir);
|
|
16041
16102
|
if (!candidate) return null;
|
|
16042
16103
|
const absProjectDir = resolve10(candidate);
|
|
16043
|
-
if (!
|
|
16104
|
+
if (!existsSync21(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
|
|
16044
16105
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
16045
16106
|
}
|
|
16046
16107
|
const entry = options.entryFile || "index.html";
|
|
16047
|
-
if (!
|
|
16108
|
+
if (!existsSync21(resolve10(absProjectDir, entry))) {
|
|
16048
16109
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
16049
16110
|
}
|
|
16050
16111
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -16065,8 +16126,8 @@ async function resolveInlineRenderHtml(body) {
|
|
|
16065
16126
|
}
|
|
16066
16127
|
function materializeInlineProject(html, options) {
|
|
16067
16128
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
|
|
16068
|
-
const tempProjectDir = mkdtempSync4(
|
|
16069
|
-
writeFileSync7(
|
|
16129
|
+
const tempProjectDir = mkdtempSync4(join32(tempRoot, "producer-project-"));
|
|
16130
|
+
writeFileSync7(join32(tempProjectDir, "index.html"), html, "utf-8");
|
|
16070
16131
|
return {
|
|
16071
16132
|
prepared: {
|
|
16072
16133
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -16136,7 +16197,7 @@ function cleanupTempDir(dir, log) {
|
|
|
16136
16197
|
}
|
|
16137
16198
|
}
|
|
16138
16199
|
function outputFileSize(path) {
|
|
16139
|
-
return
|
|
16200
|
+
return existsSync21(path) ? statSync10(path).size : 0;
|
|
16140
16201
|
}
|
|
16141
16202
|
function createBlockingProgressReporter(log, requestId) {
|
|
16142
16203
|
let lastLoggedPct = -10;
|
|
@@ -16438,7 +16499,7 @@ function createRenderHandlers(options = {}) {
|
|
|
16438
16499
|
if (!artifact) {
|
|
16439
16500
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
16440
16501
|
}
|
|
16441
|
-
if (!
|
|
16502
|
+
if (!existsSync21(artifact.path)) {
|
|
16442
16503
|
store.delete(token);
|
|
16443
16504
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
16444
16505
|
}
|