@hyperframes/producer 0.9.14 → 0.9.15
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 +142 -96
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +62 -62
- package/dist/index.js +167 -118
- package/dist/public-server.js +107 -58
- package/dist/services/render/audioPadTrim.d.ts +6 -0
- package/dist/services/render/stages/assembleStage.d.ts +1 -0
- package/dist/services/render/stages/audioStage.d.ts +4 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
// src/services/renderOrchestrator.ts
|
|
10
10
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
11
11
|
import {
|
|
12
|
-
existsSync as
|
|
12
|
+
existsSync as existsSync19,
|
|
13
13
|
mkdirSync as mkdirSync10,
|
|
14
14
|
readdirSync as readdirSync5,
|
|
15
15
|
rmSync as rmSync10,
|
|
@@ -9986,6 +9986,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
9986
9986
|
}
|
|
9987
9987
|
|
|
9988
9988
|
// src/services/render/stages/audioStage.ts
|
|
9989
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
9989
9990
|
import { join as join21 } from "node:path";
|
|
9990
9991
|
import {
|
|
9991
9992
|
MIXED_AUDIO_FILENAME,
|
|
@@ -10119,8 +10120,9 @@ var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
|
|
|
10119
10120
|
var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
|
|
10120
10121
|
var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
|
|
10121
10122
|
var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
|
|
10123
|
+
var gainFilter = (gainDb) => `volume=${gainDb.toFixed(3)}dB`;
|
|
10122
10124
|
function aacTruePeakCorrectionFilter(targetDurationSeconds, attenuationDb) {
|
|
10123
|
-
return
|
|
10125
|
+
return `${gainFilter(attenuationDb)},apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
|
|
10124
10126
|
}
|
|
10125
10127
|
function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
|
|
10126
10128
|
const delta = targetDurationSeconds - sourceDurationSeconds;
|
|
@@ -10158,7 +10160,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10158
10160
|
const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
|
|
10159
10161
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
10160
10162
|
const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
|
|
10161
|
-
const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal);
|
|
10163
|
+
const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath);
|
|
10162
10164
|
const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg || input.encodeAac ? void 0 : (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }));
|
|
10163
10165
|
const [videoResult, audioResult] = await Promise.allSettled([
|
|
10164
10166
|
probeVideo(input.videoPath),
|
|
@@ -10206,7 +10208,12 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10206
10208
|
);
|
|
10207
10209
|
try {
|
|
10208
10210
|
for (const step of plan2.steps) {
|
|
10209
|
-
const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(
|
|
10211
|
+
const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(
|
|
10212
|
+
input.sourcePath ?? input.audioPath,
|
|
10213
|
+
input.outputPath,
|
|
10214
|
+
targetDurationSeconds,
|
|
10215
|
+
step.filter
|
|
10216
|
+
);
|
|
10210
10217
|
if (!ffmpegResult.success) {
|
|
10211
10218
|
return {
|
|
10212
10219
|
success: false,
|
|
@@ -10235,6 +10242,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10235
10242
|
if (probeTruePeak) {
|
|
10236
10243
|
const correction = await enforceAacTruePeak({
|
|
10237
10244
|
audioPath: input.outputPath,
|
|
10245
|
+
sourcePath: input.sourcePath,
|
|
10238
10246
|
targetDurationSeconds,
|
|
10239
10247
|
signal: input.signal,
|
|
10240
10248
|
probeTruePeak,
|
|
@@ -10270,9 +10278,27 @@ async function constrainAacTruePeak(input) {
|
|
|
10270
10278
|
targetDurationSeconds: input.targetDurationSeconds,
|
|
10271
10279
|
signal: input.signal,
|
|
10272
10280
|
probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
|
|
10273
|
-
encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
10281
|
+
encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath),
|
|
10282
|
+
rewriteSource: input.rewriteSource ?? ((sourcePath, outputPath, filter) => defaultRunFfmpeg(
|
|
10283
|
+
["-i", sourcePath, "-af", filter, "-c:a", "pcm_f32le", "-y", outputPath],
|
|
10284
|
+
timeoutMs,
|
|
10285
|
+
input.signal
|
|
10286
|
+
))
|
|
10274
10287
|
});
|
|
10275
10288
|
}
|
|
10289
|
+
async function cutSource(input, scratchDir, attenuationDb) {
|
|
10290
|
+
if (!input.sourcePath || !input.rewriteSource) return true;
|
|
10291
|
+
const cutPath = join19(scratchDir, "source.wav");
|
|
10292
|
+
try {
|
|
10293
|
+
const result = await input.rewriteSource(input.sourcePath, cutPath, gainFilter(attenuationDb));
|
|
10294
|
+
if (!result.success) throw new Error(result.error);
|
|
10295
|
+
renameWithRetry5(cutPath, input.sourcePath, { copyFallback: true });
|
|
10296
|
+
return true;
|
|
10297
|
+
} catch {
|
|
10298
|
+
removeScratch(input.sourcePath);
|
|
10299
|
+
return false;
|
|
10300
|
+
}
|
|
10301
|
+
}
|
|
10276
10302
|
async function enforceAacTruePeak(input) {
|
|
10277
10303
|
let scratchDir;
|
|
10278
10304
|
try {
|
|
@@ -10292,8 +10318,10 @@ async function enforceAacTruePeak(input) {
|
|
|
10292
10318
|
}
|
|
10293
10319
|
if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
|
|
10294
10320
|
if (measuredPath !== correctedPath) return { success: true };
|
|
10321
|
+
const sourceCut = await cutSource(input, scratchDir, attenuationDb);
|
|
10295
10322
|
renameWithRetry5(correctedPath, input.audioPath, { copyFallback: true });
|
|
10296
|
-
|
|
10323
|
+
const loweredDb = Number((-attenuationDb).toFixed(2));
|
|
10324
|
+
return sourceCut ? { success: true, loweredDb } : { success: true, loweredDb, sourceStale: true };
|
|
10297
10325
|
}
|
|
10298
10326
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
10299
10327
|
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
@@ -10303,7 +10331,7 @@ async function enforceAacTruePeak(input) {
|
|
|
10303
10331
|
const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
|
|
10304
10332
|
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
|
|
10305
10333
|
const result = await input.encode(
|
|
10306
|
-
input.audioPath,
|
|
10334
|
+
input.sourcePath ?? input.audioPath,
|
|
10307
10335
|
correctedPath,
|
|
10308
10336
|
input.targetDurationSeconds,
|
|
10309
10337
|
aacTruePeakCorrectionFilter(input.targetDurationSeconds, attenuationDb)
|
|
@@ -10532,36 +10560,54 @@ async function runAudioStageScoped(input) {
|
|
|
10532
10560
|
const stageStart = Date.now();
|
|
10533
10561
|
const pcmSourcePath = join21(input.workDir, "audio-source.wav");
|
|
10534
10562
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
10563
|
+
let deliverySourcePath;
|
|
10535
10564
|
try {
|
|
10536
10565
|
const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
|
|
10537
10566
|
let audioLoweredDb;
|
|
10538
10567
|
if (result.hasAudio) {
|
|
10539
10568
|
const guardStart = Date.now();
|
|
10540
|
-
const
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
});
|
|
10548
|
-
if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
|
|
10549
|
-
const peak = await constrainAacTruePeak({
|
|
10550
|
-
audioPath: result.audioOutputPath,
|
|
10551
|
-
sourcePath: pcmSourcePath,
|
|
10552
|
-
targetDurationSeconds: duration,
|
|
10553
|
-
signal: input.abortSignal,
|
|
10554
|
-
timeoutMs: input.ffmpegProcessTimeout
|
|
10555
|
-
});
|
|
10556
|
-
if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
|
|
10557
|
-
audioLoweredDb = peak.loweredDb;
|
|
10569
|
+
const guard = await guardDeliveryPeaks(
|
|
10570
|
+
input,
|
|
10571
|
+
result.audioOutputPath,
|
|
10572
|
+
pcmSourcePath,
|
|
10573
|
+
duration
|
|
10574
|
+
);
|
|
10575
|
+
audioLoweredDb = guard.loweredDb;
|
|
10558
10576
|
input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
|
|
10577
|
+
if (input.keepDeliverySource && !guard.sourceStale && existsSync14(pcmSourcePath)) {
|
|
10578
|
+
deliverySourcePath = pcmSourcePath;
|
|
10579
|
+
}
|
|
10559
10580
|
}
|
|
10560
|
-
return {
|
|
10581
|
+
return {
|
|
10582
|
+
...result,
|
|
10583
|
+
audioLoweredDb,
|
|
10584
|
+
deliverySourcePath,
|
|
10585
|
+
audioProcessMs: Date.now() - stageStart
|
|
10586
|
+
};
|
|
10561
10587
|
} finally {
|
|
10562
|
-
removeScratch2(pcmSourcePath);
|
|
10588
|
+
if (!deliverySourcePath) removeScratch2(pcmSourcePath);
|
|
10563
10589
|
}
|
|
10564
10590
|
}
|
|
10591
|
+
async function guardDeliveryPeaks(input, audioPath, sourcePath, duration) {
|
|
10592
|
+
const limited = await limitDeliveryPeaks({
|
|
10593
|
+
audioPath,
|
|
10594
|
+
sourcePath,
|
|
10595
|
+
targetDurationSeconds: duration,
|
|
10596
|
+
signal: input.abortSignal,
|
|
10597
|
+
timeoutMs: input.ffmpegProcessTimeout,
|
|
10598
|
+
warn: (message, details) => input.log?.warn?.(message, details)
|
|
10599
|
+
});
|
|
10600
|
+
if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
|
|
10601
|
+
const peak = await constrainAacTruePeak({
|
|
10602
|
+
audioPath,
|
|
10603
|
+
sourcePath,
|
|
10604
|
+
targetDurationSeconds: duration,
|
|
10605
|
+
signal: input.abortSignal,
|
|
10606
|
+
timeoutMs: input.ffmpegProcessTimeout
|
|
10607
|
+
});
|
|
10608
|
+
if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
|
|
10609
|
+
return { loweredDb: peak.loweredDb, sourceStale: peak.sourceStale };
|
|
10610
|
+
}
|
|
10565
10611
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
10566
10612
|
const {
|
|
10567
10613
|
projectDir,
|
|
@@ -10656,7 +10702,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10656
10702
|
}
|
|
10657
10703
|
|
|
10658
10704
|
// src/services/render/stages/captureStage.ts
|
|
10659
|
-
import { existsSync as
|
|
10705
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
10660
10706
|
import { join as join22 } from "node:path";
|
|
10661
10707
|
import {
|
|
10662
10708
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -10727,7 +10773,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
|
|
|
10727
10773
|
}
|
|
10728
10774
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
10729
10775
|
let total = sampleDirectoryBytes(framesDir);
|
|
10730
|
-
if (!
|
|
10776
|
+
if (!existsSync15(workDir)) return total;
|
|
10731
10777
|
for (const attempt of readdirSync3(workDir)) {
|
|
10732
10778
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
10733
10779
|
total += sampleDirectoryBytes(join22(workDir, attempt));
|
|
@@ -11030,7 +11076,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
11030
11076
|
}
|
|
11031
11077
|
|
|
11032
11078
|
// src/services/render/stages/captureHdrStage.ts
|
|
11033
|
-
import { existsSync as
|
|
11079
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8 } from "node:fs";
|
|
11034
11080
|
import { join as join26 } from "node:path";
|
|
11035
11081
|
import {
|
|
11036
11082
|
closeCaptureSession as closeCaptureSession7,
|
|
@@ -11276,7 +11322,7 @@ import { Worker } from "node:worker_threads";
|
|
|
11276
11322
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
11277
11323
|
import { dirname as dirname10, join as join24 } from "node:path";
|
|
11278
11324
|
import { createRequire } from "node:module";
|
|
11279
|
-
import { existsSync as
|
|
11325
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
11280
11326
|
import { cpus as cpus2 } from "node:os";
|
|
11281
11327
|
function resolveWorkerEntry(explicit) {
|
|
11282
11328
|
if (explicit && explicit.length > 0) {
|
|
@@ -11289,7 +11335,7 @@ function resolveWorkerEntry(explicit) {
|
|
|
11289
11335
|
}
|
|
11290
11336
|
const moduleDir = dirname10(fileURLToPath3(import.meta.url));
|
|
11291
11337
|
const jsPath = join24(moduleDir, "shaderTransitionWorker.js");
|
|
11292
|
-
if (
|
|
11338
|
+
if (existsSync16(jsPath)) return { path: jsPath, isTs: false };
|
|
11293
11339
|
const tsPath = join24(moduleDir, "shaderTransitionWorker.ts");
|
|
11294
11340
|
return { path: tsPath, isTs: true };
|
|
11295
11341
|
}
|
|
@@ -11850,7 +11896,7 @@ async function runCaptureHdrStage(input) {
|
|
|
11850
11896
|
}
|
|
11851
11897
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
11852
11898
|
const debugDumpDir = debugDumpEnabled ? join26(framesDir, "debug-composite") : null;
|
|
11853
|
-
if (debugDumpDir && !
|
|
11899
|
+
if (debugDumpDir && !existsSync17(debugDumpDir)) {
|
|
11854
11900
|
mkdirSync8(debugDumpDir, { recursive: true });
|
|
11855
11901
|
}
|
|
11856
11902
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
@@ -12007,7 +12053,7 @@ async function runCaptureHdrStage(input) {
|
|
|
12007
12053
|
// src/services/render/stages/encodeStage.ts
|
|
12008
12054
|
import {
|
|
12009
12055
|
copyFileSync as copyFileSync4,
|
|
12010
|
-
existsSync as
|
|
12056
|
+
existsSync as existsSync18,
|
|
12011
12057
|
mkdirSync as mkdirSync9,
|
|
12012
12058
|
readdirSync as readdirSync4,
|
|
12013
12059
|
readFileSync as readFileSync8,
|
|
@@ -12141,7 +12187,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
12141
12187
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
12142
12188
|
if (failure) return failure;
|
|
12143
12189
|
}
|
|
12144
|
-
const fileSize =
|
|
12190
|
+
const fileSize = existsSync18(outputPath) ? statSync7(outputPath).size : 0;
|
|
12145
12191
|
return {
|
|
12146
12192
|
success: true,
|
|
12147
12193
|
outputPath,
|
|
@@ -12302,7 +12348,7 @@ async function runEncodeStage(input) {
|
|
|
12302
12348
|
const stage5Start = Date.now();
|
|
12303
12349
|
if (isPngSequence) {
|
|
12304
12350
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
12305
|
-
if (!
|
|
12351
|
+
if (!existsSync18(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
12306
12352
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
12307
12353
|
if (captured.length === 0) {
|
|
12308
12354
|
throw new Error(
|
|
@@ -12313,7 +12359,7 @@ async function runEncodeStage(input) {
|
|
|
12313
12359
|
const dst = join27(outputPath, formatExportFrameName(i, "png"));
|
|
12314
12360
|
copyFileSync4(join27(framesDir, name), dst);
|
|
12315
12361
|
});
|
|
12316
|
-
if (hasAudio && audioOutputPath &&
|
|
12362
|
+
if (hasAudio && audioOutputPath && existsSync18(audioOutputPath)) {
|
|
12317
12363
|
copyFileSync4(audioOutputPath, join27(outputPath, MIXED_AUDIO_FILENAME2));
|
|
12318
12364
|
log.info(
|
|
12319
12365
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
@@ -12424,6 +12470,7 @@ async function assembleOutput(input, onSecondsWritten) {
|
|
|
12424
12470
|
const normalizeResult = await padOrTrimAudioToVideoFrameCount({
|
|
12425
12471
|
videoPath: videoOnlyPath,
|
|
12426
12472
|
audioPath: audioOutputPath,
|
|
12473
|
+
sourcePath: input.audioPcmPath,
|
|
12427
12474
|
outputPath: normalizedAudioPath,
|
|
12428
12475
|
signal: abortSignal,
|
|
12429
12476
|
timeoutMs: ffmpegProcessTimeout
|
|
@@ -12705,7 +12752,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
12705
12752
|
let rangeStart = null;
|
|
12706
12753
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12707
12754
|
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12708
|
-
const missing = !
|
|
12755
|
+
const missing = !existsSync19(framePath) || statSync8(framePath).size <= 8;
|
|
12709
12756
|
if (missing && rangeStart === null) {
|
|
12710
12757
|
rangeStart = frameIndex;
|
|
12711
12758
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -12830,7 +12877,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
|
12830
12877
|
let captured = 0;
|
|
12831
12878
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12832
12879
|
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12833
|
-
if (
|
|
12880
|
+
if (existsSync19(framePath)) captured++;
|
|
12834
12881
|
}
|
|
12835
12882
|
return captured;
|
|
12836
12883
|
}
|
|
@@ -13495,7 +13542,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
13495
13542
|
validateDeliveryEncoderConfig(job.config);
|
|
13496
13543
|
const debugDir = resolveRenderDebugDir();
|
|
13497
13544
|
const outputDir = dirname12(outputPath);
|
|
13498
|
-
if (!
|
|
13545
|
+
if (!existsSync19(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
13499
13546
|
const workDir = job.config.debug ? join28(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
13500
13547
|
const pipelineStart = Date.now();
|
|
13501
13548
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
@@ -13637,7 +13684,7 @@ async function executeRenderPipeline(input) {
|
|
|
13637
13684
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
13638
13685
|
assertNotAborted();
|
|
13639
13686
|
assertConfiguredFfmpegBinariesExist();
|
|
13640
|
-
if (!
|
|
13687
|
+
if (!existsSync19(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
13641
13688
|
if (job.config.debug) {
|
|
13642
13689
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
13643
13690
|
}
|
|
@@ -13667,7 +13714,7 @@ async function executeRenderPipeline(input) {
|
|
|
13667
13714
|
});
|
|
13668
13715
|
const entryFile = job.config.entryFile || "index.html";
|
|
13669
13716
|
let htmlPath = join28(projectDir, entryFile);
|
|
13670
|
-
if (!
|
|
13717
|
+
if (!existsSync19(htmlPath)) {
|
|
13671
13718
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
13672
13719
|
}
|
|
13673
13720
|
assertNotAborted();
|
|
@@ -13675,7 +13722,7 @@ async function executeRenderPipeline(input) {
|
|
|
13675
13722
|
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
13676
13723
|
const wrapperPath = join28(workDir, "standalone-entry.html");
|
|
13677
13724
|
const projectIndexPath = join28(projectDir, "index.html");
|
|
13678
|
-
if (!
|
|
13725
|
+
if (!existsSync19(projectIndexPath)) {
|
|
13679
13726
|
throw new Error(
|
|
13680
13727
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
13681
13728
|
);
|
|
@@ -13986,7 +14033,8 @@ async function executeRenderPipeline(input) {
|
|
|
13986
14033
|
masterFxChain: composition.masterFxChain,
|
|
13987
14034
|
abortSignal: executionSignal,
|
|
13988
14035
|
assertNotAborted,
|
|
13989
|
-
log
|
|
14036
|
+
log,
|
|
14037
|
+
keepDeliverySource: true
|
|
13990
14038
|
})
|
|
13991
14039
|
);
|
|
13992
14040
|
pendingAudio = audioStage.catch(() => void 0);
|
|
@@ -14018,7 +14066,7 @@ async function executeRenderPipeline(input) {
|
|
|
14018
14066
|
throw new Error("File server failed to initialize before frame capture");
|
|
14019
14067
|
}
|
|
14020
14068
|
const framesDir = join28(workDir, "captured-frames");
|
|
14021
|
-
if (!
|
|
14069
|
+
if (!existsSync19(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
14022
14070
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
14023
14071
|
code: "start_browsers"
|
|
14024
14072
|
});
|
|
@@ -14694,7 +14742,7 @@ async function executeRenderPipeline(input) {
|
|
|
14694
14742
|
const effectiveQuality = job.config.crf ?? preset.quality;
|
|
14695
14743
|
const effectiveBitrate = job.config.crf != null ? void 0 : job.config.videoBitrate;
|
|
14696
14744
|
const audioResult = await audioStage;
|
|
14697
|
-
const { audioOutputPath, hasAudio } = audioResult;
|
|
14745
|
+
const { audioOutputPath, deliverySourcePath, hasAudio } = audioResult;
|
|
14698
14746
|
perfStages.audioProcessMs = audioResult.audioProcessMs;
|
|
14699
14747
|
recordLimiterAttenuation(job, audioResult.audioLoweredDb);
|
|
14700
14748
|
if (audioResult.audioError) {
|
|
@@ -15320,6 +15368,7 @@ async function executeRenderPipeline(input) {
|
|
|
15320
15368
|
job,
|
|
15321
15369
|
videoOnlyPath,
|
|
15322
15370
|
audioOutputPath,
|
|
15371
|
+
audioPcmPath: deliverySourcePath,
|
|
15323
15372
|
outputPath: stagedOutputPath,
|
|
15324
15373
|
hasAudio,
|
|
15325
15374
|
format: outputFormat,
|
|
@@ -15345,7 +15394,7 @@ async function executeRenderPipeline(input) {
|
|
|
15345
15394
|
})
|
|
15346
15395
|
);
|
|
15347
15396
|
const totalElapsed = Date.now() - pipelineStart;
|
|
15348
|
-
const tmpPeakBytes =
|
|
15397
|
+
const tmpPeakBytes = existsSync19(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
15349
15398
|
recordTransientRetryObservability();
|
|
15350
15399
|
observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
|
|
15351
15400
|
const observabilitySummary = observability.summary({
|
|
@@ -15426,7 +15475,7 @@ async function executeRenderPipeline(input) {
|
|
|
15426
15475
|
}
|
|
15427
15476
|
}
|
|
15428
15477
|
if (job.config.debug) {
|
|
15429
|
-
if (!isPngSequence && !isHls &&
|
|
15478
|
+
if (!isPngSequence && !isHls && existsSync19(stagedOutputPath)) {
|
|
15430
15479
|
const debugOutput = join28(workDir, `output${videoExt}`);
|
|
15431
15480
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
15432
15481
|
}
|
|
@@ -15938,7 +15987,7 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
|
|
|
15938
15987
|
import {
|
|
15939
15988
|
copyFileSync as copyFileSync6,
|
|
15940
15989
|
cpSync as cpSync2,
|
|
15941
|
-
existsSync as
|
|
15990
|
+
existsSync as existsSync24,
|
|
15942
15991
|
linkSync,
|
|
15943
15992
|
mkdirSync as mkdirSync14,
|
|
15944
15993
|
rmSync as rmSync12,
|
|
@@ -15969,7 +16018,7 @@ import {
|
|
|
15969
16018
|
} from "@hyperframes/engine";
|
|
15970
16019
|
|
|
15971
16020
|
// src/services/distributed/chunkFrames.ts
|
|
15972
|
-
import { existsSync as
|
|
16021
|
+
import { existsSync as existsSync21, readdirSync as readdirSync6 } from "node:fs";
|
|
15973
16022
|
import { extname as extname4, join as join31 } from "node:path";
|
|
15974
16023
|
import {
|
|
15975
16024
|
compositionRequiresWebGpu as compositionRequiresWebGpu3,
|
|
@@ -15981,7 +16030,7 @@ import {
|
|
|
15981
16030
|
// src/services/distributed/shared.ts
|
|
15982
16031
|
import { execFile as execFileCallback } from "node:child_process";
|
|
15983
16032
|
import { dirname as dirname14, join as join30 } from "node:path";
|
|
15984
|
-
import { existsSync as
|
|
16033
|
+
import { existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
|
|
15985
16034
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
15986
16035
|
import { promisify as promisify2 } from "node:util";
|
|
15987
16036
|
import {
|
|
@@ -15997,7 +16046,7 @@ function isPlanAudioArtifactPath(path) {
|
|
|
15997
16046
|
function resolvePlanAudioPath(planDir) {
|
|
15998
16047
|
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
15999
16048
|
const candidate = join30(planDir, name);
|
|
16000
|
-
if (
|
|
16049
|
+
if (existsSync20(candidate)) return candidate;
|
|
16001
16050
|
}
|
|
16002
16051
|
return null;
|
|
16003
16052
|
}
|
|
@@ -16258,7 +16307,7 @@ function readProducerVersion() {
|
|
|
16258
16307
|
let current = startDir;
|
|
16259
16308
|
for (let i = 0; i < 10; i++) {
|
|
16260
16309
|
const candidate = join30(current, "package.json");
|
|
16261
|
-
if (
|
|
16310
|
+
if (existsSync20(candidate)) {
|
|
16262
16311
|
try {
|
|
16263
16312
|
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
16264
16313
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
@@ -16312,7 +16361,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
|
|
|
16312
16361
|
const result = [];
|
|
16313
16362
|
for (const v of videos) {
|
|
16314
16363
|
const outputDir = join31(planDir, "video-frames", v.videoId);
|
|
16315
|
-
if (!
|
|
16364
|
+
if (!existsSync21(outputDir)) {
|
|
16316
16365
|
throw new Error(
|
|
16317
16366
|
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
16318
16367
|
);
|
|
@@ -16428,7 +16477,7 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
16428
16477
|
const range = video.deferredRange;
|
|
16429
16478
|
const remoteUrl = remoteSources[range.sourcePath];
|
|
16430
16479
|
const sourcePath = remoteUrl ?? join31(input.planDir, ...range.sourcePath.split("/"));
|
|
16431
|
-
if (remoteUrl === void 0 && !
|
|
16480
|
+
if (remoteUrl === void 0 && !existsSync21(sourcePath)) {
|
|
16432
16481
|
throw new RenderChunkValidationError(
|
|
16433
16482
|
MISSING_PLAN_ARTIFACT,
|
|
16434
16483
|
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
@@ -16646,7 +16695,7 @@ async function readPlanReferenceBackground(input) {
|
|
|
16646
16695
|
}
|
|
16647
16696
|
|
|
16648
16697
|
// src/services/render/stages/freezePlan.ts
|
|
16649
|
-
import { existsSync as
|
|
16698
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
16650
16699
|
import { join as join34, relative as relative3, resolve as resolve9 } from "node:path";
|
|
16651
16700
|
import {
|
|
16652
16701
|
REFERENCE_BACKGROUND_FIELD,
|
|
@@ -16840,7 +16889,7 @@ function planFileSha256(absolutePath, digests) {
|
|
|
16840
16889
|
return sha256;
|
|
16841
16890
|
}
|
|
16842
16891
|
async function prehashPlanFiles(dir, digests) {
|
|
16843
|
-
if (!
|
|
16892
|
+
if (!existsSync22(dir)) return;
|
|
16844
16893
|
for (const { absolutePath } of listPlanFiles(dir)) {
|
|
16845
16894
|
if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
|
|
16846
16895
|
}
|
|
@@ -16910,15 +16959,15 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
16910
16959
|
}
|
|
16911
16960
|
function readPlanRemoteFiles(planDir) {
|
|
16912
16961
|
const path = join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
16913
|
-
return
|
|
16962
|
+
return existsSync22(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
16914
16963
|
}
|
|
16915
16964
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
16916
16965
|
const planJsonPath = join34(planDir, "plan.json");
|
|
16917
16966
|
const encoderJsonPath = join34(planDir, "meta", "encoder.json");
|
|
16918
|
-
if (!
|
|
16967
|
+
if (!existsSync22(planJsonPath)) {
|
|
16919
16968
|
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
16920
16969
|
}
|
|
16921
|
-
if (!
|
|
16970
|
+
if (!existsSync22(encoderJsonPath)) {
|
|
16922
16971
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
16923
16972
|
}
|
|
16924
16973
|
const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
@@ -16948,11 +16997,11 @@ async function freezePlan(input) {
|
|
|
16948
16997
|
hasAudio,
|
|
16949
16998
|
digests
|
|
16950
16999
|
} = input;
|
|
16951
|
-
if (!
|
|
17000
|
+
if (!existsSync22(planDir)) {
|
|
16952
17001
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
16953
17002
|
}
|
|
16954
17003
|
const metaDir = join34(planDir, "meta");
|
|
16955
|
-
if (!
|
|
17004
|
+
if (!existsSync22(metaDir)) mkdirSync12(metaDir, { recursive: true });
|
|
16956
17005
|
writeFileSync7(
|
|
16957
17006
|
join34(metaDir, "composition.json"),
|
|
16958
17007
|
`${JSON.stringify(composition, null, 2)}
|
|
@@ -17029,7 +17078,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
17029
17078
|
}
|
|
17030
17079
|
|
|
17031
17080
|
// src/services/distributed/chunkAudio.ts
|
|
17032
|
-
import { closeSync as closeSync6, existsSync as
|
|
17081
|
+
import { closeSync as closeSync6, existsSync as existsSync23, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
|
|
17033
17082
|
import { writeFileSync as writeFileSync8, writeSync } from "node:fs";
|
|
17034
17083
|
import { join as join35 } from "node:path";
|
|
17035
17084
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
@@ -17135,7 +17184,7 @@ async function writeChunkAudioSlices(input) {
|
|
|
17135
17184
|
}
|
|
17136
17185
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
17137
17186
|
const layoutPath = join35(planDir, SLICE_LAYOUT);
|
|
17138
|
-
if (!
|
|
17187
|
+
if (!existsSync23(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
17139
17188
|
const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
|
|
17140
17189
|
const frame = readFileSync11(join35(planDir, SLICE_FRAME));
|
|
17141
17190
|
const fd = openSync6(scratchPath, "w");
|
|
@@ -17695,7 +17744,7 @@ function createVideoSourceShipper(input) {
|
|
|
17695
17744
|
let result = null;
|
|
17696
17745
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
17697
17746
|
const rel = pathInside(root, srcPath);
|
|
17698
|
-
if (rel !== null && (
|
|
17747
|
+
if (rel !== null && (existsSync24(join37(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
17699
17748
|
result = ["compiled", ...rel.split(sep2)].join("/");
|
|
17700
17749
|
break;
|
|
17701
17750
|
}
|
|
@@ -17723,7 +17772,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
17723
17772
|
useGpu: false,
|
|
17724
17773
|
browserGpuMode: "software"
|
|
17725
17774
|
});
|
|
17726
|
-
if (!
|
|
17775
|
+
if (!existsSync24(planDir)) mkdirSync14(planDir, { recursive: true });
|
|
17727
17776
|
const log = config.logger ?? defaultLogger;
|
|
17728
17777
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
17729
17778
|
const abortSignal = config.abortSignal;
|
|
@@ -17753,7 +17802,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
17753
17802
|
});
|
|
17754
17803
|
const entryFile = config.entryFile ?? "index.html";
|
|
17755
17804
|
const htmlPath = join37(projectDir, entryFile);
|
|
17756
|
-
if (!
|
|
17805
|
+
if (!existsSync24(htmlPath)) {
|
|
17757
17806
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
17758
17807
|
}
|
|
17759
17808
|
const workDir = join37(planDir, ".plan-work");
|
|
@@ -17982,15 +18031,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
17982
18031
|
logPlanStage(log, "extract", stageStarted);
|
|
17983
18032
|
const stagedVideoFrames = join37(compiledDir, "__hyperframes_video_frames");
|
|
17984
18033
|
const videoFramesDst = join37(planDir, "video-frames");
|
|
17985
|
-
if (
|
|
18034
|
+
if (existsSync24(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
|
|
17986
18035
|
const videoSourcesDir = join37(planDir, "video-sources");
|
|
17987
18036
|
rmSync12(videoSourcesDir, { recursive: true, force: true });
|
|
17988
|
-
if (
|
|
18037
|
+
if (existsSync24(stagedVideoFrames)) {
|
|
17989
18038
|
renameWithRetry8(stagedVideoFrames, videoFramesDst);
|
|
17990
18039
|
} else {
|
|
17991
18040
|
mkdirSync14(videoFramesDst, { recursive: true });
|
|
17992
18041
|
}
|
|
17993
|
-
if (
|
|
18042
|
+
if (existsSync24(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
|
|
17994
18043
|
renameWithRetry8(compiledDir, finalCompiledDir);
|
|
17995
18044
|
const shipVideoSource = createVideoSourceShipper({
|
|
17996
18045
|
planDir,
|
|
@@ -18110,10 +18159,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18110
18159
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
18111
18160
|
}
|
|
18112
18161
|
const planAudioPath = join37(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
18113
|
-
if (audioResult?.hasAudio &&
|
|
18162
|
+
if (audioResult?.hasAudio && existsSync24(audioResult.audioOutputPath)) {
|
|
18114
18163
|
renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
|
|
18115
18164
|
}
|
|
18116
|
-
if (chunksCarryAudio &&
|
|
18165
|
+
if (chunksCarryAudio && existsSync24(planAudioPath)) {
|
|
18117
18166
|
if (options.sliceChunkAudio) {
|
|
18118
18167
|
stageStarted = Date.now();
|
|
18119
18168
|
await writeChunkAudioSlices({
|
|
@@ -18231,7 +18280,7 @@ async function plan(projectDir, config, planDir, options = {}) {
|
|
|
18231
18280
|
|
|
18232
18281
|
// src/services/distributed/renderChunk.ts
|
|
18233
18282
|
import { randomBytes } from "node:crypto";
|
|
18234
|
-
import { existsSync as
|
|
18283
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync13, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "node:fs";
|
|
18235
18284
|
import { cpus as cpus3 } from "node:os";
|
|
18236
18285
|
import { join as join41 } from "node:path";
|
|
18237
18286
|
import {
|
|
@@ -18258,7 +18307,7 @@ import {
|
|
|
18258
18307
|
} from "@hyperframes/engine";
|
|
18259
18308
|
import {
|
|
18260
18309
|
copyFileSync as copyFileSync8,
|
|
18261
|
-
existsSync as
|
|
18310
|
+
existsSync as existsSync26,
|
|
18262
18311
|
lstatSync as lstatSync3,
|
|
18263
18312
|
mkdirSync as mkdirSync16,
|
|
18264
18313
|
mkdtempSync as mkdtempSync6,
|
|
@@ -18276,7 +18325,7 @@ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as r
|
|
|
18276
18325
|
// src/services/distributed/planV2Publisher.ts
|
|
18277
18326
|
import {
|
|
18278
18327
|
copyFileSync as copyFileSync7,
|
|
18279
|
-
existsSync as
|
|
18328
|
+
existsSync as existsSync25,
|
|
18280
18329
|
linkSync as linkSync2,
|
|
18281
18330
|
mkdirSync as mkdirSync15,
|
|
18282
18331
|
mkdtempSync as mkdtempSync5,
|
|
@@ -18345,7 +18394,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18345
18394
|
#linkFile;
|
|
18346
18395
|
#committed = false;
|
|
18347
18396
|
constructor(destinationDir, options = {}) {
|
|
18348
|
-
if (
|
|
18397
|
+
if (existsSync25(destinationDir)) {
|
|
18349
18398
|
throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
|
|
18350
18399
|
}
|
|
18351
18400
|
this.destinationDir = destinationDir;
|
|
@@ -18362,7 +18411,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18362
18411
|
);
|
|
18363
18412
|
}
|
|
18364
18413
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
18365
|
-
if (
|
|
18414
|
+
if (existsSync25(destinationPath)) return;
|
|
18366
18415
|
mkdirSync15(dirname15(destinationPath), { recursive: true });
|
|
18367
18416
|
const stagingDir = mkdtempSync5(join39(dirname15(destinationPath), ".plan-v2-blob-"));
|
|
18368
18417
|
const temporaryPath = join39(stagingDir, "blob");
|
|
@@ -18380,7 +18429,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18380
18429
|
}
|
|
18381
18430
|
async commitManifest(manifestBytes) {
|
|
18382
18431
|
for (const digest of new Set(manifestDigests(manifestBytes))) {
|
|
18383
|
-
if (!
|
|
18432
|
+
if (!existsSync25(planV2BlobPath(this.temporaryDir, digest))) {
|
|
18384
18433
|
throw new PlanV2IntegrityError(
|
|
18385
18434
|
`cannot commit manifest before referenced blob is durable: ${digest}`
|
|
18386
18435
|
);
|
|
@@ -18459,7 +18508,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
|
|
|
18459
18508
|
}
|
|
18460
18509
|
function validateExtractionCacheCompleteSentinels(executionPlanDir) {
|
|
18461
18510
|
const videoRoot = join40(executionPlanDir, "video-frames");
|
|
18462
|
-
if (!
|
|
18511
|
+
if (!existsSync26(videoRoot)) return;
|
|
18463
18512
|
for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
|
|
18464
18513
|
if (!videoEntry.isDirectory()) continue;
|
|
18465
18514
|
const videoDir = join40(videoRoot, videoEntry.name);
|
|
@@ -18560,7 +18609,7 @@ function parsePlanVideosJson2(value) {
|
|
|
18560
18609
|
}
|
|
18561
18610
|
function materializeExtractedVideoDirectories(planDir) {
|
|
18562
18611
|
const videosPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
18563
|
-
if (!
|
|
18612
|
+
if (!existsSync26(videosPath)) return;
|
|
18564
18613
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
18565
18614
|
for (const video of videos.extracted) {
|
|
18566
18615
|
mkdirSync16(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
@@ -18594,9 +18643,9 @@ function parseChunkSlices(value) {
|
|
|
18594
18643
|
}
|
|
18595
18644
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
18596
18645
|
const videoRoot = join40(executionPlanDir, "video-frames");
|
|
18597
|
-
const hasExtractedFrames =
|
|
18646
|
+
const hasExtractedFrames = existsSync26(videoRoot) && listFiles(videoRoot).length > 0;
|
|
18598
18647
|
const videosPath = join40(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
18599
|
-
if (!
|
|
18648
|
+
if (!existsSync26(videosPath)) {
|
|
18600
18649
|
return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
|
|
18601
18650
|
}
|
|
18602
18651
|
const chunksPath = join40(executionPlanDir, "meta", "chunks.json");
|
|
@@ -18653,7 +18702,7 @@ function computeManifestHash(manifest) {
|
|
|
18653
18702
|
return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
|
|
18654
18703
|
}
|
|
18655
18704
|
function writeBlob(sourcePath, destinationPath) {
|
|
18656
|
-
if (
|
|
18705
|
+
if (existsSync26(destinationPath)) return;
|
|
18657
18706
|
mkdirSync16(dirname16(destinationPath), { recursive: true });
|
|
18658
18707
|
const temporaryDir = mkdtempSync6(join40(dirname16(destinationPath), ".plan-v2-blob-"));
|
|
18659
18708
|
const temporaryPath = join40(temporaryDir, "blob");
|
|
@@ -18693,7 +18742,7 @@ async function putTasks(tasks) {
|
|
|
18693
18742
|
}
|
|
18694
18743
|
function buildPlanV2Publication(executionPlanDir, digests) {
|
|
18695
18744
|
const executionPlanPath = join40(executionPlanDir, "plan.json");
|
|
18696
|
-
if (!
|
|
18745
|
+
if (!existsSync26(executionPlanPath)) {
|
|
18697
18746
|
throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
|
|
18698
18747
|
}
|
|
18699
18748
|
const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
|
|
@@ -18773,7 +18822,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
18773
18822
|
};
|
|
18774
18823
|
}
|
|
18775
18824
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
18776
|
-
if (
|
|
18825
|
+
if (existsSync26(planV2Dir)) {
|
|
18777
18826
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
18778
18827
|
}
|
|
18779
18828
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
@@ -18859,7 +18908,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
18859
18908
|
}
|
|
18860
18909
|
}
|
|
18861
18910
|
async function planV2(projectDir, config, planV2Dir) {
|
|
18862
|
-
if (
|
|
18911
|
+
if (existsSync26(planV2Dir)) {
|
|
18863
18912
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
18864
18913
|
}
|
|
18865
18914
|
const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
|
|
@@ -19013,7 +19062,7 @@ function parsePlanV2Manifest(value) {
|
|
|
19013
19062
|
}
|
|
19014
19063
|
function readPlanV2Manifest(planV2Dir) {
|
|
19015
19064
|
const manifestPath = join40(planV2Dir, "plan.json");
|
|
19016
|
-
if (!
|
|
19065
|
+
if (!existsSync26(manifestPath)) {
|
|
19017
19066
|
throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
|
|
19018
19067
|
}
|
|
19019
19068
|
const value = readJsonFile(manifestPath, "v2 manifest");
|
|
@@ -19032,7 +19081,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
|
|
|
19032
19081
|
}
|
|
19033
19082
|
function verifyBlob(planV2Dir, artifact) {
|
|
19034
19083
|
const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
|
|
19035
|
-
if (!
|
|
19084
|
+
if (!existsSync26(sourcePath)) {
|
|
19036
19085
|
throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
|
|
19037
19086
|
}
|
|
19038
19087
|
const stats = statSync10(sourcePath);
|
|
@@ -19045,7 +19094,7 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
19045
19094
|
return sourcePath;
|
|
19046
19095
|
}
|
|
19047
19096
|
function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
|
|
19048
|
-
if (
|
|
19097
|
+
if (existsSync26(destinationDir)) {
|
|
19049
19098
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
19050
19099
|
}
|
|
19051
19100
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
@@ -19110,7 +19159,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
19110
19159
|
}
|
|
19111
19160
|
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
19112
19161
|
const markerPath = join40(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
19113
|
-
if (!
|
|
19162
|
+
if (!existsSync26(markerPath)) return null;
|
|
19114
19163
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
19115
19164
|
if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
|
|
19116
19165
|
throw new PlanV2IntegrityError("malformed materialization marker");
|
|
@@ -19128,7 +19177,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
19128
19177
|
for (const artifact of required) {
|
|
19129
19178
|
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
19130
19179
|
const path = join40(planDir, ...artifact.path.split("/"));
|
|
19131
|
-
if (!
|
|
19180
|
+
if (!existsSync26(path) || statSync10(path).size !== artifact.sizeBytes) {
|
|
19132
19181
|
throw new PlanV2IntegrityError(
|
|
19133
19182
|
`materialized artifact missing or truncated: ${artifact.path}`
|
|
19134
19183
|
);
|
|
@@ -19268,7 +19317,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19268
19317
|
const planJsonPath = join41(planDir, "plan.json");
|
|
19269
19318
|
const encoderJsonPath = join41(planDir, "meta", "encoder.json");
|
|
19270
19319
|
const chunksJsonPath = join41(planDir, "meta", "chunks.json");
|
|
19271
|
-
if (!
|
|
19320
|
+
if (!existsSync27(planJsonPath)) {
|
|
19272
19321
|
throw new RenderChunkValidationError(
|
|
19273
19322
|
MISSING_PLAN_ARTIFACT,
|
|
19274
19323
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
@@ -19284,7 +19333,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19284
19333
|
const v2Manifest = v2Materialization?.manifest ?? null;
|
|
19285
19334
|
const remoteSources = v2Materialization?.remoteSources ?? {};
|
|
19286
19335
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
19287
|
-
if (!
|
|
19336
|
+
if (!existsSync27(required)) {
|
|
19288
19337
|
throw new RenderChunkValidationError(
|
|
19289
19338
|
MISSING_PLAN_ARTIFACT,
|
|
19290
19339
|
`[renderChunk] planDir is missing required artifact: ${required}`
|
|
@@ -19295,7 +19344,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19295
19344
|
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
19296
19345
|
const videosJsonPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19297
19346
|
let planVideos = null;
|
|
19298
|
-
if (
|
|
19347
|
+
if (existsSync27(videosJsonPath)) {
|
|
19299
19348
|
try {
|
|
19300
19349
|
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
|
|
19301
19350
|
} catch (err) {
|
|
@@ -19326,7 +19375,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19326
19375
|
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
19327
19376
|
const framesInChunk = range.endFrame - range.startFrame;
|
|
19328
19377
|
const compiledDir = join41(planDir, "compiled");
|
|
19329
|
-
if (!
|
|
19378
|
+
if (!existsSync27(compiledDir)) {
|
|
19330
19379
|
throw new RenderChunkValidationError(
|
|
19331
19380
|
MISSING_PLAN_ARTIFACT,
|
|
19332
19381
|
`[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
|
|
@@ -19576,10 +19625,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19576
19625
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
19577
19626
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
19578
19627
|
if (isPngSequence) {
|
|
19579
|
-
if (!
|
|
19628
|
+
if (!existsSync27(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
|
|
19580
19629
|
} else {
|
|
19581
19630
|
const outDir = join41(outputChunkPath, "..");
|
|
19582
|
-
if (!
|
|
19631
|
+
if (!existsSync27(outDir)) mkdirSync17(outDir, { recursive: true });
|
|
19583
19632
|
}
|
|
19584
19633
|
const encodeInput = {
|
|
19585
19634
|
job,
|
|
@@ -20479,7 +20528,7 @@ import {
|
|
|
20479
20528
|
|
|
20480
20529
|
// src/server.ts
|
|
20481
20530
|
import {
|
|
20482
|
-
existsSync as
|
|
20531
|
+
existsSync as existsSync30,
|
|
20483
20532
|
mkdirSync as mkdirSync19,
|
|
20484
20533
|
statSync as statSync12,
|
|
20485
20534
|
mkdtempSync as mkdtempSync8,
|
|
@@ -20502,7 +20551,7 @@ import {
|
|
|
20502
20551
|
|
|
20503
20552
|
// src/services/hyperframeLint.ts
|
|
20504
20553
|
import {
|
|
20505
|
-
existsSync as
|
|
20554
|
+
existsSync as existsSync28,
|
|
20506
20555
|
readFileSync as readFileSync15,
|
|
20507
20556
|
statSync as statSync11,
|
|
20508
20557
|
openSync as openSync7,
|
|
@@ -20556,7 +20605,7 @@ function readEntryHtml(filePath) {
|
|
|
20556
20605
|
}
|
|
20557
20606
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
20558
20607
|
const absProjectDir = resolve11(projectDir);
|
|
20559
|
-
if (!
|
|
20608
|
+
if (!existsSync28(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
|
|
20560
20609
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
20561
20610
|
}
|
|
20562
20611
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -20625,7 +20674,7 @@ async function runHyperframeLint(prepared) {
|
|
|
20625
20674
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
20626
20675
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
20627
20676
|
import { dirname as dirname17, join as join44 } from "node:path";
|
|
20628
|
-
import { existsSync as
|
|
20677
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
20629
20678
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
20630
20679
|
async function startHealthWorker(options = {}) {
|
|
20631
20680
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -20702,7 +20751,7 @@ function resolveWorkerEntry2() {
|
|
|
20702
20751
|
const here = dirname17(fileURLToPath6(import.meta.url));
|
|
20703
20752
|
const candidates = [join44(here, "healthWorkerThread.js"), join44(here, "healthWorkerThread.ts")];
|
|
20704
20753
|
for (const candidate of candidates) {
|
|
20705
|
-
if (
|
|
20754
|
+
if (existsSync29(candidate)) return candidate;
|
|
20706
20755
|
}
|
|
20707
20756
|
return void 0;
|
|
20708
20757
|
}
|
|
@@ -20848,7 +20897,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
|
20848
20897
|
const { input, cleanupProjectDir } = prepared;
|
|
20849
20898
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
20850
20899
|
const outputDir = dirname18(absoluteOutputPath);
|
|
20851
|
-
if (!
|
|
20900
|
+
if (!existsSync30(outputDir)) mkdirSync19(outputDir, { recursive: true });
|
|
20852
20901
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
20853
20902
|
}
|
|
20854
20903
|
function validateRenderOverrides(body) {
|
|
@@ -20889,11 +20938,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
20889
20938
|
const candidate = nonEmptyString(projectDir);
|
|
20890
20939
|
if (!candidate) return null;
|
|
20891
20940
|
const absProjectDir = resolve12(candidate);
|
|
20892
|
-
if (!
|
|
20941
|
+
if (!existsSync30(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
|
|
20893
20942
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
20894
20943
|
}
|
|
20895
20944
|
const entry = options.entryFile || "index.html";
|
|
20896
|
-
if (!
|
|
20945
|
+
if (!existsSync30(resolve12(absProjectDir, entry))) {
|
|
20897
20946
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
20898
20947
|
}
|
|
20899
20948
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -20985,7 +21034,7 @@ function cleanupTempDir(dir, log) {
|
|
|
20985
21034
|
}
|
|
20986
21035
|
}
|
|
20987
21036
|
function outputFileSize(path) {
|
|
20988
|
-
return
|
|
21037
|
+
return existsSync30(path) ? statSync12(path).size : 0;
|
|
20989
21038
|
}
|
|
20990
21039
|
function createBlockingProgressReporter(log, requestId) {
|
|
20991
21040
|
let lastLoggedPct = -10;
|
|
@@ -21289,7 +21338,7 @@ function createRenderHandlers(options = {}) {
|
|
|
21289
21338
|
if (!artifact) {
|
|
21290
21339
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
21291
21340
|
}
|
|
21292
|
-
if (!
|
|
21341
|
+
if (!existsSync30(artifact.path)) {
|
|
21293
21342
|
store.delete(token);
|
|
21294
21343
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
21295
21344
|
}
|
|
@@ -21395,7 +21444,7 @@ import {
|
|
|
21395
21444
|
} from "@hyperframes/engine";
|
|
21396
21445
|
import {
|
|
21397
21446
|
cpSync as cpSync3,
|
|
21398
|
-
existsSync as
|
|
21447
|
+
existsSync as existsSync31,
|
|
21399
21448
|
mkdirSync as mkdirSync20,
|
|
21400
21449
|
mkdtempSync as mkdtempSync9,
|
|
21401
21450
|
readFileSync as readFileSync16,
|
|
@@ -21450,7 +21499,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21450
21499
|
}
|
|
21451
21500
|
const format = plan2.dimensions.format;
|
|
21452
21501
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
21453
|
-
if (!
|
|
21502
|
+
if (!existsSync31(dirname19(outputPath))) {
|
|
21454
21503
|
mkdirSync20(dirname19(outputPath), { recursive: true });
|
|
21455
21504
|
}
|
|
21456
21505
|
const workDir = mkdtempSync9(`${outputPath}.assemble-work-`);
|
|
@@ -21474,7 +21523,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21474
21523
|
try {
|
|
21475
21524
|
const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
|
|
21476
21525
|
const finalPath = join46(workDir, `output.${format}`);
|
|
21477
|
-
const audio = audioPath !== null &&
|
|
21526
|
+
const audio = audioPath !== null && existsSync31(audioPath) ? audioPath : null;
|
|
21478
21527
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
21479
21528
|
renameWithRetry11(finalPath, outputPath);
|
|
21480
21529
|
log.info("[assemble] stage timings", {
|
|
@@ -21492,7 +21541,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21492
21541
|
});
|
|
21493
21542
|
}
|
|
21494
21543
|
}
|
|
21495
|
-
const fileSize =
|
|
21544
|
+
const fileSize = existsSync31(outputPath) ? statSync13(outputPath).size : 0;
|
|
21496
21545
|
return {
|
|
21497
21546
|
...referenceBackgroundResult8(plan2[REFERENCE_BACKGROUND_FIELD6]),
|
|
21498
21547
|
outputPath,
|
|
@@ -21504,13 +21553,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21504
21553
|
function readAssemblePlan(planDir, chunkPaths) {
|
|
21505
21554
|
const planJsonPath = join46(planDir, "plan.json");
|
|
21506
21555
|
const chunksJsonPath = join46(planDir, "meta", "chunks.json");
|
|
21507
|
-
if (!
|
|
21556
|
+
if (!existsSync31(planJsonPath)) {
|
|
21508
21557
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
21509
21558
|
}
|
|
21510
21559
|
const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
|
|
21511
21560
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
21512
21561
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
21513
|
-
if (!
|
|
21562
|
+
if (!existsSync31(chunksJsonPath)) {
|
|
21514
21563
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
21515
21564
|
}
|
|
21516
21565
|
const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
|
|
@@ -21525,7 +21574,7 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
21525
21574
|
}
|
|
21526
21575
|
}
|
|
21527
21576
|
for (const path of chunkPaths.flat()) {
|
|
21528
|
-
if (!
|
|
21577
|
+
if (!existsSync31(path)) {
|
|
21529
21578
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
21530
21579
|
}
|
|
21531
21580
|
}
|
|
@@ -21664,7 +21713,7 @@ function planFormat(planDir) {
|
|
|
21664
21713
|
}
|
|
21665
21714
|
function lockedAudio(planDir) {
|
|
21666
21715
|
const encoderJsonPath = join46(planDir, "meta", "encoder.json");
|
|
21667
|
-
if (!
|
|
21716
|
+
if (!existsSync31(encoderJsonPath)) return {};
|
|
21668
21717
|
return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
21669
21718
|
}
|
|
21670
21719
|
function readCfrEncoder(planDir, format) {
|
|
@@ -21674,7 +21723,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
21674
21723
|
);
|
|
21675
21724
|
}
|
|
21676
21725
|
const encoderJsonPath = join46(planDir, "meta", "encoder.json");
|
|
21677
|
-
if (!
|
|
21726
|
+
if (!existsSync31(encoderJsonPath)) {
|
|
21678
21727
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
21679
21728
|
}
|
|
21680
21729
|
const encoderJson = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
@@ -21686,7 +21735,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
21686
21735
|
return encoderJson;
|
|
21687
21736
|
}
|
|
21688
21737
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
21689
|
-
if (
|
|
21738
|
+
if (existsSync31(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
|
|
21690
21739
|
mkdirSync20(outputPath, { recursive: true });
|
|
21691
21740
|
let globalIdx = 0;
|
|
21692
21741
|
for (const chunkDir of chunkPaths) {
|
|
@@ -21710,7 +21759,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
21710
21759
|
`[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
|
|
21711
21760
|
);
|
|
21712
21761
|
}
|
|
21713
|
-
if (audioPath !== null &&
|
|
21762
|
+
if (audioPath !== null && existsSync31(audioPath)) {
|
|
21714
21763
|
const sidecar = join46(outputPath, MIXED_AUDIO_FILENAME4);
|
|
21715
21764
|
cpSync3(audioPath, sidecar);
|
|
21716
21765
|
}
|