@hyperframes/producer 0.9.15 → 0.9.16
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 +135 -112
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +68 -68
- package/dist/index.js +158 -161
- package/dist/public-server.js +79 -81
- package/dist/services/render/stages/compileStage.d.ts +12 -3
- package/dist/services/renderOrchestrator.d.ts +2 -5
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -9,12 +9,12 @@ 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 existsSync20,
|
|
13
13
|
mkdirSync as mkdirSync10,
|
|
14
14
|
readdirSync as readdirSync5,
|
|
15
15
|
rmSync as rmSync10,
|
|
16
16
|
statSync as statSync8,
|
|
17
|
-
writeFileSync as
|
|
17
|
+
writeFileSync as writeFileSync7,
|
|
18
18
|
copyFileSync as copyFileSync5,
|
|
19
19
|
appendFileSync
|
|
20
20
|
} from "fs";
|
|
@@ -29,9 +29,7 @@ import {
|
|
|
29
29
|
toFps as toFps2
|
|
30
30
|
} from "@hyperframes/core";
|
|
31
31
|
import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
|
|
32
|
-
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
33
32
|
import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
|
|
34
|
-
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
35
33
|
import {
|
|
36
34
|
extractMediaMetadata as extractMediaMetadata4,
|
|
37
35
|
resolveConfig as resolveConfig2,
|
|
@@ -62,7 +60,8 @@ import {
|
|
|
62
60
|
isDrawElementCaptureError,
|
|
63
61
|
getDrawElementVerificationDetails,
|
|
64
62
|
augmentProtocolTimeoutError,
|
|
65
|
-
augmentPageNavigationTimeoutError
|
|
63
|
+
augmentPageNavigationTimeoutError,
|
|
64
|
+
displaySize
|
|
66
65
|
} from "@hyperframes/engine";
|
|
67
66
|
import {
|
|
68
67
|
referenceBackgroundResult,
|
|
@@ -5895,8 +5894,11 @@ function countAuthoredTimedClips(html) {
|
|
|
5895
5894
|
}
|
|
5896
5895
|
|
|
5897
5896
|
// src/services/render/stages/compileStage.ts
|
|
5897
|
+
import { existsSync as existsSync11, writeFileSync as writeFileSync5 } from "node:fs";
|
|
5898
5898
|
import { join as join16 } from "node:path";
|
|
5899
5899
|
import { suggestMatchingPreset } from "@hyperframes/core";
|
|
5900
|
+
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
5901
|
+
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
5900
5902
|
|
|
5901
5903
|
// src/services/htmlCompiler.ts
|
|
5902
5904
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
@@ -8604,11 +8606,36 @@ function adaptAspectAgnosticResolution(requested, aspectAgnostic, width, height,
|
|
|
8604
8606
|
});
|
|
8605
8607
|
return flipped;
|
|
8606
8608
|
}
|
|
8609
|
+
function resolveEntryHtmlPath(input) {
|
|
8610
|
+
const { projectDir, workDir, entryFile, log } = input;
|
|
8611
|
+
const htmlPath = join16(projectDir, entryFile);
|
|
8612
|
+
const rawEntry = readTextFile2(htmlPath);
|
|
8613
|
+
if (entryFile === "index.html" || !isTemplateEntry(rawEntry)) return htmlPath;
|
|
8614
|
+
const projectIndexPath = join16(projectDir, "index.html");
|
|
8615
|
+
if (!existsSync11(projectIndexPath)) {
|
|
8616
|
+
throw new Error(
|
|
8617
|
+
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
8618
|
+
);
|
|
8619
|
+
}
|
|
8620
|
+
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
8621
|
+
readTextFile2(projectIndexPath),
|
|
8622
|
+
entryFile,
|
|
8623
|
+
rawEntry
|
|
8624
|
+
);
|
|
8625
|
+
if (!standaloneHtml) {
|
|
8626
|
+
throw new Error(
|
|
8627
|
+
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
8628
|
+
);
|
|
8629
|
+
}
|
|
8630
|
+
const wrapperPath = join16(workDir, "standalone-entry.html");
|
|
8631
|
+
writeFileSync5(wrapperPath, standaloneHtml, "utf-8");
|
|
8632
|
+
log.info("Extracted standalone entry from index.html host context", { entryFile });
|
|
8633
|
+
return wrapperPath;
|
|
8634
|
+
}
|
|
8607
8635
|
async function runCompileStage(input) {
|
|
8608
8636
|
const {
|
|
8609
8637
|
projectDir,
|
|
8610
8638
|
workDir,
|
|
8611
|
-
htmlPath,
|
|
8612
8639
|
entryFile,
|
|
8613
8640
|
job,
|
|
8614
8641
|
cfg,
|
|
@@ -8619,6 +8646,7 @@ async function runCompileStage(input) {
|
|
|
8619
8646
|
allowSystemFontCapture,
|
|
8620
8647
|
abortSignal
|
|
8621
8648
|
} = input;
|
|
8649
|
+
const htmlPath = resolveEntryHtmlPath({ projectDir, workDir, entryFile, log });
|
|
8622
8650
|
const compileStart = Date.now();
|
|
8623
8651
|
const compiled = await compileForRender(projectDir, htmlPath, join16(workDir, "downloads"), {
|
|
8624
8652
|
log,
|
|
@@ -9450,7 +9478,7 @@ function validateDistributedDuration(input) {
|
|
|
9450
9478
|
}
|
|
9451
9479
|
|
|
9452
9480
|
// src/services/render/stages/extractVideosStage.ts
|
|
9453
|
-
import { existsSync as
|
|
9481
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
9454
9482
|
import { join as join18 } from "node:path";
|
|
9455
9483
|
import {
|
|
9456
9484
|
classifyVideoExtractionError,
|
|
@@ -9725,7 +9753,7 @@ async function runExtractVideosStage(input) {
|
|
|
9725
9753
|
const probeFailures = await Promise.all(
|
|
9726
9754
|
composition.videos.map(async (v) => {
|
|
9727
9755
|
const videoPath = resolveProjectRelativeSrc3(v.src, projectDir, compiledDir);
|
|
9728
|
-
if (!
|
|
9756
|
+
if (!existsSync12(videoPath)) return null;
|
|
9729
9757
|
try {
|
|
9730
9758
|
const attempted = extractionPolicy.maxTransientRetries === 0 ? { result: await extractMediaMetadata3(videoPath), retries: 0 } : await runVideoExtractionWithRetry(() => extractMediaMetadata3(videoPath), {
|
|
9731
9759
|
signal: abortSignal,
|
|
@@ -9763,7 +9791,7 @@ async function runExtractVideosStage(input) {
|
|
|
9763
9791
|
const probed = await Promise.all(
|
|
9764
9792
|
composition.images.map(async (img) => {
|
|
9765
9793
|
const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
|
|
9766
|
-
if (!
|
|
9794
|
+
if (!existsSync12(imgPath)) return null;
|
|
9767
9795
|
const colorSpace = await probeColorSpaceSafely(imgPath, log);
|
|
9768
9796
|
if (isHdrColorSpace2(colorSpace)) {
|
|
9769
9797
|
nativeHdrImageIds.add(img.id);
|
|
@@ -9883,7 +9911,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
9883
9911
|
videos.map(async (video) => {
|
|
9884
9912
|
const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
|
|
9885
9913
|
const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
|
|
9886
|
-
if (!isHttpUrl2(readFrom) && !
|
|
9914
|
+
if (!isHttpUrl2(readFrom) && !existsSync12(readFrom)) return null;
|
|
9887
9915
|
const metadata = await extractMediaMetadata3(readFrom);
|
|
9888
9916
|
const window2 = await resolveFinalFrameExtractionWindow2(
|
|
9889
9917
|
readFrom,
|
|
@@ -9903,7 +9931,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
9903
9931
|
}
|
|
9904
9932
|
|
|
9905
9933
|
// src/services/render/videoLayerPassthroughPlan.ts
|
|
9906
|
-
import { existsSync as
|
|
9934
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
9907
9935
|
import {
|
|
9908
9936
|
buildVideoLayerInput,
|
|
9909
9937
|
isHttpUrl as isHttpUrl3,
|
|
@@ -9971,7 +9999,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
9971
9999
|
const { video } = q;
|
|
9972
10000
|
if (isHttpUrl3(video.src)) return { reason: "remote_source" };
|
|
9973
10001
|
const path = resolveProjectRelativeSrc4(video.src, input.projectDir, input.compiledDir);
|
|
9974
|
-
if (!
|
|
10002
|
+
if (!existsSync13(path)) return { reason: "source_missing" };
|
|
9975
10003
|
const layout = await probeVideoLayout(input.page, video.id, input.canvas, input.duration);
|
|
9976
10004
|
if ("reason" in layout) return layout;
|
|
9977
10005
|
const layer = await buildVideoLayerInput(
|
|
@@ -9986,7 +10014,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
9986
10014
|
}
|
|
9987
10015
|
|
|
9988
10016
|
// src/services/render/stages/audioStage.ts
|
|
9989
|
-
import { existsSync as
|
|
10017
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
9990
10018
|
import { join as join21 } from "node:path";
|
|
9991
10019
|
import {
|
|
9992
10020
|
MIXED_AUDIO_FILENAME,
|
|
@@ -9997,7 +10025,7 @@ import {
|
|
|
9997
10025
|
} from "@hyperframes/engine";
|
|
9998
10026
|
|
|
9999
10027
|
// src/services/render/audioDeliveryLimit.ts
|
|
10000
|
-
import { existsSync as
|
|
10028
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
10001
10029
|
import { dirname as dirname9, join as join20 } from "node:path";
|
|
10002
10030
|
import {
|
|
10003
10031
|
applyAudioFxChain,
|
|
@@ -10504,7 +10532,7 @@ async function limitDeliveryPeaks(input, deps = {
|
|
|
10504
10532
|
applyChain: applyAudioFxChain,
|
|
10505
10533
|
encode: encodeAac2,
|
|
10506
10534
|
rename: renameWithRetry6,
|
|
10507
|
-
sourceExists:
|
|
10535
|
+
sourceExists: existsSync14
|
|
10508
10536
|
}) {
|
|
10509
10537
|
const { audioPath, sourcePath, signal, timeoutMs } = input;
|
|
10510
10538
|
if (!deps.sourceExists(sourcePath)) return { success: true };
|
|
@@ -10574,7 +10602,7 @@ async function runAudioStageScoped(input) {
|
|
|
10574
10602
|
);
|
|
10575
10603
|
audioLoweredDb = guard.loweredDb;
|
|
10576
10604
|
input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
|
|
10577
|
-
if (input.keepDeliverySource && !guard.sourceStale &&
|
|
10605
|
+
if (input.keepDeliverySource && !guard.sourceStale && existsSync15(pcmSourcePath)) {
|
|
10578
10606
|
deliverySourcePath = pcmSourcePath;
|
|
10579
10607
|
}
|
|
10580
10608
|
}
|
|
@@ -10702,7 +10730,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10702
10730
|
}
|
|
10703
10731
|
|
|
10704
10732
|
// src/services/render/stages/captureStage.ts
|
|
10705
|
-
import { existsSync as
|
|
10733
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
10706
10734
|
import { join as join22 } from "node:path";
|
|
10707
10735
|
import {
|
|
10708
10736
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -10773,7 +10801,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
|
|
|
10773
10801
|
}
|
|
10774
10802
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
10775
10803
|
let total = sampleDirectoryBytes(framesDir);
|
|
10776
|
-
if (!
|
|
10804
|
+
if (!existsSync16(workDir)) return total;
|
|
10777
10805
|
for (const attempt of readdirSync3(workDir)) {
|
|
10778
10806
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
10779
10807
|
total += sampleDirectoryBytes(join22(workDir, attempt));
|
|
@@ -11076,7 +11104,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
11076
11104
|
}
|
|
11077
11105
|
|
|
11078
11106
|
// src/services/render/stages/captureHdrStage.ts
|
|
11079
|
-
import { existsSync as
|
|
11107
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8 } from "node:fs";
|
|
11080
11108
|
import { join as join26 } from "node:path";
|
|
11081
11109
|
import {
|
|
11082
11110
|
closeCaptureSession as closeCaptureSession7,
|
|
@@ -11322,7 +11350,7 @@ import { Worker } from "node:worker_threads";
|
|
|
11322
11350
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
11323
11351
|
import { dirname as dirname10, join as join24 } from "node:path";
|
|
11324
11352
|
import { createRequire } from "node:module";
|
|
11325
|
-
import { existsSync as
|
|
11353
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
11326
11354
|
import { cpus as cpus2 } from "node:os";
|
|
11327
11355
|
function resolveWorkerEntry(explicit) {
|
|
11328
11356
|
if (explicit && explicit.length > 0) {
|
|
@@ -11335,7 +11363,7 @@ function resolveWorkerEntry(explicit) {
|
|
|
11335
11363
|
}
|
|
11336
11364
|
const moduleDir = dirname10(fileURLToPath3(import.meta.url));
|
|
11337
11365
|
const jsPath = join24(moduleDir, "shaderTransitionWorker.js");
|
|
11338
|
-
if (
|
|
11366
|
+
if (existsSync17(jsPath)) return { path: jsPath, isTs: false };
|
|
11339
11367
|
const tsPath = join24(moduleDir, "shaderTransitionWorker.ts");
|
|
11340
11368
|
return { path: tsPath, isTs: true };
|
|
11341
11369
|
}
|
|
@@ -11896,7 +11924,7 @@ async function runCaptureHdrStage(input) {
|
|
|
11896
11924
|
}
|
|
11897
11925
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
11898
11926
|
const debugDumpDir = debugDumpEnabled ? join26(framesDir, "debug-composite") : null;
|
|
11899
|
-
if (debugDumpDir && !
|
|
11927
|
+
if (debugDumpDir && !existsSync18(debugDumpDir)) {
|
|
11900
11928
|
mkdirSync8(debugDumpDir, { recursive: true });
|
|
11901
11929
|
}
|
|
11902
11930
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
@@ -12053,13 +12081,13 @@ async function runCaptureHdrStage(input) {
|
|
|
12053
12081
|
// src/services/render/stages/encodeStage.ts
|
|
12054
12082
|
import {
|
|
12055
12083
|
copyFileSync as copyFileSync4,
|
|
12056
|
-
existsSync as
|
|
12084
|
+
existsSync as existsSync19,
|
|
12057
12085
|
mkdirSync as mkdirSync9,
|
|
12058
12086
|
readdirSync as readdirSync4,
|
|
12059
12087
|
readFileSync as readFileSync8,
|
|
12060
12088
|
rmSync as rmSync9,
|
|
12061
12089
|
statSync as statSync7,
|
|
12062
|
-
writeFileSync as
|
|
12090
|
+
writeFileSync as writeFileSync6
|
|
12063
12091
|
} from "node:fs";
|
|
12064
12092
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
12065
12093
|
import { dirname as dirname11, join as join27 } from "node:path";
|
|
@@ -12155,7 +12183,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
12155
12183
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
12156
12184
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
12157
12185
|
}
|
|
12158
|
-
|
|
12186
|
+
writeFileSync6(outputPath, gif);
|
|
12159
12187
|
return null;
|
|
12160
12188
|
}
|
|
12161
12189
|
async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
@@ -12187,7 +12215,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
12187
12215
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
12188
12216
|
if (failure) return failure;
|
|
12189
12217
|
}
|
|
12190
|
-
const fileSize =
|
|
12218
|
+
const fileSize = existsSync19(outputPath) ? statSync7(outputPath).size : 0;
|
|
12191
12219
|
return {
|
|
12192
12220
|
success: true,
|
|
12193
12221
|
outputPath,
|
|
@@ -12348,7 +12376,7 @@ async function runEncodeStage(input) {
|
|
|
12348
12376
|
const stage5Start = Date.now();
|
|
12349
12377
|
if (isPngSequence) {
|
|
12350
12378
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
12351
|
-
if (!
|
|
12379
|
+
if (!existsSync19(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
12352
12380
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
12353
12381
|
if (captured.length === 0) {
|
|
12354
12382
|
throw new Error(
|
|
@@ -12359,7 +12387,7 @@ async function runEncodeStage(input) {
|
|
|
12359
12387
|
const dst = join27(outputPath, formatExportFrameName(i, "png"));
|
|
12360
12388
|
copyFileSync4(join27(framesDir, name), dst);
|
|
12361
12389
|
});
|
|
12362
|
-
if (hasAudio && audioOutputPath &&
|
|
12390
|
+
if (hasAudio && audioOutputPath && existsSync19(audioOutputPath)) {
|
|
12363
12391
|
copyFileSync4(audioOutputPath, join27(outputPath, MIXED_AUDIO_FILENAME2));
|
|
12364
12392
|
log.info(
|
|
12365
12393
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
@@ -12741,18 +12769,14 @@ function hasUsableVideoDimensions(metadata) {
|
|
|
12741
12769
|
return Number.isFinite(metadata.width) && Number.isFinite(metadata.height) && metadata.width > 0 && metadata.height > 0;
|
|
12742
12770
|
}
|
|
12743
12771
|
function collectVideoMetadataHints(extractedVideos) {
|
|
12744
|
-
return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({
|
|
12745
|
-
id: video.videoId,
|
|
12746
|
-
width: video.metadata.width,
|
|
12747
|
-
height: video.metadata.height
|
|
12748
|
-
})).sort((a, b) => a.id.localeCompare(b.id));
|
|
12772
|
+
return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({ id: video.videoId, ...displaySize(video.metadata) })).sort((a, b) => a.id.localeCompare(b.id));
|
|
12749
12773
|
}
|
|
12750
12774
|
function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
12751
12775
|
const ranges = [];
|
|
12752
12776
|
let rangeStart = null;
|
|
12753
12777
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12754
12778
|
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12755
|
-
const missing = !
|
|
12779
|
+
const missing = !existsSync20(framePath) || statSync8(framePath).size <= 8;
|
|
12756
12780
|
if (missing && rangeStart === null) {
|
|
12757
12781
|
rangeStart = frameIndex;
|
|
12758
12782
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -12877,7 +12901,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
|
12877
12901
|
let captured = 0;
|
|
12878
12902
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12879
12903
|
const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12880
|
-
if (
|
|
12904
|
+
if (existsSync20(framePath)) captured++;
|
|
12881
12905
|
}
|
|
12882
12906
|
return captured;
|
|
12883
12907
|
}
|
|
@@ -13542,7 +13566,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
13542
13566
|
validateDeliveryEncoderConfig(job.config);
|
|
13543
13567
|
const debugDir = resolveRenderDebugDir();
|
|
13544
13568
|
const outputDir = dirname12(outputPath);
|
|
13545
|
-
if (!
|
|
13569
|
+
if (!existsSync20(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
13546
13570
|
const workDir = job.config.debug ? join28(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
13547
13571
|
const pipelineStart = Date.now();
|
|
13548
13572
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
@@ -13684,7 +13708,7 @@ async function executeRenderPipeline(input) {
|
|
|
13684
13708
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
13685
13709
|
assertNotAborted();
|
|
13686
13710
|
assertConfiguredFfmpegBinariesExist();
|
|
13687
|
-
if (!
|
|
13711
|
+
if (!existsSync20(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
13688
13712
|
if (job.config.debug) {
|
|
13689
13713
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
13690
13714
|
}
|
|
@@ -13713,36 +13737,11 @@ async function executeRenderPipeline(input) {
|
|
|
13713
13737
|
requestedWorkers: job.config.workers ?? "auto"
|
|
13714
13738
|
});
|
|
13715
13739
|
const entryFile = job.config.entryFile || "index.html";
|
|
13716
|
-
|
|
13717
|
-
if (!
|
|
13740
|
+
const htmlPath = join28(projectDir, entryFile);
|
|
13741
|
+
if (!existsSync20(htmlPath)) {
|
|
13718
13742
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
13719
13743
|
}
|
|
13720
13744
|
assertNotAborted();
|
|
13721
|
-
const rawEntry = readTextFile2(htmlPath);
|
|
13722
|
-
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
13723
|
-
const wrapperPath = join28(workDir, "standalone-entry.html");
|
|
13724
|
-
const projectIndexPath = join28(projectDir, "index.html");
|
|
13725
|
-
if (!existsSync19(projectIndexPath)) {
|
|
13726
|
-
throw new Error(
|
|
13727
|
-
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
13728
|
-
);
|
|
13729
|
-
}
|
|
13730
|
-
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
13731
|
-
readTextFile2(projectIndexPath),
|
|
13732
|
-
entryFile,
|
|
13733
|
-
rawEntry
|
|
13734
|
-
);
|
|
13735
|
-
if (!standaloneHtml) {
|
|
13736
|
-
throw new Error(
|
|
13737
|
-
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
13738
|
-
);
|
|
13739
|
-
}
|
|
13740
|
-
writeFileSync6(wrapperPath, standaloneHtml, "utf-8");
|
|
13741
|
-
htmlPath = wrapperPath;
|
|
13742
|
-
log.info("Extracted standalone entry from index.html host context", {
|
|
13743
|
-
entryFile
|
|
13744
|
-
});
|
|
13745
|
-
}
|
|
13746
13745
|
const stage1Start = Date.now();
|
|
13747
13746
|
updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress, {
|
|
13748
13747
|
code: "compile"
|
|
@@ -13754,7 +13753,6 @@ async function executeRenderPipeline(input) {
|
|
|
13754
13753
|
() => runCompileStage({
|
|
13755
13754
|
projectDir,
|
|
13756
13755
|
workDir,
|
|
13757
|
-
htmlPath,
|
|
13758
13756
|
entryFile,
|
|
13759
13757
|
job,
|
|
13760
13758
|
cfg,
|
|
@@ -14066,7 +14064,7 @@ async function executeRenderPipeline(input) {
|
|
|
14066
14064
|
throw new Error("File server failed to initialize before frame capture");
|
|
14067
14065
|
}
|
|
14068
14066
|
const framesDir = join28(workDir, "captured-frames");
|
|
14069
|
-
if (!
|
|
14067
|
+
if (!existsSync20(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
14070
14068
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
14071
14069
|
code: "start_browsers"
|
|
14072
14070
|
});
|
|
@@ -15394,7 +15392,7 @@ async function executeRenderPipeline(input) {
|
|
|
15394
15392
|
})
|
|
15395
15393
|
);
|
|
15396
15394
|
const totalElapsed = Date.now() - pipelineStart;
|
|
15397
|
-
const tmpPeakBytes =
|
|
15395
|
+
const tmpPeakBytes = existsSync20(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
15398
15396
|
recordTransientRetryObservability();
|
|
15399
15397
|
observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
|
|
15400
15398
|
const observabilitySummary = observability.summary({
|
|
@@ -15466,7 +15464,7 @@ async function executeRenderPipeline(input) {
|
|
|
15466
15464
|
job.perfSummary = perfSummary;
|
|
15467
15465
|
if (job.config.debug) {
|
|
15468
15466
|
try {
|
|
15469
|
-
|
|
15467
|
+
writeFileSync7(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
|
|
15470
15468
|
} catch (err) {
|
|
15471
15469
|
log.debug("Failed to write perf summary", {
|
|
15472
15470
|
perfOutputPath,
|
|
@@ -15475,7 +15473,7 @@ async function executeRenderPipeline(input) {
|
|
|
15475
15473
|
}
|
|
15476
15474
|
}
|
|
15477
15475
|
if (job.config.debug) {
|
|
15478
|
-
if (!isPngSequence && !isHls &&
|
|
15476
|
+
if (!isPngSequence && !isHls && existsSync20(stagedOutputPath)) {
|
|
15479
15477
|
const debugOutput = join28(workDir, `output${videoExt}`);
|
|
15480
15478
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
15481
15479
|
}
|
|
@@ -15987,11 +15985,11 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
|
|
|
15987
15985
|
import {
|
|
15988
15986
|
copyFileSync as copyFileSync6,
|
|
15989
15987
|
cpSync as cpSync2,
|
|
15990
|
-
existsSync as
|
|
15988
|
+
existsSync as existsSync25,
|
|
15991
15989
|
linkSync,
|
|
15992
15990
|
mkdirSync as mkdirSync14,
|
|
15993
15991
|
rmSync as rmSync12,
|
|
15994
|
-
writeFileSync as
|
|
15992
|
+
writeFileSync as writeFileSync10
|
|
15995
15993
|
} from "node:fs";
|
|
15996
15994
|
import { extname as extname6, isAbsolute as isAbsolute3, join as join37, relative as relative5, sep as sep2 } from "node:path";
|
|
15997
15995
|
import {
|
|
@@ -16018,7 +16016,7 @@ import {
|
|
|
16018
16016
|
} from "@hyperframes/engine";
|
|
16019
16017
|
|
|
16020
16018
|
// src/services/distributed/chunkFrames.ts
|
|
16021
|
-
import { existsSync as
|
|
16019
|
+
import { existsSync as existsSync22, readdirSync as readdirSync6 } from "node:fs";
|
|
16022
16020
|
import { extname as extname4, join as join31 } from "node:path";
|
|
16023
16021
|
import {
|
|
16024
16022
|
compositionRequiresWebGpu as compositionRequiresWebGpu3,
|
|
@@ -16030,7 +16028,7 @@ import {
|
|
|
16030
16028
|
// src/services/distributed/shared.ts
|
|
16031
16029
|
import { execFile as execFileCallback } from "node:child_process";
|
|
16032
16030
|
import { dirname as dirname14, join as join30 } from "node:path";
|
|
16033
|
-
import { existsSync as
|
|
16031
|
+
import { existsSync as existsSync21, readFileSync as readFileSync9 } from "node:fs";
|
|
16034
16032
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
16035
16033
|
import { promisify as promisify2 } from "node:util";
|
|
16036
16034
|
import {
|
|
@@ -16046,7 +16044,7 @@ function isPlanAudioArtifactPath(path) {
|
|
|
16046
16044
|
function resolvePlanAudioPath(planDir) {
|
|
16047
16045
|
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
16048
16046
|
const candidate = join30(planDir, name);
|
|
16049
|
-
if (
|
|
16047
|
+
if (existsSync21(candidate)) return candidate;
|
|
16050
16048
|
}
|
|
16051
16049
|
return null;
|
|
16052
16050
|
}
|
|
@@ -16307,7 +16305,7 @@ function readProducerVersion() {
|
|
|
16307
16305
|
let current = startDir;
|
|
16308
16306
|
for (let i = 0; i < 10; i++) {
|
|
16309
16307
|
const candidate = join30(current, "package.json");
|
|
16310
|
-
if (
|
|
16308
|
+
if (existsSync21(candidate)) {
|
|
16311
16309
|
try {
|
|
16312
16310
|
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
16313
16311
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
@@ -16361,7 +16359,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
|
|
|
16361
16359
|
const result = [];
|
|
16362
16360
|
for (const v of videos) {
|
|
16363
16361
|
const outputDir = join31(planDir, "video-frames", v.videoId);
|
|
16364
|
-
if (!
|
|
16362
|
+
if (!existsSync22(outputDir)) {
|
|
16365
16363
|
throw new Error(
|
|
16366
16364
|
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
16367
16365
|
);
|
|
@@ -16477,7 +16475,7 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
16477
16475
|
const range = video.deferredRange;
|
|
16478
16476
|
const remoteUrl = remoteSources[range.sourcePath];
|
|
16479
16477
|
const sourcePath = remoteUrl ?? join31(input.planDir, ...range.sourcePath.split("/"));
|
|
16480
|
-
if (remoteUrl === void 0 && !
|
|
16478
|
+
if (remoteUrl === void 0 && !existsSync22(sourcePath)) {
|
|
16481
16479
|
throw new RenderChunkValidationError(
|
|
16482
16480
|
MISSING_PLAN_ARTIFACT,
|
|
16483
16481
|
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
@@ -16695,7 +16693,7 @@ async function readPlanReferenceBackground(input) {
|
|
|
16695
16693
|
}
|
|
16696
16694
|
|
|
16697
16695
|
// src/services/render/stages/freezePlan.ts
|
|
16698
|
-
import { existsSync as
|
|
16696
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16699
16697
|
import { join as join34, relative as relative3, resolve as resolve9 } from "node:path";
|
|
16700
16698
|
import {
|
|
16701
16699
|
REFERENCE_BACKGROUND_FIELD,
|
|
@@ -16889,7 +16887,7 @@ function planFileSha256(absolutePath, digests) {
|
|
|
16889
16887
|
return sha256;
|
|
16890
16888
|
}
|
|
16891
16889
|
async function prehashPlanFiles(dir, digests) {
|
|
16892
|
-
if (!
|
|
16890
|
+
if (!existsSync23(dir)) return;
|
|
16893
16891
|
for (const { absolutePath } of listPlanFiles(dir)) {
|
|
16894
16892
|
if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
|
|
16895
16893
|
}
|
|
@@ -16955,19 +16953,19 @@ var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
|
|
|
16955
16953
|
function writePlanRemoteFiles(planDir, files) {
|
|
16956
16954
|
if (files.length === 0) return;
|
|
16957
16955
|
const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
|
|
16958
|
-
|
|
16956
|
+
writeFileSync8(join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
|
|
16959
16957
|
}
|
|
16960
16958
|
function readPlanRemoteFiles(planDir) {
|
|
16961
16959
|
const path = join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
16962
|
-
return
|
|
16960
|
+
return existsSync23(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
16963
16961
|
}
|
|
16964
16962
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
16965
16963
|
const planJsonPath = join34(planDir, "plan.json");
|
|
16966
16964
|
const encoderJsonPath = join34(planDir, "meta", "encoder.json");
|
|
16967
|
-
if (!
|
|
16965
|
+
if (!existsSync23(planJsonPath)) {
|
|
16968
16966
|
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
16969
16967
|
}
|
|
16970
|
-
if (!
|
|
16968
|
+
if (!existsSync23(encoderJsonPath)) {
|
|
16971
16969
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
16972
16970
|
}
|
|
16973
16971
|
const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
@@ -16997,12 +16995,12 @@ async function freezePlan(input) {
|
|
|
16997
16995
|
hasAudio,
|
|
16998
16996
|
digests
|
|
16999
16997
|
} = input;
|
|
17000
|
-
if (!
|
|
16998
|
+
if (!existsSync23(planDir)) {
|
|
17001
16999
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
17002
17000
|
}
|
|
17003
17001
|
const metaDir = join34(planDir, "meta");
|
|
17004
|
-
if (!
|
|
17005
|
-
|
|
17002
|
+
if (!existsSync23(metaDir)) mkdirSync12(metaDir, { recursive: true });
|
|
17003
|
+
writeFileSync8(
|
|
17006
17004
|
join34(metaDir, "composition.json"),
|
|
17007
17005
|
`${JSON.stringify(composition, null, 2)}
|
|
17008
17006
|
`,
|
|
@@ -17010,8 +17008,8 @@ async function freezePlan(input) {
|
|
|
17010
17008
|
);
|
|
17011
17009
|
const encoderForCanonical = stripUndefined(encoder);
|
|
17012
17010
|
const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
|
|
17013
|
-
|
|
17014
|
-
|
|
17011
|
+
writeFileSync8(join34(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
|
|
17012
|
+
writeFileSync8(join34(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
|
|
17015
17013
|
`, "utf-8");
|
|
17016
17014
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
17017
17015
|
const planHash = computePlanHash({
|
|
@@ -17037,7 +17035,7 @@ async function freezePlan(input) {
|
|
|
17037
17035
|
hasAudio
|
|
17038
17036
|
};
|
|
17039
17037
|
const planJsonPath = join34(planDir, "plan.json");
|
|
17040
|
-
|
|
17038
|
+
writeFileSync8(planJsonPath, `${JSON.stringify(planJson, null, 2)}
|
|
17041
17039
|
`, "utf-8");
|
|
17042
17040
|
return { planJsonPath, planHash };
|
|
17043
17041
|
}
|
|
@@ -17078,8 +17076,8 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
17078
17076
|
}
|
|
17079
17077
|
|
|
17080
17078
|
// src/services/distributed/chunkAudio.ts
|
|
17081
|
-
import { closeSync as closeSync6, existsSync as
|
|
17082
|
-
import { writeFileSync as
|
|
17079
|
+
import { closeSync as closeSync6, existsSync as existsSync24, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
|
|
17080
|
+
import { writeFileSync as writeFileSync9, writeSync } from "node:fs";
|
|
17083
17081
|
import { join as join35 } from "node:path";
|
|
17084
17082
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
17085
17083
|
function audioCutForChunk(grid, chunks, index, fps) {
|
|
@@ -17176,15 +17174,15 @@ async function writeChunkAudioSlices(input) {
|
|
|
17176
17174
|
const start = offsets[cut.firstPacket];
|
|
17177
17175
|
layout.partStarts[index] = start;
|
|
17178
17176
|
const end = offsets[last] + track.sizes[last];
|
|
17179
|
-
|
|
17177
|
+
writeFileSync9(join35(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
|
|
17180
17178
|
});
|
|
17181
17179
|
const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
|
|
17182
|
-
|
|
17183
|
-
|
|
17180
|
+
writeFileSync9(join35(input.planDir, SLICE_FRAME), Buffer.concat(frame));
|
|
17181
|
+
writeFileSync9(join35(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
|
|
17184
17182
|
}
|
|
17185
17183
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
17186
17184
|
const layoutPath = join35(planDir, SLICE_LAYOUT);
|
|
17187
|
-
if (!
|
|
17185
|
+
if (!existsSync24(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
17188
17186
|
const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
|
|
17189
17187
|
const frame = readFileSync11(join35(planDir, SLICE_FRAME));
|
|
17190
17188
|
const fd = openSync6(scratchPath, "w");
|
|
@@ -17744,7 +17742,7 @@ function createVideoSourceShipper(input) {
|
|
|
17744
17742
|
let result = null;
|
|
17745
17743
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
17746
17744
|
const rel = pathInside(root, srcPath);
|
|
17747
|
-
if (rel !== null && (
|
|
17745
|
+
if (rel !== null && (existsSync25(join37(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
17748
17746
|
result = ["compiled", ...rel.split(sep2)].join("/");
|
|
17749
17747
|
break;
|
|
17750
17748
|
}
|
|
@@ -17772,7 +17770,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
17772
17770
|
useGpu: false,
|
|
17773
17771
|
browserGpuMode: "software"
|
|
17774
17772
|
});
|
|
17775
|
-
if (!
|
|
17773
|
+
if (!existsSync25(planDir)) mkdirSync14(planDir, { recursive: true });
|
|
17776
17774
|
const log = config.logger ?? defaultLogger;
|
|
17777
17775
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
17778
17776
|
const abortSignal = config.abortSignal;
|
|
@@ -17802,7 +17800,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
17802
17800
|
});
|
|
17803
17801
|
const entryFile = config.entryFile ?? "index.html";
|
|
17804
17802
|
const htmlPath = join37(projectDir, entryFile);
|
|
17805
|
-
if (!
|
|
17803
|
+
if (!existsSync25(htmlPath)) {
|
|
17806
17804
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
17807
17805
|
}
|
|
17808
17806
|
const workDir = join37(planDir, ".plan-work");
|
|
@@ -17846,7 +17844,6 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
17846
17844
|
urlByAbsentLocalPath,
|
|
17847
17845
|
projectDir,
|
|
17848
17846
|
workDir,
|
|
17849
|
-
htmlPath,
|
|
17850
17847
|
entryFile,
|
|
17851
17848
|
job,
|
|
17852
17849
|
cfg,
|
|
@@ -18031,15 +18028,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18031
18028
|
logPlanStage(log, "extract", stageStarted);
|
|
18032
18029
|
const stagedVideoFrames = join37(compiledDir, "__hyperframes_video_frames");
|
|
18033
18030
|
const videoFramesDst = join37(planDir, "video-frames");
|
|
18034
|
-
if (
|
|
18031
|
+
if (existsSync25(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
|
|
18035
18032
|
const videoSourcesDir = join37(planDir, "video-sources");
|
|
18036
18033
|
rmSync12(videoSourcesDir, { recursive: true, force: true });
|
|
18037
|
-
if (
|
|
18034
|
+
if (existsSync25(stagedVideoFrames)) {
|
|
18038
18035
|
renameWithRetry8(stagedVideoFrames, videoFramesDst);
|
|
18039
18036
|
} else {
|
|
18040
18037
|
mkdirSync14(videoFramesDst, { recursive: true });
|
|
18041
18038
|
}
|
|
18042
|
-
if (
|
|
18039
|
+
if (existsSync25(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
|
|
18043
18040
|
renameWithRetry8(compiledDir, finalCompiledDir);
|
|
18044
18041
|
const shipVideoSource = createVideoSourceShipper({
|
|
18045
18042
|
planDir,
|
|
@@ -18078,7 +18075,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18078
18075
|
sizeBytes
|
|
18079
18076
|
}))
|
|
18080
18077
|
);
|
|
18081
|
-
|
|
18078
|
+
writeFileSync10(
|
|
18082
18079
|
join37(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
|
|
18083
18080
|
JSON.stringify(planVideosJson, null, 2),
|
|
18084
18081
|
"utf-8"
|
|
@@ -18159,10 +18156,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18159
18156
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
18160
18157
|
}
|
|
18161
18158
|
const planAudioPath = join37(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
18162
|
-
if (audioResult?.hasAudio &&
|
|
18159
|
+
if (audioResult?.hasAudio && existsSync25(audioResult.audioOutputPath)) {
|
|
18163
18160
|
renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
|
|
18164
18161
|
}
|
|
18165
|
-
if (chunksCarryAudio &&
|
|
18162
|
+
if (chunksCarryAudio && existsSync25(planAudioPath)) {
|
|
18166
18163
|
if (options.sliceChunkAudio) {
|
|
18167
18164
|
stageStarted = Date.now();
|
|
18168
18165
|
await writeChunkAudioSlices({
|
|
@@ -18280,7 +18277,7 @@ async function plan(projectDir, config, planDir, options = {}) {
|
|
|
18280
18277
|
|
|
18281
18278
|
// src/services/distributed/renderChunk.ts
|
|
18282
18279
|
import { randomBytes } from "node:crypto";
|
|
18283
|
-
import { existsSync as
|
|
18280
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync13, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync13 } from "node:fs";
|
|
18284
18281
|
import { cpus as cpus3 } from "node:os";
|
|
18285
18282
|
import { join as join41 } from "node:path";
|
|
18286
18283
|
import {
|
|
@@ -18307,7 +18304,7 @@ import {
|
|
|
18307
18304
|
} from "@hyperframes/engine";
|
|
18308
18305
|
import {
|
|
18309
18306
|
copyFileSync as copyFileSync8,
|
|
18310
|
-
existsSync as
|
|
18307
|
+
existsSync as existsSync27,
|
|
18311
18308
|
lstatSync as lstatSync3,
|
|
18312
18309
|
mkdirSync as mkdirSync16,
|
|
18313
18310
|
mkdtempSync as mkdtempSync6,
|
|
@@ -18315,7 +18312,7 @@ import {
|
|
|
18315
18312
|
readdirSync as readdirSync9,
|
|
18316
18313
|
rmSync as rmSync14,
|
|
18317
18314
|
statSync as statSync10,
|
|
18318
|
-
writeFileSync as
|
|
18315
|
+
writeFileSync as writeFileSync12
|
|
18319
18316
|
} from "node:fs";
|
|
18320
18317
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
18321
18318
|
import { dirname as dirname16, isAbsolute as isAbsolute4, join as join40, relative as relative6, resolve as resolve10, sep as sep3 } from "node:path";
|
|
@@ -18325,13 +18322,13 @@ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as r
|
|
|
18325
18322
|
// src/services/distributed/planV2Publisher.ts
|
|
18326
18323
|
import {
|
|
18327
18324
|
copyFileSync as copyFileSync7,
|
|
18328
|
-
existsSync as
|
|
18325
|
+
existsSync as existsSync26,
|
|
18329
18326
|
linkSync as linkSync2,
|
|
18330
18327
|
mkdirSync as mkdirSync15,
|
|
18331
18328
|
mkdtempSync as mkdtempSync5,
|
|
18332
18329
|
rmSync as rmSync13,
|
|
18333
18330
|
statSync as statSync9,
|
|
18334
|
-
writeFileSync as
|
|
18331
|
+
writeFileSync as writeFileSync11
|
|
18335
18332
|
} from "node:fs";
|
|
18336
18333
|
import { renameWithRetry as renameWithRetry9 } from "@hyperframes/engine";
|
|
18337
18334
|
import { dirname as dirname15, join as join39 } from "node:path";
|
|
@@ -18394,7 +18391,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18394
18391
|
#linkFile;
|
|
18395
18392
|
#committed = false;
|
|
18396
18393
|
constructor(destinationDir, options = {}) {
|
|
18397
|
-
if (
|
|
18394
|
+
if (existsSync26(destinationDir)) {
|
|
18398
18395
|
throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
|
|
18399
18396
|
}
|
|
18400
18397
|
this.destinationDir = destinationDir;
|
|
@@ -18411,7 +18408,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18411
18408
|
);
|
|
18412
18409
|
}
|
|
18413
18410
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
18414
|
-
if (
|
|
18411
|
+
if (existsSync26(destinationPath)) return;
|
|
18415
18412
|
mkdirSync15(dirname15(destinationPath), { recursive: true });
|
|
18416
18413
|
const stagingDir = mkdtempSync5(join39(dirname15(destinationPath), ".plan-v2-blob-"));
|
|
18417
18414
|
const temporaryPath = join39(stagingDir, "blob");
|
|
@@ -18429,13 +18426,13 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18429
18426
|
}
|
|
18430
18427
|
async commitManifest(manifestBytes) {
|
|
18431
18428
|
for (const digest of new Set(manifestDigests(manifestBytes))) {
|
|
18432
|
-
if (!
|
|
18429
|
+
if (!existsSync26(planV2BlobPath(this.temporaryDir, digest))) {
|
|
18433
18430
|
throw new PlanV2IntegrityError(
|
|
18434
18431
|
`cannot commit manifest before referenced blob is durable: ${digest}`
|
|
18435
18432
|
);
|
|
18436
18433
|
}
|
|
18437
18434
|
}
|
|
18438
|
-
|
|
18435
|
+
writeFileSync11(join39(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
|
|
18439
18436
|
renameWithRetry9(this.temporaryDir, this.destinationDir);
|
|
18440
18437
|
this.#committed = true;
|
|
18441
18438
|
}
|
|
@@ -18508,7 +18505,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
|
|
|
18508
18505
|
}
|
|
18509
18506
|
function validateExtractionCacheCompleteSentinels(executionPlanDir) {
|
|
18510
18507
|
const videoRoot = join40(executionPlanDir, "video-frames");
|
|
18511
|
-
if (!
|
|
18508
|
+
if (!existsSync27(videoRoot)) return;
|
|
18512
18509
|
for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
|
|
18513
18510
|
if (!videoEntry.isDirectory()) continue;
|
|
18514
18511
|
const videoDir = join40(videoRoot, videoEntry.name);
|
|
@@ -18609,7 +18606,7 @@ function parsePlanVideosJson2(value) {
|
|
|
18609
18606
|
}
|
|
18610
18607
|
function materializeExtractedVideoDirectories(planDir) {
|
|
18611
18608
|
const videosPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
18612
|
-
if (!
|
|
18609
|
+
if (!existsSync27(videosPath)) return;
|
|
18613
18610
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
18614
18611
|
for (const video of videos.extracted) {
|
|
18615
18612
|
mkdirSync16(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
@@ -18643,9 +18640,9 @@ function parseChunkSlices(value) {
|
|
|
18643
18640
|
}
|
|
18644
18641
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
18645
18642
|
const videoRoot = join40(executionPlanDir, "video-frames");
|
|
18646
|
-
const hasExtractedFrames =
|
|
18643
|
+
const hasExtractedFrames = existsSync27(videoRoot) && listFiles(videoRoot).length > 0;
|
|
18647
18644
|
const videosPath = join40(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
18648
|
-
if (!
|
|
18645
|
+
if (!existsSync27(videosPath)) {
|
|
18649
18646
|
return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
|
|
18650
18647
|
}
|
|
18651
18648
|
const chunksPath = join40(executionPlanDir, "meta", "chunks.json");
|
|
@@ -18702,7 +18699,7 @@ function computeManifestHash(manifest) {
|
|
|
18702
18699
|
return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
|
|
18703
18700
|
}
|
|
18704
18701
|
function writeBlob(sourcePath, destinationPath) {
|
|
18705
|
-
if (
|
|
18702
|
+
if (existsSync27(destinationPath)) return;
|
|
18706
18703
|
mkdirSync16(dirname16(destinationPath), { recursive: true });
|
|
18707
18704
|
const temporaryDir = mkdtempSync6(join40(dirname16(destinationPath), ".plan-v2-blob-"));
|
|
18708
18705
|
const temporaryPath = join40(temporaryDir, "blob");
|
|
@@ -18742,7 +18739,7 @@ async function putTasks(tasks) {
|
|
|
18742
18739
|
}
|
|
18743
18740
|
function buildPlanV2Publication(executionPlanDir, digests) {
|
|
18744
18741
|
const executionPlanPath = join40(executionPlanDir, "plan.json");
|
|
18745
|
-
if (!
|
|
18742
|
+
if (!existsSync27(executionPlanPath)) {
|
|
18746
18743
|
throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
|
|
18747
18744
|
}
|
|
18748
18745
|
const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
|
|
@@ -18822,7 +18819,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
18822
18819
|
};
|
|
18823
18820
|
}
|
|
18824
18821
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
18825
|
-
if (
|
|
18822
|
+
if (existsSync27(planV2Dir)) {
|
|
18826
18823
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
18827
18824
|
}
|
|
18828
18825
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
@@ -18835,7 +18832,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
18835
18832
|
for (const blob of publication.blobs) {
|
|
18836
18833
|
writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
|
|
18837
18834
|
}
|
|
18838
|
-
|
|
18835
|
+
writeFileSync12(
|
|
18839
18836
|
join40(tempDir, "plan.json"),
|
|
18840
18837
|
canonicalJsonStringify(publication.manifest),
|
|
18841
18838
|
"utf-8"
|
|
@@ -18908,7 +18905,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
18908
18905
|
}
|
|
18909
18906
|
}
|
|
18910
18907
|
async function planV2(projectDir, config, planV2Dir) {
|
|
18911
|
-
if (
|
|
18908
|
+
if (existsSync27(planV2Dir)) {
|
|
18912
18909
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
18913
18910
|
}
|
|
18914
18911
|
const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
|
|
@@ -19062,7 +19059,7 @@ function parsePlanV2Manifest(value) {
|
|
|
19062
19059
|
}
|
|
19063
19060
|
function readPlanV2Manifest(planV2Dir) {
|
|
19064
19061
|
const manifestPath = join40(planV2Dir, "plan.json");
|
|
19065
|
-
if (!
|
|
19062
|
+
if (!existsSync27(manifestPath)) {
|
|
19066
19063
|
throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
|
|
19067
19064
|
}
|
|
19068
19065
|
const value = readJsonFile(manifestPath, "v2 manifest");
|
|
@@ -19081,7 +19078,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
|
|
|
19081
19078
|
}
|
|
19082
19079
|
function verifyBlob(planV2Dir, artifact) {
|
|
19083
19080
|
const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
|
|
19084
|
-
if (!
|
|
19081
|
+
if (!existsSync27(sourcePath)) {
|
|
19085
19082
|
throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
|
|
19086
19083
|
}
|
|
19087
19084
|
const stats = statSync10(sourcePath);
|
|
@@ -19094,7 +19091,7 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
19094
19091
|
return sourcePath;
|
|
19095
19092
|
}
|
|
19096
19093
|
function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
|
|
19097
|
-
if (
|
|
19094
|
+
if (existsSync27(destinationDir)) {
|
|
19098
19095
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
19099
19096
|
}
|
|
19100
19097
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
@@ -19115,7 +19112,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
19115
19112
|
copyFileSync8(sourcePath, destinationPath);
|
|
19116
19113
|
}
|
|
19117
19114
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
19118
|
-
|
|
19115
|
+
writeFileSync12(
|
|
19119
19116
|
join40(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
19120
19117
|
canonicalJsonStringify({
|
|
19121
19118
|
manifest,
|
|
@@ -19159,7 +19156,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
19159
19156
|
}
|
|
19160
19157
|
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
19161
19158
|
const markerPath = join40(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
19162
|
-
if (!
|
|
19159
|
+
if (!existsSync27(markerPath)) return null;
|
|
19163
19160
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
19164
19161
|
if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
|
|
19165
19162
|
throw new PlanV2IntegrityError("malformed materialization marker");
|
|
@@ -19177,7 +19174,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
19177
19174
|
for (const artifact of required) {
|
|
19178
19175
|
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
19179
19176
|
const path = join40(planDir, ...artifact.path.split("/"));
|
|
19180
|
-
if (!
|
|
19177
|
+
if (!existsSync27(path) || statSync10(path).size !== artifact.sizeBytes) {
|
|
19181
19178
|
throw new PlanV2IntegrityError(
|
|
19182
19179
|
`materialized artifact missing or truncated: ${artifact.path}`
|
|
19183
19180
|
);
|
|
@@ -19317,7 +19314,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19317
19314
|
const planJsonPath = join41(planDir, "plan.json");
|
|
19318
19315
|
const encoderJsonPath = join41(planDir, "meta", "encoder.json");
|
|
19319
19316
|
const chunksJsonPath = join41(planDir, "meta", "chunks.json");
|
|
19320
|
-
if (!
|
|
19317
|
+
if (!existsSync28(planJsonPath)) {
|
|
19321
19318
|
throw new RenderChunkValidationError(
|
|
19322
19319
|
MISSING_PLAN_ARTIFACT,
|
|
19323
19320
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
@@ -19333,7 +19330,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19333
19330
|
const v2Manifest = v2Materialization?.manifest ?? null;
|
|
19334
19331
|
const remoteSources = v2Materialization?.remoteSources ?? {};
|
|
19335
19332
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
19336
|
-
if (!
|
|
19333
|
+
if (!existsSync28(required)) {
|
|
19337
19334
|
throw new RenderChunkValidationError(
|
|
19338
19335
|
MISSING_PLAN_ARTIFACT,
|
|
19339
19336
|
`[renderChunk] planDir is missing required artifact: ${required}`
|
|
@@ -19344,7 +19341,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19344
19341
|
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
19345
19342
|
const videosJsonPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19346
19343
|
let planVideos = null;
|
|
19347
|
-
if (
|
|
19344
|
+
if (existsSync28(videosJsonPath)) {
|
|
19348
19345
|
try {
|
|
19349
19346
|
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
|
|
19350
19347
|
} catch (err) {
|
|
@@ -19375,7 +19372,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19375
19372
|
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
19376
19373
|
const framesInChunk = range.endFrame - range.startFrame;
|
|
19377
19374
|
const compiledDir = join41(planDir, "compiled");
|
|
19378
|
-
if (!
|
|
19375
|
+
if (!existsSync28(compiledDir)) {
|
|
19379
19376
|
throw new RenderChunkValidationError(
|
|
19380
19377
|
MISSING_PLAN_ARTIFACT,
|
|
19381
19378
|
`[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
|
|
@@ -19625,10 +19622,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19625
19622
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
19626
19623
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
19627
19624
|
if (isPngSequence) {
|
|
19628
|
-
if (!
|
|
19625
|
+
if (!existsSync28(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
|
|
19629
19626
|
} else {
|
|
19630
19627
|
const outDir = join41(outputChunkPath, "..");
|
|
19631
|
-
if (!
|
|
19628
|
+
if (!existsSync28(outDir)) mkdirSync17(outDir, { recursive: true });
|
|
19632
19629
|
}
|
|
19633
19630
|
const encodeInput = {
|
|
19634
19631
|
job,
|
|
@@ -19838,7 +19835,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19838
19835
|
producerVersion: plan2.producerVersion,
|
|
19839
19836
|
ffmpegVersion
|
|
19840
19837
|
};
|
|
19841
|
-
|
|
19838
|
+
writeFileSync13(perfPath, `${JSON.stringify(perfPayload, null, 2)}
|
|
19842
19839
|
`, "utf-8");
|
|
19843
19840
|
try {
|
|
19844
19841
|
rmSync15(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
@@ -20528,11 +20525,11 @@ import {
|
|
|
20528
20525
|
|
|
20529
20526
|
// src/server.ts
|
|
20530
20527
|
import {
|
|
20531
|
-
existsSync as
|
|
20528
|
+
existsSync as existsSync31,
|
|
20532
20529
|
mkdirSync as mkdirSync19,
|
|
20533
20530
|
statSync as statSync12,
|
|
20534
20531
|
mkdtempSync as mkdtempSync8,
|
|
20535
|
-
writeFileSync as
|
|
20532
|
+
writeFileSync as writeFileSync14,
|
|
20536
20533
|
rmSync as rmSync17,
|
|
20537
20534
|
createReadStream as createReadStream5
|
|
20538
20535
|
} from "node:fs";
|
|
@@ -20551,7 +20548,7 @@ import {
|
|
|
20551
20548
|
|
|
20552
20549
|
// src/services/hyperframeLint.ts
|
|
20553
20550
|
import {
|
|
20554
|
-
existsSync as
|
|
20551
|
+
existsSync as existsSync29,
|
|
20555
20552
|
readFileSync as readFileSync15,
|
|
20556
20553
|
statSync as statSync11,
|
|
20557
20554
|
openSync as openSync7,
|
|
@@ -20605,7 +20602,7 @@ function readEntryHtml(filePath) {
|
|
|
20605
20602
|
}
|
|
20606
20603
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
20607
20604
|
const absProjectDir = resolve11(projectDir);
|
|
20608
|
-
if (!
|
|
20605
|
+
if (!existsSync29(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
|
|
20609
20606
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
20610
20607
|
}
|
|
20611
20608
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -20674,7 +20671,7 @@ async function runHyperframeLint(prepared) {
|
|
|
20674
20671
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
20675
20672
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
20676
20673
|
import { dirname as dirname17, join as join44 } from "node:path";
|
|
20677
|
-
import { existsSync as
|
|
20674
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
20678
20675
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
20679
20676
|
async function startHealthWorker(options = {}) {
|
|
20680
20677
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -20751,7 +20748,7 @@ function resolveWorkerEntry2() {
|
|
|
20751
20748
|
const here = dirname17(fileURLToPath6(import.meta.url));
|
|
20752
20749
|
const candidates = [join44(here, "healthWorkerThread.js"), join44(here, "healthWorkerThread.ts")];
|
|
20753
20750
|
for (const candidate of candidates) {
|
|
20754
|
-
if (
|
|
20751
|
+
if (existsSync30(candidate)) return candidate;
|
|
20755
20752
|
}
|
|
20756
20753
|
return void 0;
|
|
20757
20754
|
}
|
|
@@ -20897,7 +20894,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
|
20897
20894
|
const { input, cleanupProjectDir } = prepared;
|
|
20898
20895
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
20899
20896
|
const outputDir = dirname18(absoluteOutputPath);
|
|
20900
|
-
if (!
|
|
20897
|
+
if (!existsSync31(outputDir)) mkdirSync19(outputDir, { recursive: true });
|
|
20901
20898
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
20902
20899
|
}
|
|
20903
20900
|
function validateRenderOverrides(body) {
|
|
@@ -20938,11 +20935,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
20938
20935
|
const candidate = nonEmptyString(projectDir);
|
|
20939
20936
|
if (!candidate) return null;
|
|
20940
20937
|
const absProjectDir = resolve12(candidate);
|
|
20941
|
-
if (!
|
|
20938
|
+
if (!existsSync31(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
|
|
20942
20939
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
20943
20940
|
}
|
|
20944
20941
|
const entry = options.entryFile || "index.html";
|
|
20945
|
-
if (!
|
|
20942
|
+
if (!existsSync31(resolve12(absProjectDir, entry))) {
|
|
20946
20943
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
20947
20944
|
}
|
|
20948
20945
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -20964,7 +20961,7 @@ async function resolveInlineRenderHtml(body) {
|
|
|
20964
20961
|
function materializeInlineProject(html, options) {
|
|
20965
20962
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir5();
|
|
20966
20963
|
const tempProjectDir = mkdtempSync8(join45(tempRoot, "producer-project-"));
|
|
20967
|
-
|
|
20964
|
+
writeFileSync14(join45(tempProjectDir, "index.html"), html, "utf-8");
|
|
20968
20965
|
return {
|
|
20969
20966
|
prepared: {
|
|
20970
20967
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -21034,7 +21031,7 @@ function cleanupTempDir(dir, log) {
|
|
|
21034
21031
|
}
|
|
21035
21032
|
}
|
|
21036
21033
|
function outputFileSize(path) {
|
|
21037
|
-
return
|
|
21034
|
+
return existsSync31(path) ? statSync12(path).size : 0;
|
|
21038
21035
|
}
|
|
21039
21036
|
function createBlockingProgressReporter(log, requestId) {
|
|
21040
21037
|
let lastLoggedPct = -10;
|
|
@@ -21338,7 +21335,7 @@ function createRenderHandlers(options = {}) {
|
|
|
21338
21335
|
if (!artifact) {
|
|
21339
21336
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
21340
21337
|
}
|
|
21341
|
-
if (!
|
|
21338
|
+
if (!existsSync31(artifact.path)) {
|
|
21342
21339
|
store.delete(token);
|
|
21343
21340
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
21344
21341
|
}
|
|
@@ -21444,14 +21441,14 @@ import {
|
|
|
21444
21441
|
} from "@hyperframes/engine";
|
|
21445
21442
|
import {
|
|
21446
21443
|
cpSync as cpSync3,
|
|
21447
|
-
existsSync as
|
|
21444
|
+
existsSync as existsSync32,
|
|
21448
21445
|
mkdirSync as mkdirSync20,
|
|
21449
21446
|
mkdtempSync as mkdtempSync9,
|
|
21450
21447
|
readFileSync as readFileSync16,
|
|
21451
21448
|
readdirSync as readdirSync11,
|
|
21452
21449
|
rmSync as rmSync18,
|
|
21453
21450
|
statSync as statSync13,
|
|
21454
|
-
writeFileSync as
|
|
21451
|
+
writeFileSync as writeFileSync15
|
|
21455
21452
|
} from "node:fs";
|
|
21456
21453
|
import { dirname as dirname19, join as join46 } from "node:path";
|
|
21457
21454
|
import {
|
|
@@ -21499,7 +21496,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21499
21496
|
}
|
|
21500
21497
|
const format = plan2.dimensions.format;
|
|
21501
21498
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
21502
|
-
if (!
|
|
21499
|
+
if (!existsSync32(dirname19(outputPath))) {
|
|
21503
21500
|
mkdirSync20(dirname19(outputPath), { recursive: true });
|
|
21504
21501
|
}
|
|
21505
21502
|
const workDir = mkdtempSync9(`${outputPath}.assemble-work-`);
|
|
@@ -21523,7 +21520,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21523
21520
|
try {
|
|
21524
21521
|
const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
|
|
21525
21522
|
const finalPath = join46(workDir, `output.${format}`);
|
|
21526
|
-
const audio = audioPath !== null &&
|
|
21523
|
+
const audio = audioPath !== null && existsSync32(audioPath) ? audioPath : null;
|
|
21527
21524
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
21528
21525
|
renameWithRetry11(finalPath, outputPath);
|
|
21529
21526
|
log.info("[assemble] stage timings", {
|
|
@@ -21541,7 +21538,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21541
21538
|
});
|
|
21542
21539
|
}
|
|
21543
21540
|
}
|
|
21544
|
-
const fileSize =
|
|
21541
|
+
const fileSize = existsSync32(outputPath) ? statSync13(outputPath).size : 0;
|
|
21545
21542
|
return {
|
|
21546
21543
|
...referenceBackgroundResult8(plan2[REFERENCE_BACKGROUND_FIELD6]),
|
|
21547
21544
|
outputPath,
|
|
@@ -21553,13 +21550,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21553
21550
|
function readAssemblePlan(planDir, chunkPaths) {
|
|
21554
21551
|
const planJsonPath = join46(planDir, "plan.json");
|
|
21555
21552
|
const chunksJsonPath = join46(planDir, "meta", "chunks.json");
|
|
21556
|
-
if (!
|
|
21553
|
+
if (!existsSync32(planJsonPath)) {
|
|
21557
21554
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
21558
21555
|
}
|
|
21559
21556
|
const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
|
|
21560
21557
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
21561
21558
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
21562
|
-
if (!
|
|
21559
|
+
if (!existsSync32(chunksJsonPath)) {
|
|
21563
21560
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
21564
21561
|
}
|
|
21565
21562
|
const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
|
|
@@ -21574,7 +21571,7 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
21574
21571
|
}
|
|
21575
21572
|
}
|
|
21576
21573
|
for (const path of chunkPaths.flat()) {
|
|
21577
|
-
if (!
|
|
21574
|
+
if (!existsSync32(path)) {
|
|
21578
21575
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
21579
21576
|
}
|
|
21580
21577
|
}
|
|
@@ -21672,7 +21669,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
|
|
|
21672
21669
|
function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
|
|
21673
21670
|
const listPath = join46(workDir, "concat-list.txt");
|
|
21674
21671
|
const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
|
|
21675
|
-
|
|
21672
|
+
writeFileSync15(listPath, `${body}
|
|
21676
21673
|
`, "utf-8");
|
|
21677
21674
|
return ["-r", fpsArg, "-f", "concat", "-safe", "0", "-i", listPath];
|
|
21678
21675
|
}
|
|
@@ -21713,7 +21710,7 @@ function planFormat(planDir) {
|
|
|
21713
21710
|
}
|
|
21714
21711
|
function lockedAudio(planDir) {
|
|
21715
21712
|
const encoderJsonPath = join46(planDir, "meta", "encoder.json");
|
|
21716
|
-
if (!
|
|
21713
|
+
if (!existsSync32(encoderJsonPath)) return {};
|
|
21717
21714
|
return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
21718
21715
|
}
|
|
21719
21716
|
function readCfrEncoder(planDir, format) {
|
|
@@ -21723,7 +21720,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
21723
21720
|
);
|
|
21724
21721
|
}
|
|
21725
21722
|
const encoderJsonPath = join46(planDir, "meta", "encoder.json");
|
|
21726
|
-
if (!
|
|
21723
|
+
if (!existsSync32(encoderJsonPath)) {
|
|
21727
21724
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
21728
21725
|
}
|
|
21729
21726
|
const encoderJson = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
@@ -21735,7 +21732,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
21735
21732
|
return encoderJson;
|
|
21736
21733
|
}
|
|
21737
21734
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
21738
|
-
if (
|
|
21735
|
+
if (existsSync32(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
|
|
21739
21736
|
mkdirSync20(outputPath, { recursive: true });
|
|
21740
21737
|
let globalIdx = 0;
|
|
21741
21738
|
for (const chunkDir of chunkPaths) {
|
|
@@ -21759,7 +21756,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
21759
21756
|
`[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
|
|
21760
21757
|
);
|
|
21761
21758
|
}
|
|
21762
|
-
if (audioPath !== null &&
|
|
21759
|
+
if (audioPath !== null && existsSync32(audioPath)) {
|
|
21763
21760
|
const sidecar = join46(outputPath, MIXED_AUDIO_FILENAME4);
|
|
21764
21761
|
cpSync3(audioPath, sidecar);
|
|
21765
21762
|
}
|